Master Génie Logiciel Architecture et Développent de Logiciels Cours 3: Design pattern Mr : Y. GAFOUR 2023/2024 Plan Introduction Définition design pattern Catalogue du design pattern Creational Patterns : Singleton pattern Factory pattern Structural Patterns The Adapter Pattern Facade pattern Behavioral Patterns Iterator pattern Strategy pattern Autres design pattern Pattern de conception MVC (Modèle-Vue-Contrôleur) Data Access Object pattern or DAO Injection de Dépendances Conclusion 2 Introduction Les concepteurs experts en orientés objet diront qu'une conception réutilisable et flexible (accepte les modifications souhaitées) peut être difficile, mais ce n'est pas impossible à réaliser dès la première tentative. Avant qu’une conception ne soit terminée, ils essaient généralement de réutiliser des solutions plusieurs fois, en la modifiant à chaque fois. Lorsqu'ils trouvent la bonne solution, ils l'utilisent plusieurs fois. Par conséquent, vous trouverez des modèles récurrents ( )متكررةde classes et d'objets dans nombreux cas de conception en orientés objet. 3 Définition design pattern Les design pattern (Patron de conception ) sont des solutions typiques ( )نموذجيةaux problèmes Un modèle n'est pas une partie de code spécifique, mais un concept ( )مفهومgénéral pour résoudre un problème particulier courants dans la conception de logiciels Un algorithme définit un ensemble des instructions pouvant atteindre un objectif. Par contre, un modèle est une description de plus haut niveau d'une solution. Les modèles de conception ont d'abord été publiés pour la première fois dans un livre scientifique il y a plus de 20 ans. Les quatre auteurs du livre : Erich Gamma, Richard Helm, Ralph Johnson et John Vlissides, ont depuis été surnommés «The Gang of Four». 4 Importance de connaître et d'utiliser des design patterns lors de la conception logicielle Il est important de connaître et d'utiliser des design patterns car ils permettent de résoudre efficacement des problèmes courants en conception logicielle. Il favorisent la réutilisabilité du code, améliorent la clarté et la maintenabilité du code, et facilitent la communication entre les développeurs Les design patterns peuvent aider à réaliser un couplage faible: En utilisant des interfaces ou des abstractions pour communiquer entre eux, réduisant ainsi leur interdépendance. 5 Catalogue du design pattern Creational Patterns : Ces patrons sont utilisés pour résoudre des problèmes liés à la création d'objets tout en minimisant le couplage et en maximisant la flexibilité du code. the ability to adapt to changing requirements and extend functionality without breaking existing code Structural Patterns : Expliquent comment assembler des objets et des classes dans des structures plus grandes et plus complexes tout en permettant une meilleure flexibilité et maintenabilité du code. Par exemple Adapter Pattern : Permet à des interfaces incompatibles de travailler ensemble en convertissant l'interface d'une classe en une autre interface que le client attend. 6 Catalogue du design pattern Behavioral Patterns : sont des modèles de conception qui se concentrent sur la communication entre les objets et la répartition des responsabilités entre eux. Les Behavioral Patterns fournissent des mécanismes spécifiques pour établir des interactions entre les objets. Ils définissent des rôles clairs pour chaque objet participant, indiquant les responsabilités spécifiques de chaque objet dans le système. 7 Classification1 Creational Structural Behavioral Abstract Factory Adapter Null Object Factory method Bridge Command Builder Composite Interpreter Lazy instantiation Decorator Iterator Object pool Façade Mediator Prototype Flyweight Memento Singleton Proxy Observer Multiton State Resource acquisition is initialization Chain of responsibility Strategy Specification Template method Visitor 8 1. Creational Patterns Creational Patterns : Fournissent des mécanismes de création d'objets qui accroître la flexibilité et la réutilisation du code existant. Ces modèles concernent la manière dont les objets sont créés et instanciés. Ils offrent des solutions pour créer des objets d'une manière qui soit flexible, réutilisable et adaptée à la situation. Exemples de patrons de création : Singleton pattern Factory pattern 9 Singleton pattern Le modèle singleton est l'un des modèles de conception les plus simples. Ce modèle restreint l'instanciation d'une classe à un objet afin d’avoir d'une seule instance de notre classe tout en s'assurant qu'un seul objet est créé. 10 Exemple Etape 1 :Créer une classe singleton. public class SingleObject { // Créer un objet de SingleObject private static SingleObject instance = new SingleObject(); // Rendre le constructeur privé afin que cette classe ne puisse pas être instanciée private SingleObject(){} // Récupèrer le seul objet disponible public static SingleObject getInstance(){ return instance; } public void showMessage(){ System.out.println("Hello World!"); } } 11 Exemple Étape 2 : Obtenir le seul objet de la classe singleton. public class SingletonPatternDemo { public static void main(String[] args) { SingleObject object = SingleObject.getInstance(); // Afficher le message object.showMessage(); } } 12 Factory pattern Le Factory Design Pattern permet de séparer la création d'objets dérivant d'une classe mère de leur utilisation. De ce fait, il crée plusieurs objets issus d'une même classe mère. Il est un modèle de conception qui fournit une interface pour créer des objets, mais permet aux sous-classes de modifier le type d'objets qui seront créés. Factory Method is a creational design pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created 13 Factory pattern Exemple de modèle de conception Design Pattern Dans cet exemple, nous allons créer une interface Shape qui sera implémentée par plusieurs classes concrètes. ShapeFactory sera utilisée pour récupérer les objets de cette famille : 14 Factory pattern Exemple de modèle de conception Design Pattern public interface Shape { void draw(); } public class Circle implements Shape{ public void draw(){ System.out.println("Drawing circle"); } } public class Rectangle implements Shape{ public void draw(){ System.out.println("Drawing Rectangle"); } } public class Square implements Shape{ public void draw(){ System.out.println("Drawing Square"); } } 15 Factory pattern Exemple de modèle de conception Design Pattern the problem here is that he's making the creator class instanciate the classes // ShapeFactory pour créer des formes }} whereas it could be that the creator has class ShapeFactory { some business logic that concern // Méthode statique pour obtenir une forme globally all the classes, so as a solution public static Shape getShape (String shapeType) { we should make the creator class abstract class with a factory if (shapeType == null) { method that is abstract return null; } if (shapeType.equalsIgnoreCase("CIRCLE")) { return new Circle(); } else if (shapeType.equalsIgnoreCase("SQUARE")) { return new Square(); } else if (shapeType.equalsIgnoreCase("TRIANGLE")) { return new Triangle(); } return null; }} 16 Factory pattern Exemple de modèle de conception Design Pattern public class Main { public static void main(String[] args){ ShapeFactory shapeFactory = new ShapeFactory(); Shape shape1 = shapeFactory.getShape("circle"); shape1.draw();//Drawing circle Shape shape2 = shapeFactory.getShape("rectangle"); shape2.draw();//Drawing Rectangle Shape shape3 = shapeFactory.getShape("square"); shape3.draw();//Drawing Square } } check implementation in refactoring guru 17 2. Structural Patterns Structural Patterns : Expliquent comment assembler des objets et des classes dans des structures plus importantes, tout en conservant la flexibilité efficacité des structures. The Adapter Pattern Facade pattern 18 Adapter Pattern Permet à des interfaces incompatibles de travailler ensemble. Convertit l'interface d'une classe en une autre interface que le client attend. Un composant intermédiaire est appelé «adaptateur» et fournit un pont pour éliminer les incompatibilités dans deux systèmes externes. Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate 19 Adapter Pattern Un exemple est de créer un adaptateur lorsqu'un objet existe (l'adaptee) qui fournit la fonctionnalité souhaitée par le client, mais n'implémente pas l'interface (cible) requise par le client. Supposons que nous ayons une interface MediaPlayer représentant un lecteur multimédia avec des méthodes pour lire, mettre en pause et arrêter la lecture. Nous avons également une classe VLCPlayer qui implémente cette interface avec sa propre implémentation spécifique. Maintenant, imaginons que nous voulions utiliser une classe MP3Player existante qui a une interface différente mais que nous voulons toujours l'utiliser comme lecteur multimédia dans notre application. Nous pouvons utiliser le Adapter Pattern pour adapter l'interface de MP3Player à celle de MediaPlayer. 20 duck classes turkey classes Adapter Pattern turkeyAdapter // Interface représentant un lecteur multimédia standar interface MediaPlayer { void play(); void pause(); void stop(); } // Classe représentant un lecteur VLC class VLCPlayer implements MediaPlayer { @Override public void play() { System.out.println("Lecture VLCPlayer"); } @Override public void pause() { System.out.println("Pause VLCPlayer"); } @Override public void stop() { System.out.println("Arrêt VLCPlayer"); } } 21 Adapter Pattern // Classe représentant un lecteur MP3 existant avec une interface différente class MP3Player { void playMP3() { System.out.println("Lecture MP3Player"); } void pauseMP3() { System.out.println("Pause MP3Player"); } void stopMP3() { System.out.println("Arrêt MP3Player"); } } 22 Adapter Pattern // Adaptateur pour convertir l'interface MP3Player en une interface MediaPlayer class MP3PlayerAdapter implements MediaPlayer { private MP3Player mp3Player; public MP3PlayerAdapter(MP3Player mp3Player) { this.mp3Player = mp3Player; } @Override public void play() { mp3Player.playMP3(); } @Override public void pause() { mp3Player.pauseMP3(); } @Override public void stop() { mp3Player.stopMP3(); } } 23 Adapter Pattern // Utilisation du lecteur multimédia avec le Adapter Pattern public class Main { public static void main(String[] args) { // Utilisation du lecteur VLCPlayer MediaPlayer mediaPlayer1 = new VLCPlayer(); mediaPlayer1.play(); mediaPlayer1.pause(); mediaPlayer1.stop(); // Utilisation du lecteur MP3Player avec l'adaptateur MP3Player mp3Player = new MP3Player(); MediaPlayer mediaPlayer2 = new MP3PlayerAdapter(mp3Player); mediaPlayer2.play(); mediaPlayer2.pause(); mediaPlayer2.stop(); } } 24 Facade pattern Le modèle de façade cache les complexités du système et fournit une interface au client à laquelle le client peut accéder au système, cachant ainsi la complexité du sous-système aux clients. Il permet de cacher les détails d'implémentation complexes du sous-système aux clients, ce qui facilite son utilisation. 25 Facade pattern Facade is a structural design pattern that provides a simplified interface to a library, a framework, or any other complex set of classes. Dans l’exemple montré dans la figure ci-dessous, nous allons créer une interface Shape et des classes concrètes implémentant l'interface Shape. Une classe de façade ShapeMaker est définie dans l’étape suivante. 26 Facade pattern Étape 1 :Créer une interface. Shape.java public interface Shape { void draw(); } Etape 2 : Créer des classes concrètes implémentant la même interface. Rectangle.java public class Rectangle implements Shape { @Override public void draw() { System.out.println("Rectangle::draw()"); } } 27 Facade pattern Square.java public class Square implements Shape { @Override public void draw() { System.out.println("Square::draw()"); } } Circle.java public class Circle implements Shape { @Override public void draw() { System.out.println("Circle::draw()"); } } 28 Facade pattern Étape 3 :Créer une classe de façade: ShapeMaker.java public class ShapeMaker { private Shape circle; private Shape rectangle; private Shape square; public ShapeMaker() { circle = new Circle(); rectangle = new Rectangle(); square = new Square(); } public void drawCircle(){ circle.draw(); } public void drawRectangle(){ rectangle.draw(); } public void drawSquare(){ square.draw(); } } 29 Facade pattern Étape 4 : Utiliser la façade pour dessiner différents types de formes. FacadePatternDemo.java public class FacadePatternDemo { public static void main(String[] args) { ShapeMaker shapeMaker = new ShapeMaker(); shapeMaker.drawCircle(); shapeMaker.drawRectangle(); shapeMaker.drawSquare(); } } 30 3. Behavioral Patterns Behavioral Patterns : Les modèles comportementaux prennent soin de la communication efficace. Dans ces modèles de conception, l'interaction entre les objets devrait toujours être faiblement couplé. Les Behavioral Patterns fournissent des mécanismes spécifiques pour établir des interactions entre les objets. Ils définissent des rôles clairs pour chaque objet participant, indiquant les responsabilités spécifiques de chaque objet dans le système. Exemples de Behavioral Patterns : Iterator pattern Strategy pattern 31 Iterator pattern Ce modèle est utilisé pour obtenir un moyen d'accéder aux éléments d'un objet de collection de manière séquentielle sans avoir besoin de connaître sa représentation interne (liste, pile, arbre, etc.). Il est un patron de conception comportemental qui fournit un moyen de parcourir séquentiellement les éléments d'une collection sans exposer les détails de sa structure interne. 32 Iterator pattern Il permet d'itérer sur les éléments d'une collection de manière uniforme, quel que soit le type de collection ou sa structure sous-jacente ()بنيتها األساسية. C’est un modèle de conception relativement simple et fréquemment utilisé. Il existe de nombreuses structures / collections de données disponibles. 33 Iterator pattern Chaque collection doit fournir un itérateur qui lui permet de parcourir ses objets. Cependant, ce faisant, il doit s'assurer qu'il n'expose pas son implémentation. Quelle que soit sa structure, une collection doit fournir un moyen d’accéder à ses éléments pour permettre au code de les utiliser. 34 Iterator pattern Il y a plusieurs manières de parcourir une même collection . Iterator pattern fournit une manière générique ()طريقة عامة d'itérer sur une collection indépendante de son type. 35 Iterator pattern - first create iterator interface with all the iteration methods you want (make sure next(), hasNext() are always present) - then create the classes that represent the origins of objects you want to iterate through whether it's list, linked list, tree, graph ..., then add a method to it called generally createIterator that will return the specific iterator for each object - then create an iterator for each of those objects, that implements iterator interface - now go to main class, instanciate the objects and call createIterator on them then use the methods of the iterator interface to interate through the objects 36 Strategy pattern Dans ce modèle, il crée des objets qui représentent diverses stratégies et un objet de contexte dont le comportement varie en fonction de son objet de stratégie. L'objet de stratégie modifie l'algorithme d'exécution de l'objet de contexte. Strategy is a behavioral design pattern that lets you define a family of algorithms, put each of them into a separate class, and make their objects interchangeable 37 Strategy pattern 38 Strategy pattern Nous allons créer une interface Stratégie définissant une action et des classes de stratégie concrètes implémentant l'interface Stratégie. Le contexte est une classe qui utilise une stratégie. La classe StrategyPatternDemo, utilisera des objets de contexte et de stratégie pour démontrer le changement de comportement de contexte en fonction de la stratégie qu'il utilise. 39 Strategy pattern Étape 1 : Créer une interface. Strategy.java public interface Strategy { public int doOperation(int num1, int num2); } Étape 2 :Créer des classes concrètes implémentant la même interface. OperationAdd.java public class OperationAdd implements Strategy{ @Override public int doOperation(int num1, int num2) { return num1 + num2; } } 40 Strategy pattern OperationSubstract.java public class OperationSubstract implements Strategy{ @Override public int doOperation(int num1, int num2) { return num1 - num2; } } OperationMultiply.java public class OperationMultiply implements Strategy{ @Override public int doOperation(int num1, int num2) { return num1 * num2; } } 41 Strategy pattern Étape 3 : Créer une classe de contexte. Context.java public class Context { private Strategy strategy; public Context(Strategy strategy){ this.strategy = strategy; } public int executeStrategy(int num1, int num2){ return strategy.doOperation(num1, num2); } } 42 Strategy pattern Étape 4 : Utilisez le contexte pour voir le changement de comportement lorsqu'il change de stratégie. StrategyPatternDemo.java public class StrategyPatternDemo { public static void main(String[] args) { Context context = new Context(new OperationAdd()); System.out.println("10 + 5 = " + context.executeStrategy(10, 5)); context = new Context(new OperationSubstract()); System.out.println("10 - 5 = " + context.executeStrategy(10, 5)); context = new Context(new OperationMultiply()); System.out.println("10 * 5 = " + context.executeStrategy(10, 5)); } } 43 Autres design pattern Pattern de conception MVC (Modèle-Vue-Contrôleur) Data Access Object pattern or DAO Injection de Dépendances 44 Pattern de conception MVC (Modèle-Vue-Contrôleur) MVC (Model-View-Controller) est une méthode de conception qui organise l’interface homme-machine (IHM) d’une application logicielle. Ce modèle d’architecture impose la séparation entre : Données, Présentation, Traitements. Cette séparation donne trois parties fondamentales: Modèle : représente les données. Il peut également représenter un objet ou JAVA POJO transportant des données. Vue : représente l’interface utilisateur, Elle n’effectue aucun traitement. Elle affiche les données fournit par le modèle. Contrôleur : gère l’interface entre le modèle et la vue ainsi que il garde la vue et le modèle séparés. 45 Pattern de conception MVC (Modèle-Vue-Contrôleur) 46 Data Access Object pattern or DAO En supposant que nous ayons un projet d'application Web qui utilise la base de données MySQL. Nous utiliserons donc le pilote de MySQL pour interagir avec la base de données. Mais dans un autre beau jour, le client veut utiliser une base de données supplémentaire telle que PostgreSQL, alors, nous devons modifier notre code pour qu'il soit compatible avec cette base de données. Cela rend nos couches étroitement liées à la couche de persistance lorsque nous passons à une autre base de données. Par conséquent, quelle est la solution pour empêcher le couplage étroit d'autres couches avec la couche de persistance ? 47 Data Access Object pattern or DAO DAO est un moyen de réduire le couplage entre la logique métier et la logique de persistance. La logique métier de l'application a souvent besoin d'objets de domaine qui sont persistants dans la base de données. L’un des principaux avantages d'utilisation du modèle DAO est lors de la modification d'un mécanisme de persistance, la couche de service n'a même pas besoin de savoir d'où proviennent les données. Par exemple, si vous envisagez de passer de MySQL à MongoDB, toutes les modifications doivent être effectuées uniquement dans la couche DAO. 48 Data Access Object pattern or DAO 49 Inversion de Contrôle (IoC) Qu'est-ce que la dépendance en les classes ? Une classe dépend d'une autre classe pour effectuer certains travaux, par exemple : public class ClientA { ServiceB service; public void doSomething() { String info = service.getInfo(); } public class ServiceB { public String getInfo() { return "ServiceB’s Info"; } } } Ici, la classe ClientA utilise la classe ServiceB qui s'écrit comme ci-dessus. La classe ClientA est dite dépendante de la classe ServiceB, et ServiceB est appelée une dépendance de ClientA. 50 Dépendance en les classes Ce type de dépendance est très trivial en programmation. Cependant, lorsque le code de l'application devient plus gros et plus complexe, la dépendance codée en dur entre les classes présente certains inconvénients : Le code est inflexible: il est difficile à maintenir et à étendre car lorsqu'une classe dépend en permanence d'une autre classe, le changement vers la classe dépendante nécessite un changement vers la classe dépendante. Et il est impossible de changer la classe dépendante plus tard sans mettre à jour et recompiler le code. Le code est difficile pour les tests unitaires car lorsque vous souhaitez tester uniquement les fonctionnalités d'une classe, vous devez également tester d'autres classes dépendantes. Le code est difficile à réutiliser car les classes sont étroitement couplées. 51 Inversion de Contrôle Inversion de Contrôle (inversion of control, IoC) : est un principe du génie logiciel qui transfère le contrôle d'objets d'un programme à un conteneur ou à un framework. Nous l'utilisons le plus souvent dans le cadre de la programmation orientée objet. Le modèle de conception IOC (Inversion de Contrôle) est un principe de conception logicielle qui met l'accent sur l'inversion du contrôle entre différents composants logiciels. 52 Injection de Dépendances Il existe plusieurs façons de mettre en œuvre l'IOC, la Injection de Dépendances (DI) étant l'une des techniques les plus courantes. Injection de Dépendances permet de supprimer les dépendances fortes entre les classes et de rendre notre application faiblement couplée, extensible et maintenable. Injection de Dépendances (DI) : La technique où les dépendances d'un composant sont fournies de l'extérieur, généralement par injection de constructeur, injection de setter ou injection d'interface. 53 Conclusion Les designs patterns représentent les meilleures pratiques expérimentés par les développeurs de logiciels orientés objet. Des solutions aux problèmes généraux rencontrés par les développeurs lors du développement de logiciels, ce qui permet de gagner du temps. Les designs patterns rendent notre code facile à comprendre et à déboguer. Cela conduit à un développement plus rapide et les nouveaux 54 membres de l'équipe le comprennent facilement.