La modélisation avec UML: introduction aux patrons de ACDA CPOO () 1 Les patrons de Mathieu Sassolas IUT de Sénart Fontainebleau Département Informatique Année 2015-2016 2 création (abstract factory) 3 structuration (adapter) (decorator) (facade) 4 comportement (semaine prochaine) 2 / 41 Maintenant qu on «connaît» UML... 1 Les patrons de 2 création On a vu la syntaxe d UML. Le meilleur moyen de s exercer à la est de concevoir. 3 structuration 4 comportement (semaine prochaine) Problème : Professeur : «Construisez le diagramme de classe du système qui... Étudiant : Je ne sais pas par où commencer». : fournir quelques recettes de base. 3 / 41 4 / 41
Historique des patrons de Les types de patrons de Lorsque les modèles ont commencé à s unifier dans la syntaxe, on a remarqué que les même problèmes revenaient souvent. Les même solutions s appliquent. Des motifs (patterns) apparaissent dans la (design) des programmes : les design patterns. Le Gang of Four (GoF) en identifie 23. Construction : donnent des manières de créer de nouveaux objets. : donnent des structure de programmes. : donnent des moyens d interaction entre objets (qui du coup contraignent la structure). Cette année On ne verra que quelques-un des patrons de. 5 / 41 En français, le terme patron de est utilisé, car on s en sert comme modèle. 6 / 41 Souvent, on utilise ces patrons (ou plutôt motifs...) sans s en rendre compte. 1 Les patrons de 1 Les patrons de 2 création (abstract factory) 2 création (abstract factory) 3 structuration 3 structuration 4 comportement (semaine prochaine) 4 comportement (semaine prochaine) 7 / 41 8 / 41
Problème de la solution Des objets dont le type précis n est pas forcément connu sont créés par des classes constructrices. TrucConcretA1 TrucAbstrait1 TrucConcretB1 Puisque les objets que l on crée sont des concrétisations d objets abstraits, on peut également abstraire les constructeurs. On abstrait les constructeurs par une interface. CstrAbstrait <<create>> CstrA <<create>> CstrB +createtruc1() +createtruc2() +createtruc1() +createtruc2() TrucAbstrait2 +createtruc1() +createtruc2() CstrA CstrB 9 / 41 <<create>> TrucConcretA2 TrucConcretB2 <<create>> 10 / 41 +createtruc1() +createtruc2() +createtruc1() +createtruc2() Avantages de cette abstraction Le diagramme de classe complet Avec un seul Truc CstrAbstrait Un «client» qui manipule des Trucs n a pas besoin de connaître les détails de l implémentation du Truc. +createtruc() Client On peut aisément étendre les Trucs avec une implémentation TrucConcretC simplement en fournissant la classe constructeur idoine CstrC. CstrA TrucAbstrait CstrB +createtruc() <<create>> TrucConcretA +createtruc() <<create>> TrucConcretB 11 / 41 12 / 41
: le problème Dans notre système, on veut pouvoir fournir de la documentation en PDF ou en HTML. On envisage un jour d ajouter d autres formats. La documentation peut concerner l utilisation de l interface ou la maintenance du serveur. Ces documentations différentes sont manipulée différemment ; par exemple, l accès à la documentation de maintenance peut inclure une vérification des permissions. Elle est générée à chaque demande afin de tenir compte des différentes versions de tous les modules installés. PdfCreator PdfUserDoc HtmlCreator HtmlUserDoc HtmlAdminDoc DocCreator UserDoc DocHandler 13 / 41 14 / 41 PdfAdminDoc AdminDoc Problème 1 Les patrons de 2 création (abstract factory) Une classe dont on ne veut qu une seule instance. s courants : classe principale, classe de connexion à la base de donnée, constructeurs d objets (cf fabrique abstraite).... 3 structuration ClasseÀInstanceUnique 4 comportement (semaine prochaine) 15 / 41 16 / 41
Remarques La classe dispose d un attribut de classe (static) du même type qui pointe vers l unique instance. À la première instanciation, cet attribut est mis à jour. Pour les suivantes, on renvoie en fait le premier. Le véritable constructeur est privé. L appel de l instance de la classe se fait donc par.instance() et non par new (). On utilise un autre «constructeur» Instance() car sinon on aurait une récursion infinie lors de la première instanciation. instance : = null 17 / 41 +Instance() «constructor» () if (instance == null) // première instanciation instance = new (); return instance; 18 / 41 19 / 41 PdfCreator instance : PdfCreator = null «constructor» PdfCreator() +Instance() : PdfCreator : UserDoc : AdminDoc 19 / 41 PdfCreator instance : PdfCreator = null public class PdfCreator implements DocCreator { «constructor private static» PdfCreator() PdfCreator instance = null; +Instance() : PdfCreator private PdfCreator(){ : UserDoc public static : AdminDoc PdfCreator Instance(){ if (instance == null) // première instanciation instance = new PdfCreator(); return instance; public UserDoc userdoc(){ public AdminDoc admindoc(){
19 / 41 PdfCreator instance : PdfCreator = null public class PdfCreator implements DocCreator { «constructor private static» PdfCreator() PdfCreator instance = null; +Instance() : PdfCreator private PdfCreator(){ thepdfcreator : UserDoc public static = PdfCreator.Instance(); : AdminDoc Instance(){ AdminDoc if (instance freshpdfadmdoc == null) = thepdfcreator.admindoc(); // première instanciation instance = new PdfCreator(); return instance; public UserDoc userdoc(){ public AdminDoc admindoc(){ 20 / 41 1 Les patrons de 2 création 3 structuration (adapter) (decorator) (facade) 4 comportement (semaine prochaine) Problème 1 Les patrons de 2 création Nos objets sont organisés en arbre. Il faut pouvoir les parcourir. document: Section titre=«truc» preambule= «Blablabla» 21 / 41 3 structuration (adapter) (decorator) (facade) 4 comportement (semaine prochaine) 22 / 41 intro: Section titre=«machin» preambule= «Blublu» para1: Section titre=«chose» preambule= «Blabli» corps: Section titre=«bidule» preambule= para2: Section titre=«chouette» preambule= «Toto»
23 / 41 On divise en classe feuille et classe nœud qui héritent d une classe principale. Les accès se font récursivement dans les descendants. Il faut prévoir des accesseurs pour ajouter/enlever des composants. Feuille +getallattr() Arbre attr : Type +getallattr() +ajout() +retrait() 0..* #sousarbres Nœud +getallattr() +ajout() +retrait() 23 / 41 public class Feuille extends Arbre { On divise public en Type classe getallattr(){return feuille et classe nœud qui attr; héritent d une classe principale. Les public accès se class font récursivement Nœud extends dans Arbre{ les descendants. Il faut prévoir des accesseurs pour ajouter/enlever des public Type getallattr(){ composants. Type res = attr; for (Arbre desc: sousarbres) 0..* { res.append(desc.getallattr()); Arbre #sousarbres ; attr : Type Nœud return res; Feuille +getallattr() +ajout() +getallattr() +retrait() +getallattr() +ajout() +retrait() 24 / 41 Paragraphe +longueur() Texte titre : String préambule : String +longueur() : int +ajout(txt : Texte) +retrait(index : int) 0..* #soussection Section +longueur() +ajout(txt : Texte) +retrait(index : int) 24 / 41 public class Paragraphe extends Texte { public int longueur(){ Texte 0..* return (titre.length() + préambule.length()); #soussection titre : String préambule : String public class Section extends Texte{ +longueur() : int public int longueur(){ +ajout(txt : Texte) int res = 0; +retrait(index : int) res = res + titre.length() + préambule.length(); for (Texte subsec: soussections) { res= res + subsec.longueur(); Section ; Paragraphe return res; +longueur() +longueur() +ajout(txt : Texte) +retrait(index : int)
Problème 1 Les patrons de 2 création Les différentes implémentations d une même fonctionnalité sont fournies avec des noms différents, voire en plusieurs parties. PdfCreator HtmlCreator 25 / 41 3 structuration (adapter) (decorator) (facade) 4 comportement (semaine prochaine) 26 / 41 +docentete() +simpledoccorps() +fulldoccorps() C est le cas lorsque... On travaille avec des classes/composants créés par quelqu un d autre (importées). On a mal conçu le système au premier abord (il aurait fallu utiliser une fabrique abstraite). Une classe s occupe de la traduction entre les méthodes demandées par l interface et les méthodes implémentées par l Adapté. Interface +opération() #adapté 1 Adapté +opérationconcrètepart1() +opérationconcrètepart2() PdfCreator 1 #vraicreator public userdoc(){ vraicreator.docentete(); vraicreator.simpledoccorps(); DocCreator 27 / 41 +opération() public opération(){ adapté.opérationconcrètepart1(); adapté.opérationconcrètepart2(); 28 / 41 PdfCreator +docentete() +simpledoccorps() +fulldoccorps() HtmlCreator
Problème 1 Les patrons de 2 création On veut étendre une (ou deux) opération de base dans certains cas autrement que par héritages. 3 structuration (adapter) (decorator) (facade) Pourquoi pas l héritage? Pas très joli : hériter juste pour modifier une fonction, c est moche. Moins flexible : si l on veut ajouter d autres extensions, on change drastiquement le modèle. 4 comportement (semaine prochaine) 29 / 41 30 / 41 Diagramme de classes Abstraction On définit un qui pointe vers l objet de base et redéfinit seulement l opération en question. On peut définir plusieurs versions (d autres extensions) en héritant du décorateur. Le décorateur et la classe de base sont abstraits par une interface. ClasseDeBase +opération() #composant A B +opàdécorer() +opauxiliairea() +opàdécorer() +opauxiliaireb() 31 / 41 32 / 41
Diagramme de classes Diagramme de classes 32 / 41 Abstraction ClasseDeBase +opération() #composant public abstract class { A B public opàdécorer(){ composant.opàdécorer(); +opàdécorer() +opàdécorer() +opauxiliairea() +opauxiliaireb() 32 / 41 public class A extends { public opàdécorer(){ Abstraction super.opàdécorer(); opauxiliairea(); public opauxiliairea(){ ClasseDeBase +opération() #composant A +opàdécorer() +opauxiliairea() B +opàdécorer() +opauxiliaireb() 33 / 41 HtmlCreatorAbs : UserDoc HtmlCreator : UserDoc : AdminDoc #basedoc HtmlUserDecorator : UserDoc Copyrighter : UserDoc 33 / 41 public abstract class HtmlUserDecorator { protected HtmlCreator basedoc; public HtmlUserDecorator(HtmlCreator thedoc){ HtmlUserDecorator HtmlCreatorAbs this.basedoc = thedoc; public UserDoc userdoc(){ : UserDoc : UserDoc return basedoc.userdoc(); #basedoc public UserDoc userdoc() { HtmlCreator UserDoc res = super.userdoc(); Copyrighter res.append("<!-- Copyright 2014 IUT S&M -->"); return: UserDoc res; : UserDoc : AdminDoc
Problème 1 Les patrons de 2 création Besoin d une interface qui implémente certaines fonctions. Ces fonctions sont implémentées mais par différentes classes/interfaces. Ou bien il ne manque pas grand chose pour les implémenter depuis ces fonctions. 3 structuration (adapter) (decorator) (facade) Documenté +eventlog(int timespan) Logger Client DocCreator 34 / 41 4 comportement (semaine prochaine) 35 / 41 events : String[] +events(date start, Date end) Regrouper dans une classe fournissant l interface tout ce dont on a besoin en le «piochant» ailleurs. Regrouper dans une classe fournissant l interface tout ce dont on a besoin en le «piochant» ailleurs. 36 / 41 Facade +op1() +op2(typey arg1, TypeX arg2) +op3() Interface +opinter(typex arg1, TypeY arg2) Besoin ClasseA +op1a() +op2a() Abstract ClasseB +opb() 36 / 41 public op1() Facade { ClasseA.op2A(); Besoin +op1() +op2(typey arg1, TypeX arg2) public op2(typey arg1, TypeX arg2) { +op3() Abstract Interface.opInter(arg2,arg1); Interface ClasseA public op3() { ClasseB ClasseA.op1A(); +opinter(typex arg1, TypeY arg2) +op1a() +opb() +op2a()
Documenteur +eventlog(int timespan) Logger events : String[] +events(date start, Date end) Documenté DocCreator Documenteur Documenté +eventlog(int timespan) public eventlog(int timespan) { Date now= new Date(); Date Logger old= new Date(); old.goback(timespan); events : String[] Logger.events(old,now); DocCreator +events(date start, Date end) 37 / 41 37 / 41 Problème 1 Les patrons de 2 création 3 structuration (adapter) (decorator) (facade) 4 comportement (semaine prochaine) Un objet est coûteux à invoquer, on ne veut pas le créer vraiment tant qu on n en a pas besoin. Ou un objet est sur une autre ressource, que l on ne veut pas utiliser à chaque fois. Ou bien un objet nécessite des droits d accès que l on veut vérifier à chaque accès ; on souhaite également séparer la vérification de l accès. C est le cas des Query vers les bases de donnée en Java : la requête n est vraiment exécutée que sur demande expresse. 38 / 41 39 / 41
40 / 41 La classe est abstraite par une interface implémentée à la fois par le proxy et par la «vraie» classe. ClasseRéelle +op1() +op2() +autreop() ClasseAbstraite +op1() +op2() 1 #sujet Classe +op1() +op2() 40 / 41 public class Classe implements ClasseAbstraite { protected ClasseRéelle sujet = null; public op1(){ if (sujet == null) { sujet La classe = new est abstraite ClasseRéelle(); par une interface implémentée // à la fois par le proxy et par la «vraie» classe. sujet.op1(); // Délégation ClasseAbstraite public op2(){ if (sujet == null) +op1() { println("erreur"); +op2() else{ sujet.autreop(); ClasseRéelle sujet.op2(); // Délégation Classe 1 #sujet +op1() +op2() +op1() +op2() +autreop() 1 Les patrons de 2 création 3 structuration 4 comportement (semaine prochaine) 41 / 41