Design Patterns Taha Zerrouki Taha.zerrouki@gmail.com
Généralités La notion de pattern vient dès qu : on est amené à répéter plusieurs fois le même genre de tâche. par exemple le même genre de développement logiciel avec le même genre de problème à résoudre. 2
Généralités Synonymes connus: Forme de conception, modèle, patron de conception, motif, etc. 3
Origines Proposition de Cristopher Alexander: Description d'un problème récurrent et de sa solution Cristopher Alexander et al.: A Pattern Language, 1977 Cristopher Alexander: The TimelessWay of Building, 1979 4
Définition d Alexander «Chaque patron décrit un problème qui se manifeste constamment dans notre environnement, et donc décrit le cœur de la solution à ce problème, d une façon telle que l on puisse réutiliser cette solution des millions de fois, sans jamais le faire deux fois de la même manière» Ou tout simplement: une solution générale pour un problème récurrent dans un contexte donné 5
6
7
8
Origines Alexander propose de structurer les patrons selon quelques champs dont les principaux sont : le nom une illustration graphique le contexte d utilisation le problème la solution générique les conséquences de l emploi du patron. Ces champs forment le cœur d une solution qui peut être facilement réemployée. 9
Origines Le nom des patrons et leur utilisation référencée crée ce qu Alexander appelle un langage de patron «pattern language» 10
Pourquoi pas en informatique?? Les besoins pour une bonne conception et du bon code : Extensibilité Flexibilité Facilité à maintenir Réutilisabilité Les qualités internes Meilleure spécification, construction, documentation 11
Informatique 1987 - Cunningham et Beck utilisent les idées d'alexander pour développer un petit langage de patrons pour Smalltalk 1990 Le Gang des 4 («Gang of Four» : Gamma, Helm, Johnson and Vlissides) commence à travailler à la compilation d'un catalogue de patrons de conceptions 12
Informatique 1993 - Kent Beck et Grady Booch sponsorisent la première réunion de ce qui est maintenant connu comme le groupe http://www.hillside.net Hillside 1995 - Le Gang des 4 (GoF) publie le livre des Patrons de conception 1996 A system of Patterns, Buchmann et. al. 13
Classification du GoF Patrons de création (5) Factory Method Abstract Factory Builder Prototype Singleton Patrons de structure (7) Adapter Bridge Composite Decorator Facade Flyweight Proxy Patrons de comportement (11) Interpreter Template Method Chain of Responsibility Command Iterator Mediator Memento Observer State Strategy Visitor Les plus populaires sont ceux mis en gras 14
Creation Patterns Patron de Création 15
Creation Patterns Patron de Création 16
Patron de création Abstraction du processus d instanciation. Rendre le système indépendant du mode de création des objets qui le compose. Encapsulation de la connaissance des classes concrètes à utiliser Cacher la manière dont les instances sont créées et combinées Deux types Patron de création de classe : utilisation de l héritage pour faire varier la classe instanciée (ex. Factory) Patron de création d objets : délégation de la construction à un autre objet (ex. AbstractFactory, Builder) 17
18
Factory Method Méthode de fabrication (usine) 19
Instanciation au moment de l exécution Quand vous utilisez new, vous instanciez une classe concrète : Il s agit d une implémentation, non d une interface. Lier le code à une classe concrète peut le rendre plus fragile et plus rigide. Quand vous avez tout un ensemble de classes concrètes apparentées, vous êtes souvent obligé d écrire du code qui ressemble au fragment Suivant: if cond1: ob = A(); elif cond2: ob = B(); elif cond3: ob = C() ; A I n C B 20
Exemple sans Factory class Shape:... class Circle(Shape): def draw(self): print("circle.draw") def erase(self): print("circle.erase") class Square(Shape): def draw(self): print("square.draw") def erase(self): print("square.erase") # main for type in ("Circle", "Square","Circle", "Square"): if type == "Circle": shape = Circle() elif type == "Square": shape = Square() else: print "Bad shape creation: " + type sys.exit() shape.draw() shape.erase() 21
Instanciation au moment de l éxecution Plusieurs instanciations de classes concrètes et la décision de la classe à instancier est prise au moment de l exécution, en fonction d un certain nombre de conditions. Ce type de code se retrouve souvent dans plusieurs parties de l application maintenance et mises à jour plus difficiles et plus sujettes à l erreur code n ai pas «fermé à la modification» 22
Identifier ce qui varie for type in ("Circle", "Square","Circle", "Triangle"): if type == "Circle": shape = Circle() elif type == "Square": shape = Square() elif type == "Triangle": shape = Triangle() else: print "Bad shape creation: " + type sys.exit() shape.draw() shape.erase() 23
Patron de conception Tout d abord, nous extrayons le code qui crée les objets le mettre dans la méthode createshape() def createshape(type): if type == "Circle": return Circle() elif type == "Square": return Square() else: print "Bad shape creation: " + type sys.exit() Une technique courante est de rendre cette méthode statique (fabrique statique) car on n a pas besoin d instancier un objet pour utiliser la méthode de création, mais cela ne résout pas le problème Inconvénient : vous ne pouvez pas sous-classer ni modifier le comportement de la méthode de création (méthode de classe) 24
Une technique courante est de rendre cette méthode statique (fabrique statique) car on n a pas besoin d instancier un objet pour utiliser la méthode de création, mais cela ne résout pas le problème Inconvénient : vous ne pouvez pas sous-classer ni modifier le comportement de la méthode de création (méthode de classe) 25
Exemple avec Factory method class ShapeFactory: # Create based on class name: @staticmethod def factory(type): if type == "Circle": return Circle() elif type == "Square": return Square() else: print "Bad shape creation: " + type sys.exit() for i in ("Circle", "Square","Circle", "Square"): shape = Shapefactory.factory(i) shape.draw() shape.erase() 26
Exercice Modifier le code précédent pour ajouter la forme «Triangle» class ShapeFactory: # Create based on class name: @staticmethod def factory(type): if type == "Circle": return Circle() elif type == "Square": return Square() else: print "Bad shape creation: " + type sys.exit() class Circle(Shape): def draw(self): print("circle.draw") def erase(self): print("circle.erase") class Square(Shape): def draw(self): print("square.draw") def erase(self): print("square.erase") # main for i in ("Circle", "Square","Circle", "Square"): shape = ShapeFactory.factory(i) shape.draw() 27
Exercice On veut réaliser une Factory qui crée les objets circle et square, et une autre qui crée les triangles et les squares class ShapeFactory: # Create based on class name: @staticmethod def factory(type): if type == "Circle": return Circle() elif type == "Square": return Square() else: print "Bad shape creation: " + type sys.exit() class ShapeFactory_CS(ShapeFactory): # Create based on class name: @staticmethod def factory(type): if type == "Circle": return Circle() elif type == "Square": return Square() else: print "Bad shape creation: " + type sys.exit() 28
Solution On veut réaliser une Factory qui crée les objets circle et square, et une autre qui crée les triangles et les squares class ShapeFactory_CS(ShapeFactory): # Create based on class name: @staticmethod def factory(type): if type == "Circle": return Circle() elif type == "Square": return Square() else: print "Bad shape creation: " + type sys.exit() class ShapeFactory_TS(ShapeFactory): # Create based on class name: @staticmethod def factory(type): if type == "Circle": return Circle() elif type == "Triangle": return Triangle() else: print "Bad shape creation: " + type sys.exit() 29
Description du pattern Factory Method Intention Choisit la bonne sous-classe en fonction de certains paramètres Permet la délégation d instanciation : cache la classe concrète utilisée (permet l évolution) Utilisations connues Applications graphiques, un peu partout... Synonyme Constructeur virtuel, Fabrique, Fabrication, Usine Patterns associés (en relation) Abstract Factory, Template Method, Prototype 30
Description du pattern Factory Method Problème (Applicabilité, Quand l utiliser?) Une classe est incapable d anticiper le type d objets qu elle doit créer Une classe désire laisser le choix du type d objets créés à ses sousclasses Solution 31
Description du pattern Factory Method Participants Product : définit l interface des objets créés par la fabrication ConcreteProduct : implémente l interface Product Creator : déclare la fabrication; celle-ci renvoie un objet de type Product. Le Creator peut également définir une implémentation par défaut de la fabrication, qui renvoie un objet ConcreteProduct par défaut. Il peut appeler la fabrication pour créer un objet Product ConcreteCreator : surcharge la fabrication pour renvoyer une instance d un ConcreteProduct 32
Conséquence Avantage : Elimine la nécessité de lier les classes spécifiques à l'application dans votre code (le client). Le code ne traite que l'interface du produit; il peut donc travailler avec toutes classe ConcreteProduct définie par l'utilisateur. Inconvénient : Les clients pourraient avoir à sous-classer la classe Créateur juste pour créer un objet ConcreteProduct particulier. 33
Implémentation Deux variétés principales Le Créateur ne fournit pas une implémentation pour la méthode de fabrique Le Créateur fournit une implémentation par défaut de la méthode de fabrique. Factory Method paramétrée Créer plusieurs types de produits La méthode de fabrication prend un paramètre qui identifie le type d'objet à créer. L'objet partage l'interface du produit 34
Exercice Implementer les classes et le Factory de ces classes 35
36
Exercice On veut créer des Pizzerias avec des produits différents Pizzeria d ElHouma produit que le pizza simple et avec thon. Pizzeria de Lux ne produit pas de pizza simple. 37
Abstract Factory Method Méthode de fabrication Abstraite (usine) 38
Abstract Factory Elle fournit : une interface pour créer des familles d'objets liés ou inter-dépendants sans avoir à préciser au moment de leur création la classe concrète à utiliser 39
Abstract Factory Elle fournit une interface pour créer des familles d'objets liés ou inter-dépendants sans avoir à préciser au moment de leur création la classe concrète à utiliser 40
Abstract Factory Elle fournit une interface pour créer des familles d'objets liés ou inter-dépendants sans avoir à préciser au moment de leur création la classe concrète à utiliser 41
Exemple 42
Les classes class Circle(Shape): def draw(self) : print("circle.draw") def erase(self): print("circle.erase") class Circle2D(Circle): def draw(self) : print("circle.draw2d") def erase(self): print("circle.erase2d") class Circle3D(Circle): def draw(self) : print("circle.draw3d") def erase(self): print("circle.erase3d") class Square(Shape): def draw(self) : print("square.draw") def erase(self): print("square.erase") class Square2D(Square): def draw(self) : print("square.draw2d") def erase(self): print("square.erase2d") class Square3D(Square): def draw(self) : print("square.draw3d") def erase(self): print("square.erase3d") 43
Classes Factory class ShapeFactory: @staticmethod def createshape(type): pass class ShapeFactory2D(ShapeFactory): @staticmethod def createshape(type): if type == "Circle": return Circle2D() elif type == "Square": return Square2D() else: return None class ShapeFactory3D(ShapeFactory): @staticmethod def createshape(type): if type == "Circle": return Circle3D() elif type == "Square": return Square3D() else: return None 44
Appel # creation des objets 2 D for type in ("Circle", "Square","Circle", "Square"): shape = ShapeFactory2D.createShape(type) shape.draw() shape.erase() # creation des objets 3 D for type in ("Circle", "Square","Circle", "Square"): shape = ShapeFactory3D.createShape(type) shape.draw() shape.erase() 45
Abstact Factory Intention Fournir une interface pour créer des familles d objets dépendants ou associés sans connaître leur classe réelle Fabriquer des fabriques. Utilisations connues Fabriquer des widgets qui ont tous le même look&feel Synonymes : Kit, Fabrique abstraite, Usine abstraite Patrons en relation Factory Method, Prototype, Singleton. 46
Abstact Factory Problème Un système doit être indépendant de la façon dont ses produits sont créés, assemblés, représentés Un système repose sur un produit d'une famille de produits Une famille de produits doit être utilisée ensemble, pour renforcer cette contrainte On veut définir une interface unique à une famille de produits concrets 47
Abstact Factory Solution 48
Abstract Factory Participants AbstractFactory déclare l interface pour les opérations qui créent des objets abstraits ConcreteFactory implémente les opérations qui crée les objets concrets AbstractProduct déclare une interface pour un type d objet ConcreteProduct définit un objet qui doit être créé par la fabrique concrète correspondante et implémente l interface AbstractProduct Client utilise seulement les interfaces déclarée par AbstractFactory et par les classes AbstractProduct 49
Conséquences Isolation des classes concrètes (seules les classes abstraites sont connues) Échange facile des familles de produit Encouragement de la cohérence entre les produits Mais...prise en compte difficile de nouvelles formes de produit 50
Implantation Les fabriques sont souvent des singletons Ce sont les sous-classes concrètes qui font la création, en utilisant le plus souvent une Factory Method Si plusieurs familles sont possibles, la fabrique concrète utilise Prototype 51
Exercice Créer un système d interface graphique qui crée des widgets (objets graphiques) en fonction du système d exploitation. 52
Exercice 53
Factory Method vs Abstract Factory Factory Method Abstract Factory s appuie sur l héritage : la création des objets est déléguée aux sousclasses qui implémentent la méthode de fabrication pour créer des objets. L intention de Fabrication est de permettre à une classe de déléguer l instanciation à ses sous-classes. s appuie sur la composition : la création des objets est implémentée dans les méthodes exposées dans l interface fabrique. Fournit une interface abstraite pour créer UN produit. Fournit une interface abstraite pour créer une famille de produits Chaque sous-classe choisit quelle classe concrète instancier Chaque sous-classe concrète crée une famille de produits L intention de Fabrique Abstraite est de créer des familles d objets apparentés sans avoir à dépendre de leurs classes concrètes. 54