java entreprise et les design patterns 2 0 2007 french

109
Arnaud BARBE Page 1 sur 109 Java entreprise et les design patterns Version 2.0

description

un guide sur la programmation en java avancée

Transcript of java entreprise et les design patterns 2 0 2007 french

Page 1: java entreprise et les design patterns 2 0 2007 french

Arnaud BARBE Page 1 sur 109

Java entreprise et les design patterns

Version 2.0

Page 2: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 2 / 109

1 OBJECTIF 6

2 PATTERNS 7

2.1 OBJECTIFS 7 2.2 DESCRIPTION 7 2.3 AVANTAGES 7 2.4 INCONVENIENTS 7 2.5 GENERAL RESPONSABILITY ASSIGNEMENT SOFTWARE PATTERNS [GRASP] 8 2.5.1 PRESENTATION 8 2.5.2 PATTERNS FONDAMENTAUX 8 2.5.2.1 Expert 8 2.5.2.2 Low coupling 9 2.5.2.3 High cohesion 10 2.5.2.4 Creator 11 2.5.3 PATTERNS SPECIFIQUES 11 2.5.3.1 Controller 11 2.5.3.2 Polymorphism 12 2.5.3.3 Pure Fabrication 13 2.5.3.4 Indirection 14 2.5.3.5 Law of Demeter 14 2.6 DESIGN PRINCIPLES 15 2.6.1 PRESENTATION 15 2.6.2 GESTION DES EVOLUTIONS ET DES DEPENDANCES ENTRE CLASSES 15 2.6.2.1 Open/Closed principle 15 2.6.2.2 Liskov substitution principle 16 2.6.2.3 Dependency inversion principle 16 2.6.2.4 Interface segregation principle 17 2.6.3 ORGANISATION DE L’APPLICATION EN MODULES 18 2.6.3.1 Reuse/Release equivalence principle 18 2.6.3.2 Common reuse principle 19 2.6.3.3 Common closure principle 19 2.6.4 GESTION DE LA STABILITE DE L’APPLICATION 20 2.6.4.1 Acyclic dependencies principle 20 2.6.4.2 Stable dependencies principle 20 2.6.4.3 Stable abstractions principle 21 2.6.5 CONCLUSION 21 2.7 GANG OF FOUR [GOF] 22 2.7.1 PRESENTATION 22 2.7.2 PATTERNS DE CREATION 22 2.7.2.1 Abstract Factory ����� 22 2.7.2.2 Builder ����� 23 2.7.2.3 Factory Method ����� 24 2.7.2.4 Prototype ����� 26 2.7.2.5 Singleton ����� 27 2.7.3 PATTERNS DE STRUCTURE 28 2.7.3.1 Adapter ����� 28 2.7.3.2 Bridge ����� 29 2.7.3.3 Composite ����� 30 2.7.3.4 Decorator ����� 32 2.7.3.5 Facade ����� 33

Page 3: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 3 / 109

2.7.3.6 FlyWeight ����� 34 2.7.3.7 Proxy ����� 37 2.7.4 PATTERNS DE COMPORTEMENT 38 2.7.4.1 Chain of Responsability ����� 38 2.7.4.2 Command ����� 39 2.7.4.3 Interpreter ����� 41 2.7.4.4 Iterator ����� 42 2.7.4.5 Mediator ����� 43 2.7.4.6 Memento ����� 44 2.7.4.7 Observer ����� 45 2.7.4.8 State ����� 47 2.7.4.9 Strategy ����� 48 2.7.4.10 Template Method ����� 50 2.7.4.11 Visitor ����� 51 2.8 PATTERN JEE 52 2.8.1 BUSINESS DELEGATE 52 2.8.1.1 Problème 52 2.8.1.2 Solution 52 2.8.1.3 Implémentation 53 2.8.2 SERVICE LOCATOR 53 2.8.2.1 Problème 53 2.8.2.2 Solution 54 2.8.2.3 Implémentation 55 2.8.3 SESSION FACADE 56 2.8.3.1 Problème 56 2.8.3.2 Solution 56 2.8.3.3 Implémentation 57 2.8.4 MESSAGE FACADE / SERVICE ACTIVATOR 57 2.8.4.1 Problème 57 2.8.4.2 Solution 58 2.8.4.3 Implémentation 58 2.8.5 TRANSFER OBJECT / VALUE OBJECT 58 2.8.5.1 Problème 58 2.8.5.2 Solution 59 2.8.5.3 Implémentation 59 2.8.6 TRANSFER OBJECT ASSEMBLER / VALUE OBJECT ASSEMBLER 60 2.8.6.1 Problème 60 2.8.6.2 Solution 60 2.8.6.3 Implémentation 61 2.8.7 VALUE LIST HANDLER 61 2.8.7.1 Problème 61 2.8.7.2 Solution 61 2.8.7.3 Implémentation 62 2.8.8 DATA ACCESS OBJECT 62 2.8.8.1 Problème 62 2.8.8.2 Solution 62 2.8.8.3 Implémentation 62 2.8.9 CONCLUSION 63

3 ANTI-PATTERNS 64

3.1 CODE SOURCE 64 3.1.1 CODE COURT 64 3.1.2 OPTIMISATION 64

Page 4: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 4 / 109

3.1.3 COPIER/COLLER 64 3.1.4 VARIABLES 64 3.1.5 CONCATENATION DE CHAINES DE CARACTERES 64 3.1.6 COHERENCE DU SYSTEME 64 3.1.7 TYPAGE FORT/FAIBLE 65 3.1.8 SYNCHRONIZED 66 3.1.9 DOCUMENTATION 67 3.1.10 EXCEPTION 67 3.1.11 PARAMETRES DE METHODES 67 3.2 PRINCIPES 68 3.2.1 RESSOURCES 68 3.2.2 HERITAGE 68 3.2.3 HABITUDE 69 3.2.4 SIMPLICITE 69 3.3 MEMOIRE 69 3.3.1 DUREE DE VIE DES OBJETS 69 3.3.2 ALLOCATION MEMOIRE 69 3.4 CONCEPTION 69 3.4.1 THE GOD CLASS 69 3.4.2 SWISS ARMY KNIFE 70 3.4.3 LAVA FLOW 70 3.4.4 NO OO 70 3.4.5 PROLIFERATION OF CLASSES 70 3.4.6 GOLDEN HAMMER 71 3.4.7 SPAGHETTI CODE 71 3.4.8 REINVENT THE WHEEL 71

4 CONCLUSION 72

4.1 A RETENIR 72 4.2 ET APRES ? 72 4.3 CODE SOURCE 72 4.3.1 CREATION 72 4.3.1.1 AbstractFactory 72 4.3.1.2 Builder 74 4.3.1.3 FactoryMethod 75 4.3.1.4 Prototype 76 4.3.1.5 Singleton 76 4.3.2 STRUCTURE 77 4.3.2.1 Adapter 77 4.3.2.2 Bridge 77 4.3.2.3 Composite 79 4.3.2.4 Decorator 81 4.3.2.5 Facade 83 4.3.2.6 Flyweight 84 4.3.2.7 Proxy 86 4.3.3 COMPORTEMENT 88 4.3.3.1 Chain of responsability 88 4.3.3.2 Command 89 4.3.3.3 Interpreter 91 4.3.3.4 Iterator 94 4.3.3.5 Mediator 95 4.3.3.6 Memento 97 4.3.3.7 Observer 98

Page 5: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 5 / 109

4.3.3.8 State 100 4.3.3.9 Strategy 103 4.3.3.10 Template Method 105 4.3.3.11 Visitor 105

5 L’AUTEUR 109

Page 6: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 6 / 109

1 OBJECTIF Ce document est un condensé de ce qui aujourd’hui (et pour moi) semble essentiel dans le monde java et celui des systèmes d’informations d’entreprises. Il est orienté conception et (l’un ne va pas sans l’autre) design patterns. C’est un résumé de mes expériences professionnelles passées, des articles et livres lus. Mon travail n’a pas été de créer un énième article ou livre sur java et l’entreprise mais plutôt de regrouper un savoir éparpillé au travers d’écrits de personnalités brillantes comme Bertrand Meyer, Craig LARMAN, Robert MARTIN, la bande des quatre et les autres (pardon pour ceux que j’oublie). Par avance je m’excuse des risques de plagiat difficile à éviter dans ce type d’exercice de transcription et de résumé. D’ailleurs, rendons à césar ce qui est à césar, je ne m’attribue en aucune sorte le travail des personnes cités précédemment : je le répète mon travail se limite uniquement à condenser des concepts ou des principes.

Page 7: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 7 / 109

2 PATTERNS

2.1 Objectifs Ce chapitre a pour but de présenter les Patterns au travers de 4 approches : les patterns fondamentaux GRASP défini par Craig LARMAN, les « design principles » de Bertrand MEYER et Robert MARTIN, nous aborderons ensuite les incontournables patterns du Gang Of Four (GOF) développés par Erich Gamma, Richard Helm, Ralph Johnson et John Vlissides pour terminer avec les patterns JEE c'est-à-dire appliqués à la plateforme Java EE. Nous terminerons par un chapitre sur les anti-patterns les plus courants. Les patterns seront décrit (section présentation) puis agrémentés (section implémentation) de diagrammes adaptés au monde Java avec des exemples concrets.

2.2 Description Les designs patterns ont pour principal intérêt de décrire et de formaliser des problèmes récurrents. Ils synthétisent le savoir-faire et l'expérience de leurs concepteurs face à des problèmes bien définis. Ils apportent un début de réponse à votre problème. Attention cependant à ne pas les confondre avec un catalogue de solutions toutes prêtes : les design patterns constituent plutôt un guide. L’application d’un pattern à une solution passe par une période d’adaptation au problème. C’est comme si vous utilisiez des briques pour fabriquer un mur : vous possédez une forme de base qui va vous permettre de construire plus vite que si vous aviez à fabriquer les briques vous-même. Cependant lorsque le mur amorce un angle à 45°, il faudra découper certaines de vos briques. Globalement vous gagnez du temps, mais il faut adapter. Par conséquent, dans un contexte où les applications sont de plus en plus complexes, l'utilisation des design patterns est un moyen de réduire les risques d'échec, puisque les solutions proposées ont été "testées et approuvées" dans de nombreux projets. Les design patterns peuvent être utilisés soit directement, en se conformant aux concepts et aux règles qui les caractérisent, soit par l'intermédiaire de Frameworks qui les utilisent. Ils accélèrent la conception et les développements tout en accroissant la qualité générale des applications produites, qualité indispensable à la maintenabilité et aux transferts de compétence.

2.3 Avantages L’élévation du niveau d’abstraction est le principal intérêt : moins de perte de temps en explication, la complexité en est réduite. Le langage est commun et permet de représenter des systèmes complexes sans se perdre dans les méandres de la technique et du langage. Ils offrent une étape supplémentaire entre la conception et le code source. Globalement la qualité s’en trouve amélioré puisqu’on s’appuie sur des concepts éprouvés.

2.4 Inconvénients Le principal ennemi du design pattern c’est …le design pattern. En effet la construction par patron demande un apprentissage de longue haleine, une habitude de lecture (issue essentiellement de l’expérience) pour pouvoir les repérer ou les appliquer. Une application peut rapidement utiliser plusieurs dizaines voir des centaines d’occurrences de pattern. A ce stade ils ont plutôt tendance à se « noyer dans la masse » et ils deviennent difficiles à reconnaître. Il faut donc être prudent et soigneux quant à la répartition en package et à la dénomination des classes pour faciliter leur localisation. La problématique est de trouver à chaque fois le juste dosage entre l’intérêt de l’utilisation d’un pattern et les inconvénients inhérents à son application.

Page 8: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 8 / 109

2.5 General Responsability Assignement Software Patterns [GRASP]

2.5.1 Présentation Le GRASP pour « patterns généraux d’affectation des responsabilités », propose des patterns fondamentaux, issus essentiellement du bon sens du développeur. Ce sont des patterns simples et basiques. Ils peuvent paraître « simplistes » pour des développeurs confirmés, mais ils sont la base de toute bonne conception. D’ailleurs, nombre d’entre vous auront une impression de déjà vu en les découvrant puisque nous les appliquons inconsciemment. Contrairement aux patterns du GOF qui se rapprochent de cas concrets, les patterns GRASP sont plus abstraits. Ils ne sont en aucun cas des modèles ou design patterns mais plutôt des principes de bonne conduite. On distingue 9 patterns : les 4 premiers sont fondamentaux (Expert, Low coupling, High cohesion, Creator), les 5 suivants sont plus spécifiques (Controller, Polymorphism, Pure Fabrication, Indirection, Law of demeter).

2.5.2 Patterns Fondamentaux

2.5.2.1 Expert

2.5.2.1.1 Théorie Le pattern expert propose de déterminer les responsabilités en faisant appel au bon sens du concepteur. Il s’agit d’affecter les responsabilités à une classe en fonction des informations dont elle dispose (principe d’encapsulation) ou non (principe de collaboration). Dans le cas d’une collaboration, plusieurs « experts fragmentaires » (ou classes) sont identifiés et collaborent ensemble afin de remplir la responsabilité entière. Avec ce pattern les responsabilités seront donc mis avec les données.

2.5.2.1.2 Implémentation

Dans l’exemple ci-dessous, nous effectuons une addition. Le client instancie un objet Addition et prend la responsabilité de l’opération. Addition 01 package com.arnbe.patterns.grasp.expert.bad; 02 03 public class Addition { 04 private int firstOp; 05 private int secondOp; 06 07 /** 08 * constructeur 09 * @param op1 10 * @param op2 11 */ 12 public Addition(int op1, int op2) { 13 super(); 14 firstOp = op1; 15 secondOp = op2; 16 } 17 18 /** 19 * @return 20 */ 21 protected int getFirstOp() { 22 return firstOp; 23 } 24 25 /** 26 * @return 27 */ 28 protected int getSecondOp() { 29 return secondOp; 30 } 31 } Client 01 package com.arnbe.patterns.grasp.expert.bad; 02 03 public class Client { 04 public static void main(String[] args) { 05 06 Addition add = new Addition(4, 8);

Page 9: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 9 / 109

07 int result = add.getFirstOp() + add.getSecondOp(); 08 System.out.println(result); 09 } 10 } Ce procédé n’est pas bon car il ne respecte pas la répartition des responsabilités. En effet l’opération d’addition revient à la classe Addition et non au client. L’opération est ici simple (une addition), mais dans le cas d’une opération plus complexe, le même code devra être maintenu dans différent partie de l’application, entraînant des problèmes de maintenance, de test et voir de performance. Il serait plus correct d’écrire : Addition 01 package com.arnbe.patterns.grasp.expert.good; 02 03 public class Addition { 04 private int firstOp; 05 06 private int secondOp; 07 08 /** 09 * constructeur 10 * @param op1 11 * @param op2 12 */ 13 public Addition(int op1, int op2) { 14 super(); 15 firstOp = op1; 16 secondOp = op2; 17 } 18 19 /** 20 * @return 21 */ 22 protected int getFirstOp() { 23 return firstOp; 24 } 25 26 /** 27 * @return 28 */ 29 protected int getSecondOp() { 30 return secondOp; 31 } 32 33 /** 34 * additionne et retourne le résultat 35 * @return 36 */ 37 public int add() { 38 return firstOp + secondOp; 39 } 40 } Client 01 package com.arnbe.patterns.grasp.expert.good; 02 03 04 public class Client { 05 public static void main(String[] args) { 06 07 Addition add = new Addition(4, 8); 08 System.out.println(add.add()); 09 } 10 }

2.5.2.2 Low coupling

2.5.2.2.1 Théorie Le pattern « faible couplage » propose de limiter le couplage entre objets. On entend par couplage fort une association (délégation ou composition) ou un héritage. Les interdépendances (ou couplage fort) entre éléments d’un système rendent la compréhension plus difficile et la réutilisation quasi impossible. Un couplage faible facilite la réutilisation et la maintenance des éléments du système avec peu d’effet de régression (principe de modularité). Ici il convient de trouver le juste compromis entre des classes très dépendantes et un système peu communicant.

2.5.2.2.2 Implémentation

Dans le diagramme de classe suivant, un client possède des factures, et ces factures peuvent être imprimées.

Page 10: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 10 / 109

Figure 1 le Customer est couplé avec Invoice mais aussi avec le Printer

Il est préférable que l’Invoice ait la capacité de s’imprimer et que Customer ne connaisse pas l’implémentation (utilisation de la classe Printer). Le diagramme suivant est plus proche de la bonne solution.

Figure 2 Couplage plus faible

2.5.2.3 High cohesion

2.5.2.3.1 Théorie Le pattern de forte cohésion tente de répondre à la problématique d’affectation des responsabilités tout en conservant une complexité acceptable du système. Le but est de séparer les responsabilités en domaine (fonctionnel, technique, etc.) afin de préserver la cohésion de la classe. Finalement une classe cohésive est plus facile à comprendre car elle a un but bien spécifique (assuré par la finesse de l’affectation des responsabilités) et donc facilement évolutive. Attention à une cohésion trop forte (multiplication des classes) qui entraîne un couplage élevé (beaucoup de classes collaborent entre elles). A l’inverse, une classe peu cohésive est difficile à maintenir, à comprendre, à réutiliser et est globalement plus fragile car sensible à la variation.

2.5.2.3.2 Implémentation

Imaginons que notre système précédent doit assurer la persistance, la gestion et l’affichage du Customer et de l’Invoice.

Figure 3 Exemple de faible cohésion

Dans cet exemple, la classe Customer doit endosser la responsabilité de l’affichage (openCustomerWindow()), de la gestion et de la persistance de l’objet Customer (saveCustomerInDB()). La cohésion de la classe est faible et les responsabilités trop concentrées. Ce type de classe est souvent pénible à maintenir car chaque nouvelle fonctionnalité rend la classe de plus en plus complexe. A l’inverse une cohésion trop forte entraîne un couplage fort et donc une réutilisation quasi nulle.

Page 11: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 11 / 109

Figure 4 Attention à une trop forte cohésion !!

Ici la cohésion est poussée à l’extrême, mais démontre bien les risques liés à une cohésion trop forte. Toutefois si vous prenez l’option de rendre vos classes fortement cohésives, il est possible d’en réduire le couplage avec le « monde extérieur » en utilisant une façade (cf. pattern Facade du GOF).

2.5.2.4 Creator

2.5.2.4.1 Théorie Le pattern Creator est relativement proche de l’Expert (en terme de répartition des responsabilités), à une nuance près : le Creator possède uniquement la responsabilité de la création d’un l’objet. Ceci va d’ailleurs dans le sens du pattern Low Coupling puisque la responsabilité de la création peut être attribuée à une classe dédiée. En effet la création d’un objet peut nécessiter la collaboration de plusieurs classes : dans ce cas seul le Creator possède des dépendances multiples (cf. pattern abstract factory ou builder du GOF).

2.5.2.4.2 Implémentation

Dans le diagramme de collaboration suivant, Customer prend la responsabilité de la création des Invoice qui prend à son tour la responsabilité de la création du Printer.

Figure 5 Répartition de la responsabilité de création

2.5.3 Patterns Spécifiques 2.5.3.1 Controller

2.5.3.1.1 Théorie Le pattern Controller propose de prendre en charge la responsabilité de la réception et du traitement des messages systèmes. Par message système on entend tout message généré par un acteur externe. Le Controller peut déléguer des tâches à d’autres objets, sont but étant plutôt de coordonner les actions et les évènements. Il existe plusieurs types de Controller :

Page 12: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 12 / 109

� Facade Controller : point d’entrée unique vers un système ou sous système. � Use Case Controller : objet de contrôle pour un évènement système particulier. � Role Controller : élément du domaine ayant le rôle du contrôleur.

Finalement, il est préférable d’utiliser des classes Controller avec une cohésion forte : la classe ne s’occupe que du traitement du message (redirection) et non de la logique métier associée (cf. pattern Facade du GOF).

2.5.3.1.2 Implémentation

Dans le diagramme ci dessous l’implémentation d’un Facade Controller faisant l’interface entre le client et un système sous jacent complexe et fortement couplé. L’intérêt est de limiter le couplage du client vers le sous système et ainsi de faciliter la réutilisation ou le remplacement du sous système par un autre. Il normalise par un point de passage de unique, les communications vers un système.

Figure 6 Limitation du couplage par utilisation d’un Controller

2.5.3.2 Polymorphism

2.5.3.2.1 Théorie Ce pattern utilise la variation de comportement de classe en fonction de leur type. Il apporte une solution pour simuler des comportements ou des états sans avoir à modifier le client : c’est une approche par composants enfichables. Cette solution est plus élégante car la cohésion est forte et les risques de régression de l’ensemble faible en cas d’évolution applicative (cf. pattern Strategy du GOF).

2.5.3.2.2 Implémentation

Dans notre exemple, le client utilise un véhicule. Dans ce cas de figure, peu importe que l’implémentation soit une voiture, un avion ou un bateau. Il devient très simple alors d’ajouter de nouveaux comportements. Chaque implémentation prend en charge le comportement « start() ».

Page 13: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 13 / 109

Figure 7 Exemple de polymorphisme

2.5.3.3 Pure Fabrication

2.5.3.3.1 Théorie Le pattern Pure Fabrication ne représente pas le modèle du domaine : c’est une forme d’abstraction pour effectuer des traitements dans le seul but de rendre le système plus cohésif et de limiter le couplage. A contrario, le pattern Expert n’est pas un pattern Pure Fabrication car il représente un objet métier. Plus simplement ce sont des créations de l’esprit qui n’existent pas dans le modèle du domaine car ils ne représentent aucune donnée comme par exemple un client ou une facture.

2.5.3.3.2 Implémentation

La classe suivante cumule trop de responsabilités : elle est donc faiblement cohésive. En effet les notions de persistance exprimées par les méthodes loadCustomer() et saveCustomer() affaiblissent la cohésion de la classe.

Figure 8 Classe faiblement cohésive

Il est préférable dans ce cas d’utiliser, par exemple, la délégation de ces responsabilités à une autre classe.

Figure 9 Délégation de responsabilité

Page 14: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 14 / 109

2.5.3.4 Indirection

2.5.3.4.1 Théorie L’indirection intervient lorsque l’on veut éviter un couplage entre deux Experts. Dans ce cas un objet servira d’intermédiaire. D’ailleurs beaucoup de Pure Fabrication sont crées pour des besoins d’indirection.

2.5.3.4.2 Implémentation

Prenons l’exemple de la classe Vector qui propose, pour énumérer ses éléments, une Enumeration (ancêtre de l’Iterator). Notre client souhaite manipuler cette liste mais sans vouloir de couplage avec cette Enumeration. Dans ce cas de figure il utilise un Adapter qui propose une interface Iterator pour le client, tout en manipulant (en interne) une Enumeration.

Figure 10 L'Adapter reduit le couplage

2.5.3.5 Law of Demeter

2.5.3.5.1 Théorie Le pattern Law of Demeter (ou protection des variations) tente de limiter l’effort dû à l’évolution d’un système. Il consiste à utiliser uniquement des dépendances directes, c'est-à-dire des dépendances envers des composants qui rendent un service, et aucune dépendance indirecte, c'est-à-dire une dépendance envers un composant permettant d'obtenir la référence sur un composant rendant un service. Les parties jugées modulables ou susceptibles d’évoluer doivent être isolées.

2.5.3.5.2 Implémentation

Il existe plusieurs moyens à la disposition du concepteur pour rendre un système moins sensible aux variations :

� Externalisation de certaines valeurs dans des fichiers de propriétés. � Une conception par interfaces (JDBC, JAXB, JNDI, …). � Une communication par messages standards : l’émetteur et le récepteur ne se

connaissent pas (techniquement parlant). � « Don’t talk to strangers » : éviter les dépendances non nécessaires avec des systèmes

sans en avoir réellement l’utilité. D’ailleurs beaucoup de designs patterns et de principes de programmation vont dans ce sens. C’est le cas de l’encapsulation où les données et les traitements sont protégés de l’environnement extérieur et où les éléments extérieurs ne sont pas affectés par les changements internes de la classe.

Page 15: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 15 / 109

2.6 Design principles 2.6.1 Présentation

Les « design principles » sont 10 grands principes de conception répartis en 3 groupes :

� Gestion des évolutions et des dépendances entre classes. � Organisation de l’application en modules. � Gestion de la stabilité de l’application.

En appliquant ces principes, ont doit arriver à un programme robuste et facilement modifiable et ce sans y introduire d’erreurs.

2.6.2 Gestion des évolutions et des dépendances entre classes 2.6.2.1 Open/Closed principle

2.6.2.1.1 Théorie Le principe d’ouverture/fermeture tente de limiter les impacts (et donc les coûts) liées aux changements. Tout module doit être ouvert aux extensions et fermé aux modifications. Le module peut donc être étendu pour proposer des comportements n’existant pas lors de sa conception mais les nouvelles extensions ne doivent pas altérer le code existant (Cf. pattern Strategy du GOF). Attention cependant à ne pas exagérer et ne pas mettre trop facilement des points d’ouverture dans votre application afin de ne pas injecter une complexité inutile. Il convient d’identifier les futurs points d’ouverture en s’inspirant des besoins pressentis par le développeur ou des changements multiples constatés durant le développement.

2.6.2.1.2 Implémentation

L’abstraction par utilisation d’interface est la solution la mieux adaptée en java pour limiter les impacts des nouvelles fonctionnalités.

Figure 11 Limitation de la variation logicielle par utilisation de l'héritage

Dans ce cas de figure si nous souhaitons ajouter un comportement (Boat), il suffit d’ajouter une classe dérivant de l’interface Vehicule. Les classes existantes ne sont pas modifiées et un comportement nouveau est supporté. Ce motif s’applique naturellement si certains principes de bon sens sont respectés : - Les variables sont privées : des variables publiques non justifiées (autre que final) sont plutôt

le signe d’une mauvaise conception. - Les variables ne doivent pas être globales : elles maintiennent inutilement des références et

alourdissent le système. L’utilisation du mot clé instanceof est à proscrire au niveau de l’abstraction. En effet si l’on souhaite ajouter un nouveau comportement ou une nouvelle fonctionnalité, il faudra modifier la section de code de l’abstraction (ou du client) qui adopte un comportement différent (modification d’une fonctionnalité existante) en fonction de l’implémentation.

Page 16: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 16 / 109

2.6.2.2 Liskov substitution principle

2.6.2.2.1 Théorie Le principe de substitution de Liskov (ou principe des 100%) part de l’hypothèse que le client doit pouvoir utiliser des objets dérivés sans s’en apercevoir et sans avoir à changer son comportement en fonction de l’implémentation. A tout moment une classe peut se substituer à une autre pour autant que ces deux classes implémentent la même interface. Ce principe est important car bon nombre d’héritages sont fait par commodité et sans réelle existence d’une nécessité d’abstraction. D’ailleurs, il prend à contre pied un autre mauvais principe (anti pattern dirons nous) qui veut que l’héritage serve à factoriser du code : les puristes lui préféreront la délégation. Dans le cas où une classe ne se substituerait pas parfaitement à une autre, il y a un problème de conception.

2.6.2.2.2 Implémentation

Dans cet exemple, chaque implémentation de Vehicule peut prendre la place d’une autre sans que le client puisse (et doive) s’en apercevoir.

Figure 12 Substitution possible entre les différentes implémentations

2.6.2.3 Dependency inversion principle

2.6.2.3.1 Théorie Le principe d’inversion de dépendance part du précepte que la forte dépendance entre les couches d’une application, nuit à sa réutilisation et à son évolution. Par exemple une couche IHM doit connaître la couche métier pour pouvoir l’utiliser. La couche IHM est écrite pour cette couche métier et ne peut être réutilisée autrement. Pour éviter ce couplage, les modules de haut niveau ne doivent pas connaître les modules de bas niveau. Ils doivent plutôt dépendre d’abstractions. Le module de haut niveau s’inscrit au module de bas niveau et écoute ses évènements (on retrouve ici les fondements de l’injection de code). Cette approche permet aussi de faire apparaître clairement les messages entre les objets et donc de facilement les normaliser.

2.6.2.3.2 Implémentation

Figure 13 Un couplage fort nuit à la réutilisation

Page 17: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 17 / 109

L’utilisation d’une abstraction et d’une inversion des dépendances réduit le couplage et facilite la réutilisation. La couche métier OrderBusiness (ou modèle) ne connaît que l’interface OrderInterface (implémenté par OrderAction) vers laquelle elle envoie les évènements systèmes.

Figure 14 Réutilisation plus aisé

Aujourd’hui des containers légers comme Spring ou HiveMind offre une approche similaire. Les services sont définis par une interface et une implémentation. Le container instancie l’implémentation correspondante. Cette approche est intéressante en plusieurs points :

� Couplage faible : seul les interfaces sont connus. � Principe d’ouverture/fermeture respecté. � Globalement l’application est moins sensible à la variation.

2.6.2.4 Interface segregation principle

2.6.2.4.1 Théorie Il arrive que certaines classes remplissent plusieurs rôles au sein d’un système. Ce type de classe expose tous ses services à chaque client et ne facilite pas la compréhension. De plus chaque modification de la classe impacte potentiellement tous les clients. Ce pattern propose donc de limiter la taille de la classe en répartissant les méthodes dans plusieurs classes (regroupement technique ou fonctionnel par exemple).

2.6.2.4.2 Implémentation

Lorsqu’un service propose trop de fonctionnalités…

Figure 15 Trop de fonctionnalités proposées…

…il convient de répartir les services dans plusieurs classes.

Page 18: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 18 / 109

Figure 16 Répartition plus homogène

2.6.3 Organisation de l’application en modules

2.6.3.1 Reuse/Release equivalence principle

2.6.3.1.1 Théorie Le pattern d’équivalence livraison/réutilisation propose d’avoir une approche modulaire de l’application. Chaque sous-système est considéré comme une application (ou un Framework), et développé et maintenu par un tiers. Il doit être utilisé tels quel, sans avoir à en comprendre les mécanismes internes.

2.6.3.1.2 Implémentation

Le monde java avec les spécifications du JCP (Java Community Process) ainsi que les différents Frameworks existants se prêtent bien à cette approche (Hibernate, Spring, Struts, etc…).

Figure 17 Utilisation du pattern DAO

Plutôt que de réécrire un Framework de persistance avec un pattern type DAO, utilisez un outil d’ORM (Object Relational Mapping).

Page 19: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 19 / 109

Figure 18 Utilisation d'un ORM

L’approche la plus professionnelle est d’utiliser les 2 concepts : une DAO pour masquer l’implémentation (Hibernate par exemple) et l’utilisation d’une session Hibernate dans une implémentation dédié (HibernateDao). Une factory prend la responsabilité de la création de la bonne implémentation (décrite dans un fichier de propriétés par exemple).

Figure 19 Utilisation conjointe : ORM + DAO

2.6.3.2 Common reuse principle

2.6.3.2.1 Théorie Le pattern de réutilisation commune propose de rassembler dans le même package, les classes susceptibles d’être réutilisées conjointement. Il est important de rassembler ensemble des classes ayant des rapports de dépendances les unes avec les autres, inutile en effet de mettre ensemble des classes réutilisables mais ne couvrant pas le même domaine technique ou fonctionnel.

2.6.3.2.2 Implémentation

Imaginons un système de persistance de type DAO. Il sera nécessaire de posséder des classes d’outils dédiées, par exemple, à la gestion de la connexion, de la transaction ou du cache. Toutes ces classes devront être localisées dans le même package et distribuées ensemble.

2.6.3.3 Common closure principle

2.6.3.3.1 Théorie Le principe de fermeture commune stipule que les classes qui seront impactées par un même changement soient regroupées dans un même package.

Page 20: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 20 / 109

2.6.4 Gestion de la stabilité de l’application 2.6.4.1 Acyclic dependencies principle

2.6.4.1.1 Théorie Le principe de dépendances acycliques repose sur la répartition des classes en packages pour limiter la propagation des changements au sein de l’application. La propagation des changements étant guidée par les dépendances entre packages, l’organisation de ces dépendances est un élément fondamental de l’architecture. Pour pourvoir gérer ces changements, il convient de ne pas introduire de dépendance cyclique dans les packages et de regrouper les classes dépendantes dans le même package.

2.6.4.1.2 Implémentation

Dans ce cas de figure les dépendances sont cycliques, ce qui est un signe de mauvaise conception.

Figure 20 Dépendance cyclique

Pour contourner un besoin de référence cyclique, utilisez le principe d’inversion des dépendances.

2.6.4.2 Stable dependencies principle

2.6.4.2.1 Théorie Le principe de relation de dépendance stable stipule qu’un package doit dépendre d’un package plus stable que lui. Globalement ce principe permet de stabiliser l’application. Le développeur apportera donc encore plus de soin à un module si celui ci est fortement utilisé par les autres. Plusieurs critères permettent de définir le niveau de stabilité d’un module :

� Plus les dépendances de ce module vers d’autres modules sont élevées, plus il est susceptible d’être impacté par un changement et donc moins il est considéré comme stable.

� Plus il existe des relations vers ce module et plus il est considéré comme stable.

2.6.4.2.2 Implémentation

Dans ce cas de figure, on admet que la stabilité du composant doit être et sera (par force) maximale.

Page 21: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 21 / 109

Figure 21 Stabilité maximale

Par contre, on admet ici que la stabilité du composant est minimale puisque chaque changement d’un des composants peut l’affecter ou le déstabiliser.

Figure 22 Stabilité minimale

2.6.4.3 Stable abstractions principle

2.6.4.3.1 Théorie Le principe de stabilité des abstractions énonce que les packages les plus abstraits doivent être les plus stables et les plus concrets les moins stables. Le degré de stabilité d’un package doit correspondre à son degré d’abstraction.

2.6.4.3.2 Implémentation

Dans une application, les règles métiers seront plus stables que les objets techniques qu’ils utilisent (EJB, Log, etc.) : En effet ceux-ci étant plus concrets, ils sont plus sensibles aux évolutions.

2.6.5 Conclusion Les principes énoncés ci-dessus doivent vous permettre d’obtenir une application extensible, robuste et réutilisable. Contrairement aux design patterns, ce ne sont que des principes et non une recette directement applicable sur du code. Elle on toutefois l’intérêt de définir un cadre et fixant de fait certaines limites.

Page 22: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 22 / 109

2.7 Gang Of Four [GOF] 2.7.1 Présentation

Le GOF propose 23 patterns avec une classification en 3 catégories :

� Création : propose des modèles pour faciliter ou rationaliser la création de classes. Ils permettent de faire la différence entre la création et le montage des objets et plus globalement d’abstraire le mécanisme d’instanciation.

� Structure : Ces patterns proposent aussi bien des modèles d’assemblages que

d’adaptation. Ce sont des patterns adaptés aux problèmes courants (structures de données, …).

� Comportement : propose des modèles qui réagissent à leur environnement en adoptant

des comportements spécifiques différents au cours de leur cycle de vie. Note : Ce paragraphe n’a pas pour vocation de proposer un cours complet sur les Design patterns du GOF : pour de plus amples renseignements, se reporter au livre de référence « Design Patterns Elements of Reusable Object-Oriented Software ». Chaque motif est noté de 1 à 5 en fonction de son intérêt.

2.7.2 Patterns de création 2.7.2.1 Abstract Factory ��������������������

2.7.2.1.1 Théorie

2.7.2.1.1.1 Présentation

Une fabrique abstraite délocalise la responsabilité de la création d’un objet. Plutôt que de confier cette responsabilité au constructeur de l’objet, un objet dédié (la fabrique) la prend en charge. On peut alors facilement utiliser des fabriques différentes pour le même type d’objet. Elle fournit donc une interface pour créer des objets apparentés sans avoir la nécessité de spécifier leur classe concrète.

2.7.2.1.1.2 UML

Figure 23 UML Théorique Abstract Factory

2.7.2.1.2 Implémentation

2.7.2.1.2.1 Présentation

Dans cet exemple, chaque Factory fabrique un seul objet (PlaneFactory fabrique des Plane et TruckFactory des Truck). L’intérêt est de pouvoir les manipuler en utilisant l’interface Factory : le processus de création est donc normalisé. A noter que si l’on veut s’abstraire du choix de la Factory on peut utiliser une Factory de Factory.

Page 23: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 23 / 109

2.7.2.1.2.2 UML

Figure 24 UML implémentation Abstract Factory

2.7.2.1.2.3 Utilisation

Factory pFactory = new PlaneFactory(); Vehicule vehicule = pFactory.build(); //code...

2.7.2.2 Builder ��������������������

2.7.2.2.1 Théorie

2.7.2.2.1.1 Présentation

Bien que souvent confondu avec le pattern Abstract Factory, le Builder est une approche différente de la construction d’objets. En effet, outre le fait qu’il construit l’objet, il définit la façon de le construire (on peut presque parler de configuration). On l’utilise aussi dans le cas où l’algorithme de création et la méthode d’assemblage doivent être découplés de l’objet.

2.7.2.2.1.2 UML

Figure 25 UML théorique Builder

Correspondance diagramme théorique implémentation AbstractFactory Factory ConcreteFactory TruckFactory,

PlaneFactory AbstractProduct Vehicule AbstractProductImpl Truck, Plane

Page 24: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 24 / 109

2.7.2.2.2 Implémentation

2.7.2.2.2.1 Présentation

Ici le Director prend la responsabilité de l’ordre de montage de l’A380, alors que l’A380Builder conserve la responsabilité de la construction des différentes parties.

2.7.2.2.2.2 UML

Figure 26 UML implémentation Builder

2.7.2.2.2.3 Utilisation

A380Builder builder = new A380Builder(); new Director(builder).build(); Plane product = builder.getResult();

2.7.2.3 Factory Method ��������������������

2.7.2.3.1 Théorie

2.7.2.3.1.1 Présentation

La Factory Method est une variante de l’Abstract Factory. En effet, plutôt que de laisser au choix du client l’instanciation de la classe concrète de construction, c’est la classe concrète qui en prend la responsabilité.

Correspondance diagramme théorique implémentation Product Plane ConcreteBuilderProduct A380 ConcreteBuilder A380Builder Builder PlaneBuilder Director Director

Page 25: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 25 / 109

2.7.2.3.1.2 UML

Figure 27 UML théorique Factory Method

2.7.2.3.2 Implémentation

2.7.2.3.2.1 Présentation

Contrairement à l’Abstract Factory, c’est le client (AbstractCreator) qui prend la responsabilité de la création de l’objet via sa méthode factoryMethod().

2.7.2.3.2.2 UML

Figure 28 UML implémentation Factory Method

2.7.2.3.2.3 Utilisation

Cf. méthode sampleUse().

Correspondance diagramme théorique implémentation AbstractProduct Product ConcreteProduct Truck AbstractCreator AbstractCreator ConcreteCreator TruckCreator

Page 26: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 26 / 109

2.7.2.4 Prototype ��������������������

2.7.2.4.1 Théorie

2.7.2.4.1.1 Présentation

Le prototype offre une autre approche quant à la création d’objet. Le principe de Factory est intéressant, mais pèche par la création d’autant de Factory concrètes qu’il existe d’objets concrets. Le pattern Prototype donne à l’objet concret la responsabilité de sa fabrication par une méthode de clonage.

2.7.2.4.1.2 UML

Figure 29 UML théorique Prototype

2.7.2.4.2 Implémentation

2.7.2.4.2.1 Présentation

Dans notre exemple, la classe Plane et Truck ont la capacité, via la méthode clone(), de se dupliquer. On remarque une forte similitude avec java qui propose, via l’objet « Object », l’interface clone().

2.7.2.4.2.2 UML

Figure 30 UML implémentation Prototype

2.7.2.4.2.3 Utilisation //code... Prototype otherPlane = plane.clone();

Correspondance diagramme théorique implémentation Prototype Prototype ConcretePrototype Plane, Truck

Page 27: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 27 / 109

//code...

2.7.2.5 Singleton ��������������������

2.7.2.5.1 Théorie

2.7.2.5.1.1 Présentation

Il est parfois nécessaire de n’avoir qu’une seule instance d’une classe dans un système : c’est le cas lorsqu’un seul objet est nécessaire pour coordonner un ensemble. Le pattern Singleton propose un motif pour contrôler la création de l’instance. Cette responsabilité est attribuée à l’objet concerné pour éviter un phénomène d’instanciation multiple. A noter que le pattern singleton n’est pas nécessairement limitatif à une seule instance : on peut parfaitement l’utiliser pour en contrôler plusieurs comme un pool d’objet.

2.7.2.5.1.2 UML

Figure 31 UML théorique Singleton

2.7.2.5.2 Implémentation

2.7.2.5.2.1 Présentation

Dans cet exemple il suffit d’utiliser la méthode statique getInstance() pour obtenir une poignée sur l’objet unique. L’instance retournée sera systématiquement la même.

2.7.2.5.2.2 UML

Figure 32 UML implémentation Singleton

2.7.2.5.2.3 Utilisation Singleton single = Singleton.getInstance();

Correspondance diagramme théorique implémentation Singleton Singleton

Page 28: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 28 / 109

2.7.3 Patterns de structure 2.7.3.1 Adapter ��������������������

2.7.3.1.1 Théorie

2.7.3.1.1.1 Présentation

Le pattern Adapter permet de faire fonctionner un objet avec une interface qu’il n’implémente pas habituellement. Ce motif est particulièrement pratique si vous ne souhaitez pas dépendre d’une implémentation en particulier ou si vous souhaitez l'accommoder. L’adapter fera le lien entre les méthodes de la nouvelle interface vers l’ancienne.

2.7.3.1.1.2 UML

Figure 33 UML théorique Factory Adapter

2.7.3.1.2 Implémentation

2.7.3.1.2.1 Présentation

La version proposée est à base de délégation puisque java ne supporte pas l’héritage multiple. Dans cet exemple, nous adaptons un objet de type Enumeration pour qu’il supporte l’interface Iterator. L’utilisation pour le client est transparente puisqu’il utilisera une Enumeration comme un Iterator.

2.7.3.1.2.2 UML

Figure 34 UML Implémentation Adapter

Page 29: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 29 / 109

2.7.3.1.2.3 Utilisation StringTokenizer tokenize = new StringTokenizer("java c'est bien");

for (Iterator iter = new IteratorEnumerationAdapter(tokenize); iter.hasNext();) { String element = (String) iter.next(); System.out.println(element); }

2.7.3.2 Bridge ��������������������

2.7.3.2.1 Théorie

2.7.3.2.1.1 Présentation

Le bridge permet de découpler une abstraction de son implémentation pour les rendre indépendantes des évolutions. On évite un lien permanent entre l’abstraction et l’implémentation, ce qui permet à chacun d’évoluer indépendamment. Au final les modifications d’implémentations n’entraînent pas de recompilation de l’abstraction.

2.7.3.2.1.2 UML

Figure 35 UML théorique Bridge

2.7.3.2.2 Implémentation

2.7.3.2.2.1 Présentation

Dans notre exemple nous déléguons la gestion des données d’un groupe de client à un Implementor. L’abstraction du groupe de client propose des méthodes pour naviguer d’un client à un autre. Enfin pour notre exemple nous utilisons une gestion des données en mémoire. Au passage on s’aperçoit qu’il est très facile de proposer une autre implémentation (FileData ou DBData par exemple) sans qu’il soit nécessaire de recompiler l’abstraction.

Correspondance diagramme théorique implémentation Target Iterator Adapter IteratorEnumerationAdapter Adaptee Enumeration

Page 30: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 30 / 109

2.7.3.2.2.2 UML

Figure 36 UML implémentation Bridge

2.7.3.2.2.3 Utilisation AbstractCustomers ab = new Customers("bons clients");

ab.setImplementor(new InMemoryData()); ab.add("claude durillon");

2.7.3.3 Composite ��������������������

2.7.3.3.1 Théorie

2.7.3.3.1.1 Présentation

Ce pattern s’adapte parfaitement à l’utilisation et à la manipulation d’objets complexes (structure par association, arborescence, …) dont la profondeur n’est pas connue. Il propose une interface simplifiée pour la manipulation d’objets uniques ou d’objets composés du même type.

Correspondance diagramme théorique implémentation Abstraction AbstractCustomer RefinedAbstraction Customers Implementor DataObject ConcreteImplementor InMemoryData

Page 31: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 31 / 109

2.7.3.3.1.2 UML

Figure 37 UML théorique Composite

2.7.3.3.2 Implémentation

2.7.3.3.2.1 Présentation

Dans notre exemple, la classe Product contient une liste de ses enfants eux-mêmes de type Product. Cette arborescence représente la notion de « est composé de… ». La manipulation de ce graphe complexe se fait depuis le ProductComposite pour simplifier les opérations.

2.7.3.3.2.2 UML

Figure 38 UML implémentation Composite

2.7.3.3.2.3 Utilisation Product pizza = new Product("1245789456124", "pizza 4 fromages"); Product pate = new Product("1245789446124", "pate à pizza");

Correspondance diagramme théorique implémentation Component, Leaf Product Composite ProductComposite

Page 32: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 32 / 109

Product farine = new Product("1245789446127", "farine"); Product eau = new Product("1245781446127", "eau"); Product fromage = new Product("1745789446127", "fromage"); //crée la pizza ProductComposite composite = new ProductComposite(pizza); //ajoute la pâte composite.addProduct(pate, pizza.getId()); //la pâte est constituée d'eau et de farine composite.addProduct(farine, pate.getId()); composite.addProduct(eau, pate.getId()); //enfin on ajoute le fromage composite.addProduct(fromage, pizza.getId()); //en fait nan pas de fromage : trop gras composite.removeProduct(fromage.getId());

2.7.3.4 Decorator ��������������������

2.7.3.4.1 Théorie

2.7.3.4.1.1 Présentation

Le motif Decorator permet d’accrocher dynamiquement des responsabilités à un objet. Il remplace le principe d’héritage en offrant une méthode plus souple. On peut aussi l’utiliser si la classe ne peut être héritée : c’est le cas si la classe utilise le modificateur final.

2.7.3.4.1.2 UML

Figure 39 UML théorique Decorator

2.7.3.4.2 Implémentation

2.7.3.4.2.1 Description

Dans notre exemple, nous allons ajouter une responsabilité sur la classe LibraryItem : les instances de cette classe auront la capacité d’être empruntées par des clients. Par le truchement de l’héritage, ses classes filles, en bénéficieront aussi. La méthode display() est surchargée par la classe Borrowable pour afficher les informations des emprunteurs en cours : c’est dans cette méthode que les nouvelles responsabilités sont ajoutées.

Page 33: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 33 / 109

2.7.3.4.2.2 UML

Figure 40 UML implémentation Decorator

2.7.3.4.2.3 Utilisation Book book = new Book("Michael MARSHALL", "Le sang des anges", 10); book.display(); Video video = new Video("Jacques TATI", "Mon oncle", 200 ,5); video.display(); Borrowable borrowVideo = new Borrowable(video); borrowVideo.borrowItem("Mr Dupont"); borrowVideo.borrowItem("Mr Durand"); //invocation des responsabilités supplémentaires borrowVideo.display();

2.7.3.5 Facade ��������������������

2.7.3.5.1 Théorie

2.7.3.5.1.1 Présentation

Le pattern Facade fournit un point d’entrée unique et une interface simple à un système complexe. Il s’inscrit dans un principe de découpage en couches et le risque d’un couplage fort avec les classes internes d’une API est réduit. On peut le comparer à un tableau de bord d’un véhicule : vous ne voyez et ne pouvez modifier que ce qui est nécessaire (essuie-glace, clignotants) et le fait qu’il existe des pistons et des bielles dans le moteur ne vous concerne pas.

Correspondance diagramme théorique implémentation Component LibraryItem ConcreteComponent Book, Video Decorator Decorator ConcreteDecorator Borrowable

Page 34: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 34 / 109

2.7.3.5.1.2 UML

Figure 41 UML théorique Facade

2.7.3.5.2 Implémentation

2.7.3.5.2.1 Description

Dans cet exemple, notre façade est matérialisée par la classe Compiler. Elle est le point d’entrée unique à notre système de compilation.

2.7.3.5.2.2 UML

Figure 42 UML implémentation Facade

2.7.3.5.2.3 Utilisation 5 Compiler compiler = new Compiler(); 6 compiler.compile();

2.7.3.6 FlyWeight ��������������������

2.7.3.6.1 Théorie

2.7.3.6.1.1 Présentation

Le motif FlyWeight propose une technique de partage permettant la mise en œuvre d’une grande quantité d’objet de granularité fine. Ce pattern s’applique particulièrement bien si l’état des objets peut être externalisé, si le stockage des éléments coûte cher et enfin si les objets n’ont pas d’identifiant (c’est le type de la classe qui identifie l’objet et non un de ses attributs). Le Pattern FlyWeight propose le partage d’objet mais ne l’impose pas.

Correspondance diagramme théorique implémentation Facade Compiler Subsystem CodeGenerator, Scanner,

Parser

Page 35: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 35 / 109

2.7.3.6.1.2 UML

Figure 43 UML théorique FlyWeight

2.7.3.6.2 Implémentation

2.7.3.6.2.1 Présentation

Prenons un éditeur de texte pour notre exemple. Si nous raisonnons « pure objet », nous pensons de la manière suivante : pour chaque caractère de chaque ligne et de chaque page, j’ai un objet de type caractère. L’idée peut être intéressante mais on se rend vite compte que le nombre des objets peut rapidement « exploser » pour des documents de taille importante. Le motif FlyWeight permet d’externaliser certaines données : par exemple la largeur et la hauteur en point, le code du caractère ne varient jamais. Par contre remis dans le contexte de la page, il est nécessaire de connaître en plus la position ligne/colonne, la police employée, etc… Ce pattern fait donc le distinguo entre les données intrinsèques (Position ligne/colonne, etc.) et externalisables (Code caractère, etc.).

2.7.3.6.2.2 UML

Page 36: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 36 / 109

Figure 44 UML implémentation FlyWeight

2.7.3.6.2.3 Utilisation GraphicalItemFactory factory = new GraphicalItemFactory(); /*String line1 = "my flowers are beautiful"; String line2 = "my taylor is rich";*/ String line1 = "ABBBZZZABBZ"; String line2 = "ABABABAAAAABBZ"; Row row1 = (Row) factory.getRow(line1); Row row2 = (Row) factory.getRow(line2); //crée le context PageContext context = new PageContext(); //positionne la fonte sur la 1ere ligne pour le caractère de 3 à 10 context.setFont("Arial", 1, 3 , 10); //parcours la 1ere ligne for (char c : row1.charArray()) { GraphicalItem character = factory.getCharacter(c); character.display(context); context.next(); } context.nextLine(); for (char c : row2.charArray()) { GraphicalItem character = factory.getCharacter(c); character.display(context);

Correspondance diagramme théorique implémentation Flyweight GraphicalItem ConcreteFlyweight Character, CharacterA, CharacterB,

CharacterZ Unshared ConcreteFlyweight

Row

FlyweightContext Context, PageContext FlyweightFactory GraphicalItemFactory

Page 37: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 37 / 109

context.next(); }

2.7.3.7 Proxy ��������������������

2.7.3.7.1 Théorie

2.7.3.7.1.1 Présentation

Le Proxy a pour fonction de se positionner entre un objet et son client. Il permet de référencer un objet autrement qu’avec une poignée. Plusieurs Proxy existent en fonction de leur finalité technique. On utilisera un Remote Proxy pour cacher toute la couche d’instanciation distante, un Protection Proxy pour gérer l’authentification, un Lazy Proxy pour gérer l’instanciation d’un objet à la demande, etc… Attention à ne pas le confondre avec une Facade ou un Adapter.

2.7.3.7.1.2 UML

Figure 45 UML théorique Proxy

2.7.3.7.2 Implémentation

2.7.3.7.2.1 Présentation

Dans notre exemple, nous utilisons un Proxy pour manipuler un objet image. Le principal problème est que l’image peut ne pas avoir terminé son chargement et les méthodes appelées risquent de provoquer une exception. Le Proxy va donc mettre en attente les appels de méthodes, tant que l’image n’est pas chargée. Une fois celle ci téléchargée, les méthodes s’exécutent. L’utilisateur du Proxy subira un temps de latence lors du premier appel.

2.7.3.7.2.2 UML

Figure 46 UML implémentation Proxy

Page 38: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 38 / 109

2.7.3.7.2.3 Utilisation //normalement le client ne doit jamais écrire ce qui suit //c'est plutôt le rôle d'une factory ImageProxy proxy = new ImageProxy(new Image()); proxy.draw();

2.7.4 Patterns de comportement

2.7.4.1 Chain of Responsability ��������������������

2.7.4.1.1 Théorie

2.7.4.1.1.1 Présentation

Ce pattern propose de découpler l’émetteur d’une requête par rapport aux récepteurs potentiels. La requête traverse la chaîne des récepteurs jusqu'à ce qu’un objet la traite. Le couplage est fortement réduit et l’attribution des responsabilités est plus souple car des récepteurs peuvent être créés ou retirés dynamiquement. Il reste un risque cependant qu’aucun des récepteurs ne réponde à la requête.

2.7.4.1.1.2 UML

Figure 47 UML théorique Chain of Responsability

2.7.4.1.2 Implémentation

2.7.4.1.2.1 Présentation

Dans cet exemple, des employés vont prendre des décisions sur un projet. Chaque participant inscrit va recevoir le projet jusqu'à ce qu’il soit traité. On peut noter que la gestion de la liste (permutation, suppression, etc...) n’est pas aisée.

Correspondance diagramme théorique implémentation Subject Graphic Proxy ImageProxy RealSubject Image

Page 39: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 39 / 109

2.7.4.1.2.2 UML

Figure 48 UML implémentation Chain of Responsability

2.7.4.1.2.3 Utilisation Employee director = new Employee("Director"); Employee vicePresident = new Employee("vicePresident"); Employee president = new Employee("president"); director.setSuccessor(vicePresident); vicePresident.setSuccessor(president); //on peut faire démarrer la requête depuis n'importe quel employé Project project = new Project(1000, "Projet qui tue"); director.handleRequest(project);

2.7.4.2 Command ��������������������

2.7.4.2.1 Théorie

2.7.4.2.1.1 Présentation

Propose de supprimer le couplage entre l’objet qui invoque une opération et celui qui la réalise. Il fonctionne comme le motif Bridge à la différence près qu’il s’occupe des traitements et non des données. Au final, il permet de manipuler les traitements comme des objets et de pouvoir les défaire à la demande.

Correspondance diagramme théorique implémentation Handler Approver ConcreteHandler Employee

Page 40: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 40 / 109

2.7.4.2.1.2 UML

Figure 49 UML théorique Command

2.7.4.2.2 Implémentation

2.7.4.2.2.1 Présentation

Dans notre exemple, la classe User est le point d’entrée du service : elle enregistre les commandes et permet de les défaire sur plusieurs niveaux. La classe CalculatorCommand a la responsabilité pour faire et défaire une commande à l’aide du Calculator.

2.7.4.2.2.2 UML

Figure 50 UML implémentation Command

2.7.4.2.2.3 Utilisation User user = new User(); user.compute('+', 100); user.compute('-', 50); user.compute('*', 10); user.compute('/', 2); // annule 4 commandes

Correspondance diagramme théorique implémentation Invoker User Command Command ConcreteCommand CalculatorCommand Receiver Calculator

Page 41: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 41 / 109

user.undo(4); // refait 3 commandes user.redo(3);

2.7.4.3 Interpreter ��������������������

2.7.4.3.1 Théorie

2.7.4.3.1.1 Présentation

Définit une représentation pour une grammaire ainsi qu’un interpréteur pour cette grammaire. Il facilite la création, la modification et l’extension de cette grammaire. Pratique pour une grammaire simple, ce motif peut rapidement devenir complexe à maintenir à cause du nombre élevé de classes. Enfin, les performances générales de ce pattern sont souvent médiocres.

2.7.4.3.1.2 UML

Figure 51 UML théorique Interpreter

2.7.4.3.2 Implémentation

2.7.4.3.2.1 Présentation

L’exemple suivant propose un interpréteur pour des codes romains.

2.7.4.3.2.2 UML

Figure 52 UML implémentation Interpreter

Page 42: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 42 / 109

2.7.4.3.2.3 Utilisation String roman = "MCMXXVIII"; Context context = new Context(roman); ArrayList<Expression> tree = new ArrayList<Expression>(); tree.add(new ThousandExpression()); tree.add(new HundredExpression()); tree.add(new TenExpression()); tree.add(new OneExpression()); for (Expression expression : tree) { expression.interpret(context); } System.out.println(roman + "=" + context.getOutput());

2.7.4.4 Iterator ��������������������

2.7.4.4.1 Théorie

2.7.4.4.1.1 Présentation

Propose un accès séquentiel aux éléments d’un objet sans en exposer la structure interne. Ce pattern est bien connu en java puisqu’il permet d’exposer le contenu d’une collection via la méthode iterator(). Il offre une interface uniforme pour parcourir différentes structures.

2.7.4.4.1.2 UML

Figure 53 UML théorique Iterator

2.7.4.4.2 Implémentation

2.7.4.4.2.1 Présentation

Dans l’exemple suivant nous itérons une collection.

2.7.4.4.2.2 UML

Correspondance diagramme théorique implémentation Context Context AbstractExpression Expression TerminalExpression ThousandExpression,

HundredExpression, TenExpression, OneExpression

NonTerminalExpression

Page 43: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 43 / 109

Figure 54 UML implémentation Iterator

2.7.4.4.2.3 Utilisation //nous évitons d'utiliser java.util.Collection pour l'exemple String[] strings = {"a","z","e","r","t","y"}; Collection collection = new Collection(strings); //vide le contenu dans la sortie standard for (Iterator iter = collection.iterator(); iter.hasNext();) { String element = (String) iter.next(); System.out.println(element); }

2.7.4.5 Mediator ��������������������

2.7.4.5.1 Théorie

2.7.4.5.1.1 Présentation

Le Mediator réduit le couplage entre plusieurs classes en les dispensant de se faire explicitement référence : il prend en charge la manière dont les objets interagissent et de cette façon il formalise la coopération entre objets.

2.7.4.5.1.2 UML

Figure 55 UML théorique Mediator

Correspondance diagramme théorique implémentation Iterator Iterator ConcreteIterator RealIterator Agregate AbstractCollection ConcreteAgregate Collection

Page 44: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 44 / 109

2.7.4.5.2 Implémentation

2.7.4.5.2.1 Présentation

Dans notre exemple, nous utilisons un chat room pour le jeu Counter Strike. Chaque participant envoie des messages aux personnes enregistrées.

2.7.4.5.2.2 UML

Figure 56 UML implémentation Mediator

2.7.4.5.2.3 Utilisation CounterStrikeChatroom chatRoom = new CounterStrikeChatroom(); Participant killer = new Fighter("killer"); Participant grenadier = new Fighter("grenadier"); Participant mechant = new Fighter("mechant"); chatRoom.register(killer); chatRoom.register(grenadier); chatRoom.register(mechant); killer.send("yahooooo"); grenadier.send("gogogo"); mechant.send("no prisonner");

2.7.4.6 Memento ��������������������

2.7.4.6.1 Théorie

2.7.4.6.1.1 Présentation

Ce pattern propose de capturer et restaurer au besoin, l’état interne d’un objet et ce sans violer le principe d’encapsulation. La sauvegarde et la restauration peuvent s’effectuer sur une partie ou l’ensemble de l’objet.

Correspondance diagramme théorique implémentation Mediator AbstractChatroom Colleague Participant ConcreteMediator CounterStrikeChatRoom ConcreateColleague Fighter

Page 45: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 45 / 109

2.7.4.6.1.2 UML

Figure 57 UML théorique Memento

2.7.4.6.2 Implémentation

2.7.4.6.2.1 Présentation

Dans notre exemple, le Memento sauvegarde l’état du véhicule avec les options.

2.7.4.6.2.2 UML

Figure 58 UML implémentation Memento

2.7.4.6.2.3 Utilisation //configuration du véhicule Car car = new Car("Renault megane"); car.setAirConditionning(true); car.setSlidingRoof(true); //sauvegarde de la configuration CarMemory mem = new CarMemory(); mem.setMemento(car.createMemento()); //continue à modifier car.setAirConditionning(false); //restore l'état de l'objet car.restoreMemento(mem.getMemento());

2.7.4.7 Observer ��������������������

2.7.4.7.1 Théorie

2.7.4.7.1.1 Présentation

Définit une dépendance dynamique de type 1 à plusieurs, de façon telle que, quand un objet change d’état, tous ceux qui en dépendent en soient notifiés. Il permet d’isoler la relation du Sujet

Correspondance diagramme théorique implémentation Originator Car Memento CarMemento CareTaker CarMemory

Page 46: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 46 / 109

vers les observateurs : le sujet ne les connaît pas mais il peut les prévenir. Cependant il devient difficile de mesurer les interdépendances, voire les références circulaires.

2.7.4.7.1.2 UML

Figure 59 UML théorique Observer

2.7.4.7.2 Implémentation

2.7.4.7.2.1 Présentation

Dans notre exemple, 2 investisseurs écoutent le cours de l’action IBM. Chaque investisseur s’inscrit par le biais de la méthode attach(). A noter que Java propose un système équivalent basé sur java.util.Observer et java.util.Observable.

2.7.4.7.2.2 UML

Figure 60 UML implémentation Observer

2.7.4.7.2.3 Utilisation //crée les investisseurs Sorros s = new Sorros("Sorros"); BerkShire b = new BerkShire("Berkshire");

Correspondance diagramme théorique implémentation Subject Stock ConcreteSubject IBM Observer Investor ConcreteObserver Sorros, BerkShire

Page 47: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 47 / 109

//crée les Actions IBM et attache les investisseurs IBM ibm = new IBM("IBM", 120.00d); ibm.attach(s); ibm.attach(b); //le changement de prix de l'action notifie les investisseurs ibm.setPrice(120.10d); ibm.setPrice(130.20d);

2.7.4.8 State ��������������������

2.7.4.8.1 Théorie

2.7.4.8.1.1 Présentation

Le motif State permet à un objet de modifier son comportement quand son état change, donnant ainsi l’impression que l’objet lui-même a été modifié. Ce pattern bien que peu répandu est extrêmement puissant ; il évite les « switch » ou blocs d’instruction « if » à n’en plus finir. Cependant la multiplication des états peut rendre la lecture complexe. Enfin le choix du positionnement des états successifs est quelque peu éparpillé : il peut être souhaitable d’avoir une classe ayant cette responsabilité.

2.7.4.8.1.2 UML

Figure 61 UML théorique State

2.7.4.8.2 Implémentation

2.7.4.8.2.1 Présentation

Dans notre exemple, nous simulons l’état d’une banque. Une banque peut avoir aucune ou plusieurs agences. Une banque a 3 états : UnInitialize (la banque n’a pas de nom), Initialize (la banque a un nom mais pas d’agence) et HaveAgency (la banque a un nom et des agences).

2.7.4.8.2.2 UML

Page 48: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 48 / 109

Figure 62 UML implémentation State

2.7.4.8.2.3 Utilisation Bank bank = new Bank(null); // UnInitializeState System.out.println(bank.getStateName()); bank.setName("BNP"); // InitializeState System.out.println(bank.getStateName()); Agency rennes = new Agency("Rennes"); Agency tours = new Agency("Tours"); bank.addAgency(rennes); bank.addAgency(tours); // HaveAgencyState System.out.println(bank.getStateName()); bank.removeAgency(rennes); bank.removeAgency(tours); // InitializeState System.out.println(bank.getStateName());

2.7.4.9 Strategy ��������������������

2.7.4.9.1 Théorie

2.7.4.9.1.1 Présentation

Le motif Strategy définie des familles d’algorithmes (répondant à la même interface), les rendant interchangeables dynamiquement. Ils peuvent ainsi évoluer indépendamment des clients qui les utilisent. Il fournit une alternative par l’héritage mais on peut toutefois craindre une prolifération des classes.

Correspondance diagramme théorique implémentation Context State BankState ConcreteState UnInitializeState,

InitializeState, HaveAgencyState

Page 49: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 49 / 109

2.7.4.9.1.2 UML

Figure 63 UML théorique Strategy

2.7.4.9.2 Implémentation

2.7.4.9.2.1 Présentation

Notre exemple permet de compacter des fichiers en utilisant des algorithmes de compression différents.

2.7.4.9.2.2 UML

Figure 64 UML implémentation Strategy

2.7.4.9.2.3 Utilisation //ajoute le fichier toto.txt dans le fichier zip toto.zip InputStream in = new FileInputStream("c:\\toto.txt"); OutputStream out = new FileOutputStream("c:\\toto.zip"); InflateContext iContext = new InflateContext("toto.txt", in, out); iContext.inflate();

Correspondance diagramme théorique implémentation Strategy InflateStrategy Context InflateContext ConcreteStrategy ZipStrategy,

TarGunzipStrategy, RarStrategy

Page 50: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 50 / 109

2.7.4.10 Template Method ��������������������

2.7.4.10.1 Théorie

2.7.4.10.1.1 Présentation

Définit dans une opération, le squelette d’un algorithme avec ses parties invariantes, en déléguant certaines étapes à des sous classes. Les sous-classes définissent certaines parties de l’algorithme. Ce motif est, dans l’esprit, proche de la Factory Method mais se limite aux traitements et non aux données. La classe représente l’algorithme, et les méthodes les éléments variants de cet algorithme.

2.7.4.10.1.2 UML

Figure 65 UML théorique Template Method

2.7.4.10.2 Implémentation

2.7.4.10.2.1 Présentation

Dans notre exemple, une partie de l’algorithme de choix de permutation d’élément pour un tri est externalisé.

2.7.4.10.2.2 UML

Figure 66 UML implémentation Template Method

2.7.4.10.2.3 Utilisation

Sorter sorter = new DescSorter(); int[] ints = {1,3,2};

Correspondance diagramme théorique implémentation TemplateContext Sorter Implementor DescSorter

Page 51: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 51 / 109

for (int i = 0; i < ints.length; i++) { System.out.print(ints[i]); } sorter.sort(ints); System.out.println(); for (int i = 0; i < ints.length; i++) { System.out.print(ints[i]); }

2.7.4.11 Visitor ��������������������

2.7.4.11.1 Théorie

2.7.4.11.1.1 Présentation

Le motif Visitor permet à une classe externe de pouvoir parcourir les données d’un objet. Il nous permet de définir de nouveaux traitements sur les données sans en affecter la classe propriétaire. On peut ainsi en ajouter sans introduire de variation sur la classe portant les données.

2.7.4.11.1.2 UML

Figure 67 UML théorique Visitor

2.7.4.11.2 Implémentation

2.7.4.11.2.1 Présentation

Dans notre exemple, nous utiliserons un Visitor pour parcourir un objet Bank et en extraire une représentation textuelle (xml et csv). Chaque classe susceptible d’être visitée doit contenir une méthode « accept(Visitor v) ». Le code de cette méthode exécute en retour l’instruction « v.visit(this) ».

Figure 68 UML implémentation Visitor

Page 52: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 52 / 109

2.7.4.11.2.2 UML

2.7.4.11.2.3 Utilisation //constitue les données Bank bank = new Bank("BNP"); Agency rennes = new Agency("rennes"); Agency tours = new Agency("tours"); bank.addAgency(rennes); bank.addAgency(tours); //génére la version xml des données BankVisitor xml = new XMLBankVisitor(); bank.accept(xml); //génére la version csv des données BankVisitor csv = new CSVBankVisitor(); bank.accept(csv);

2.8 Pattern JEE On s’intéressera ici à l’utilisation de Patterns les plus communs au sein de Java Enterprise Edition afin d’améliorer les performances, la fiabilité et la cohérence.

2.8.1 Business Delegate 2.8.1.1 Problème

Au travers des appels directs au métier, le client n'optimise pas ou peu les accès aux objets à travers le réseau. Cette non optimisation peut coûter cher en termes de performance. Bien que proche du pattern Session Facade, le Business Delegate est différent : en effet il n'est qu'une coquille vide qui se borne à router les appels vers l'objet distant : il ne fait pas d’agrégation de services.

2.8.1.2 Solution Le pattern Business Delegate à donc pour objectif de réduire le couplage entre le client et les services métier. Il en cache l'implémentation technologique. Il agit comme une abstraction de la partie métier. Ceci à plusieurs avantages : � Plusieurs clients de natures différentes peuvent accéder au métier. � Les services métier peuvent évoluer facilement en fonction des besoins. � Pour des besoins de performances, le Business Delegate peut exploiter des caches. � Du point de vue du client, la localisation des objets métiers est complètement transparente. � La reprise sur incident est gérée de manière centralisée (par exemple, si un service métier

distant ne répond pas, le Business Delegate peut décider d'attendre la disponibilité ou de gérer une exception)

� Les interfaces proposées au client sont plus simples et uniformes.

Correspondance diagramme théorique implémentation Visitor BankVisitor ConcreteVisitor XMLBankVisitor,

CSVBankVisitor Element Element ConcreteElement Bank

Page 53: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 53 / 109

Figure 69 : Description du pattern Business Delegate

2.8.1.3 Implémentation

Ci-dessous un exemple de code appliquant le pattern Business Delegate à un EJB session. Le constructeur par défaut instancie un objet distant. Dans le reste de la classe on retrouve toutes les méthodes propres à l'objet distant. Si c'est un EJB session stateful, l'astuce est d'utiliser la méthode finalise() pour effectuer un ejb.remove() afin de libérer la ressource. public class MySessionBusinessDelegate { // référence à un session distant private MySession session; public MySessionBusinessDelegate () throws ResourceException { try { MySessionHome home =(MySessionHome) ServiceLocator.getInstance(). getRemoteHome("MySession", MySessionHome.class;); session = home.create(); } catch (ServiceLocatorException ex) { throw new ResourceException(...); } catch (CreateException ex) { throw new ResourceException(...); } catch (RemoteException ex) { throw new ResourceException(...); } } //exemple de méthode distante public ResourceTO getResourceDetails() throws ResourceException { try { return session.getResourceDetails(); } catch (RemoteException ex) { throw new ResourceException(...); } } ... //pour les ejb stateful protected void finalize() throws Throwable { home.remove(); } }

2.8.2 Service Locator 2.8.2.1 Problème

Les spécifications J2EE offrent la possibilité d'interagir avec des composants orientés services tels que les EJB, les composants JMS, etc. Pour les utiliser ils doivent soit être localisés (au travers d'une opération de "lookup"), soit créés. Pour rechercher ces composants, les applications J2EE utilisent JNDI pour pouvoir accéder à différents services ou ressources. Les serveurs J2EE enregistrent ces ressources auprès d’un

Page 54: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 54 / 109

annuaire JNDI de manière à ce que les clients puissent accéder à ces ressources depuis n’importe quel point du réseau en invoquant le service d’annuaire. Ces ressources et ces services peuvent être :

� Des objets EJBHome � Des sources de données � Des ConnectionFactory JMS � Des topics ou queues JMS � etc …

Ainsi par exemple, les clients d’EJB doivent initialement récupérer l’objet EJBHome depuis l’annuaire JNDI pour pouvoir invoquer les méthodes de l’EJB. Les clients JMS doivent récupérer la ConnectionFactory, les topics et les queues pour manipuler les messages. Les clients JDBC doivent récupérer l’objet DataSource pour obtenir une connexion à la base de données. Tous ces clients doivent donc utiliser le protocole JNDI à chaque fois qu’ils ont besoin de l’une de ces ressources. Or, le lookup JNDI est un processus coûteux car le client doit obtenir une connexion réseau vers le service d’annuaire.

2.8.2.2 Solution Le pattern Service Locator donne la responsabilité de la localisation des ressources à un objet dédié. Le Service Locator peut participer à la gestion du partage de charge. En effet, il peut être développé de manière à chercher les services sur des machines physiquement différentes en cas de surcharge. Il peut servir à étendre les services de nommage JNDI en utilisant des caches et ce afin de limiter les appels redondants à l’annuaire. Seule la première invocation est coûteuse : les fois suivantes, c’est le cache qui est directement utilisé au lieu de faire un appel à l’annuaire. Si on utilise ce pattern, le client n’a donc plus à appeler les services JNDI directement, il effectue ces appels auprès du ServiceLocator qui se charge soit de faire le lookup JNDI ou bien de renvoyer les objets depuis son cache ou alors de créer directement les objets. Le client se trouve ainsi découplé du système d’obtention de l’objet. Par exemple l’obtention d’un objet de service HiveMind nécessite une Registry. Dans notre cas, seul le ServiceLocator connaît la Registry : si nous souhaitons changer de containeur d’objets, la variation se limitera uniquement au ServiceLocator.

Figure 70 : Le Service Locator devient le point d’entrée pour obtenir des ressources

Page 55: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 55 / 109

2.8.2.3 Implémentation

Ci-dessous un exemple possible d'implémentation du pattern Service Locator. La classe est un singleton : c'est-à-dire qu'il n'existe qu'une seule instance dans une JVM. Une Map conserve les homes déjà trouvées. L'utilisation s'effectue via la méthode getInstance() puis une des méthodes dédiées à la recherche d'objets (getLocalHome(…), getRemoteHome(…)). public class ServiceLocator { private InitialContext initialContext; private Map cache; private static ServiceLocator _instance; static { try { _instance = new ServiceLocator(); } catch (ServiceLocatorException se) { System.err.println(se); se.printStackTrace(System.err); } } private ServiceLocator() throws ServiceLocatorException { try { initialContext = new InitialContext(); cache = Collections.synchronizedMap(new HashMap()); } catch (NamingException ne) { throw new ServiceLocatorException(ne); } catch (Exception e) { throw new ServiceLocatorException(e); } } static public ServiceLocator getInstance() { return _instance; } public EJBLocalHome getLocalHome(String jndiHomeName) throws ServiceLocatorException { EJBLocalHome localHome = null; try { if (cache.containsKey(jndiHomeName)) { localHome = (EJBLocalHome)cache.get(jndiHomeName); } else { localHome = (EJBLocalHome)initialContext.lookup(jndiHomeName); cache.put(jndiHomeName, localHome); } } catch (NamingException nex) { throw new ServiceLocatorException(nex); } catch (Exception ex) { throw new ServiceLocatorException(ex); } return localHome; } public EJBHome getRemoteHome(String jndiHomeName, Class homeClassName) throws ServiceLocatorException { EJBHome remoteHome = null; try { if (cache.containsKey(jndiHomeName)) { remoteHome = (EJBHome) cache.get(jndiHomeName); } else { Object objref = initialContext.lookup(jndiHomeName); Object obj = PortableRemoteObject. narrow(objref, homeClassName); remoteHome = (EJBHome)obj; cache.put(jndiHomeName, remoteHome); } } catch (NamingException nex) { throw new ServiceLocatorException(nex); } catch (Exception ex) { throw new ServiceLocatorException(ex); } return remoteHome; } // implémenter ici les autres méthodes de lookup (datasource, hibernate factory,…) . . . }

Page 56: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 56 / 109

2.8.3 Session Facade 2.8.3.1 Problème

Les EJB encapsulent la logique métier et les données métier. Pour les utiliser, il faut connaître les interfaces et être au fait de la complexité d'interaction entre tous ces objets. Les clients d’EJB (applications swing, servlets, jsps, etc.) peuvent accéder à des EJB distants de manière directe. A chaque appel d’un EJB, un transfert réseau est effectué entre le client et l’EJB. Pour une requête d’un client, il arrive souvent que tout un ensemble d’EJB soit appelé. Si les appels se font directement il en résulte un grand nombre d’appels à travers le protocole RMI/RMI-IIOP. Ces transferts réseau à répétition peuvent rapidement devenir coûteux et dégrader directement les performances générales d’une application.

Figure 71 : Nombre élévé de requêtes à travers le réseau

2.8.3.2 Solution Le pattern Session Facade propose d’utiliser un EJB Session en tant que façade pour : 1. réduire le trafic réseau en :

� encapsulant tous les appels afin finalement de ne faire qu’un appel distant. � gérant plus finement la granularité des données (seules les informations nécessaires sont

fournies au client du Session Facade). 2. découpler le client du métier en :

� cachant la complexité d'interaction entre les différents composants métier. � évitant d'exposer les objets métier au client. � proposant un service uniforme permettant d'abstraire l'implémentation du métier

(améliore la granularité des traitements). Par exemple, au lieu d'effectuer un débit sur un compte puis un crédit sur un autre, la façade peut proposer d'effectuer un virement.

3. centraliser la sécurité. 4. centraliser la gestion des transactions.

Page 57: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 57 / 109

Figure 72 : Session Facade

Ici, le client ne fait qu’un seul appel à travers le réseau vers la Session Facade qui va agréger le résultat des différents appels. De plus, dans ce cas, comme les EJB ne sont accédés que par la façade, ils peuvent être déployés comme locaux ce qui optimise encore les appels entre la façade et les EJB. Au total on réduit donc le trafic réseau, le nombre d’appels RMI et le temps d’appel des EJB. Ne pas confondre le pattern Service Facade avec le pattern Business Delegate. Le premier contient du code métier alors que le second n'est qu'une coquille vide qui redirige les appels vers l'objet distant. D'ailleurs un Service Facade ne devrait être utilisé que via son Business Delegate.

2.8.3.3 Implémentation

Le Session Facade est la plupart du temps implémenté via un Session Bean Stateless. Celui-ci contient la logique métier et sait quels services ou objets métiers il devra interroger/utiliser pour effectuer son travail.

2.8.4 Message Facade / Service Activator 2.8.4.1 Problème

Les méthodes des EJB Session et Entité s’exécutent de manière synchrone. Ceci signifie que l’appelant d’une méthode doit attendre qu’une valeur lui soit retournée. Dans certaines situations telles qu’envoyer des centaines de mails ou lancer un Process en batch, le client n’a pas à se soucier d’une quelconque valeur de retour. Conserver un mode de fonctionnement synchrone pour ce type de besoin se révèle très coûteux, car il m’est en attente l’ensemble de l’application et les utilisateurs associés à la commande.

Figure 73 : Exécution synchrone

Page 58: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 58 / 109

2.8.4.2 Solution Le pattern Message Facade permet d'éviter qu’un client n’ait à attendre une valeur de retour. Pour cela, il est préférable d’utiliser des EJB Message asynchrones (Message Driven Beans). Dans ce cas, le client peut continuer le cours de son exécution après avoir envoyé son message. La communication est alors asynchrone et les traitements peuvent être parallélisés.

Figure 74 : Exécution asynchrone

2.8.4.3 Implémentation

Plusieurs solutions existent. La première est de proposer un frontal répliquant toutes les méthodes du Business Delegate que l'on souhaite atteindre et qui utilisera JMS. Un EJB MDB en attente sur la queue JMS appellera directement le Business Delegate. Deuxième solution : proposer un frontal générique avec une méthode unique. Les paramètres de ce frontal seront la classe du Business Delegate, le nom de la méthode, un tableau d'objets contenant les paramètres de la méthode. La première solution est plus claire et simple à utiliser mais moins évolutive, la seconde, plus complexe à utiliser, permet d'invoquer n'importe quel objet de manière asynchrone (attention cependant : en cas d’échec, le processus appelant ne pourra être averti).

2.8.5 Transfer Object / Value Object 2.8.5.1 Problème

Quand un client invoque une méthode distante, cette invocation est transformée en appel réseau, puis résolue côté serveur et enfin la réponse est elle aussi envoyée à travers un appel réseau. Les performances peuvent rapidement se dégrader si l’ensemble des attributs d’un objet distant sont récupérés de la sorte. En effet, les appels aux méthodes au travers du réseau mettent en œuvre tout un processus de sérialisation/dé sérialisation très coûteux. Par exemple, si on souhaite récupérer des informations sur un objet, faire des appels consécutifs tels que ceux-ci peut vite devenir très consommateur :

Page 59: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 59 / 109

Figure 75 : Granularité fine des appels de méthodes

2.8.5.2 Solution Le pattern Transfer Object permet d'éviter les appels répétitifs à travers le réseau en remplaçant les méthodes invoquées en un objet comprenant tous les attributs désirés. On fera alors uniquement transiter par le réseau l'objet contenant seulement les informations désirées.

Figure 76 : Granularité plus forte : une seule méthode invoquée

Dans ce cas, il n’y a qu’un seul appel à travers le réseau au lieu de plusieurs auparavant. L’objet lui-même est transféré à travers le réseau au lieu de faire transiter ses attributs un à un.

2.8.5.3 Implémentation

L'implémentation de ce pattern est simple et sans surprise. Chaque méthode de session retourne un Value Object. Les Values Objects sont eux de simples porteurs d'information sérialisables. Il peut être intéressant d'implémenter Comparable afin de permettre par exemple des tris. En effet les Values Objects étant la plupart du temps destinés à être affiché, il convient de pouvoir facilement les ordonner. Exemple de Value Object : public class AccountVO implements Serializable, Comparable { private String login; private String password; public AccountVO() { login = null; password = null; }

Page 60: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 60 / 109

public void setLogin(String login) { this.login = login.toLowerCase(); } public String getLogin() { return login; } public void setPassword(String password){ this.password = password; } public String getPassword() { return password; } public int compareTo(Object object) { AccountVO accountValue = (AccountVO)object; return getLogin().compareToIgnoreCase(accountValue.getLogin()); } public boolean equals(Object object) { if(!(object instanceof AccountVO)) { return false; } else { AccountVO accountValue = (AccountVO)object; return getID().equals(accountValue.getID()); } } public String toString() { return login + "/" + password; } … }

2.8.6 Transfer Object Assembler / Value Object Assembler

2.8.6.1 Problème

Pour une seule requête, un client peut avoir besoin d’accéder à de multiples composants serveur tels que des EJB Session et Entité. Dans cette situation, le client invoque de multiples composants à travers le réseau ce qui à un impact direct sur les performances. De plus, dans la plupart des cas, le client à besoin de reconstruire une structure à partir des brides de données qu'il a pu récupérer. Nous nous retrouvons dans la même situation que le Transfert Object mais à niveau de granularité plus forte.

Figure 77 La récupération des différentes brides d'informations encombre le réseau

2.8.6.2 Solution Pour répondre à ce problème, le pattern Transfer Object Assembler propose la construction d'un objet composite. Il assemble les différents Transfer Object récupérés auprès des différents composants métier. Il renvoi l'objet composite au client après son assemblage et ne nécessite qu’un appel réseau.

Page 61: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 61 / 109

Figure 78 Le TransfertObjectComposite limite les appels

2.8.6.3 Implémentation

Proposer cette fonctionnalité au travers de Session Facade qui retournera, non pas un objet mais un graphe de Value Objets constitués par assemblage de plusieurs Values Objects.

2.8.7 Value List Handler 2.8.7.1 Problème

Les applications J2EE implémentent généralement des fonctionnalités de recherche qui ont à parcourir une grande quantité de données. Si une application retourne énormément de données en une seule fois au client, celui-ci devra attendre un long moment avant d’afficher une seule donnée. D'autre part, le client effectue souvent d'autres recherches sans parcourir l'intégralité des données qu'il a déjà reçues ce qui fait que les données ont transitées pour rien à travers le réseau. Cela a un impact direct sur les performances du serveur (réseau et mémoire). Si l'application utilise un EJB Entity pour les données, les performances peuvent être catastrophiques.

Figure 79 L'utilisation du findAll sur un Entity dégrade fortement les performances

2.8.7.2 Solution Le pattern Value List Handler permet de réduire ces fortes charges. Il accède directement à un DAO plus performant qu'un EJB. Ce DAO lui renvoi une collection de Transfer Objects. Le Value List Handler est une alternative au "finders" EJB pour des grosses requêtes.

Page 62: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 62 / 109

Figure 80 Utilisation d'une Dao avec un mode page par page

2.8.7.3 Implémentation

Le Value List Handler accède directement aux données via une DAO (EJB Entity à proscrire). Il permet de contrôler la recherche, de cacher les données si nécessaires. Il peut aussi retourner au client un sous ensemble des données correspondant à ce qu'il a demandé (affichage page par page).

2.8.8 Data Access Object 2.8.8.1 Problème

Beaucoup d'applications ont besoin d'exploiter un système persistant de données. Pour cela, il peut y avoir beaucoup de mécanismes différents mais surtout beaucoup de différences entre les API utilisées pour accéder à ces différents systèmes de persistance. Par exemple les données peuvent se trouver dans une base de données relationnelle, un annuaire LDAP, des documents XML, un système propriétaire, etc. Ces différences fondamentales entre les systèmes de stockage rendent le code de l'application très dépendant du code d'accès au système de persistance.

2.8.8.2 Solution Utiliser le pattern Data Access OBject (DAO) pour abstraire et encapsuler tous les accès aux différentes sources de données. C'est la DAO qui gère la connexion et l'obtention des données de la source. Le DAO n'expose que les interfaces simples permettant de manipuler les données en cachant les mécanismes complexes liés aux différents systèmes de stockage. Si le système de stockage évolue, c'est le DAO qui devra s'adapter tout en proposant toujours les mêmes interfaces au client. Il est à noter que la DAO n’offre pas les mécanismes évolués (cache de premier niveau et de second niveau, unicité d’objets, lazy loading) que propose des ORM tels que Hibernate ou certaines implémentations de JDO.

2.8.8.3 Implémentation

Exemple typique d’une Dao supportant plusieurs systèmes d’ORM. La Factory prend la responsabilité de créer la bonne implémentation (HibernateDao ou JpoxDao).

Page 63: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 63 / 109

Figure 81 Dao proposant plusieurs implémentations

2.8.9 Conclusion � Utiliser le Pattern ServiceLocator pour améliorer les performances d'accès aux services

distant (réduire le coût du lookup JNDI) et abstraire la technique d’obtention de la référence du service.

� Utiliser le Pattern SessionFacade pour empêcher les accès directs aux EJB et optimiser les appels aux objets à travers le réseau.

� Utiliser le Pattern MessageFacade/ServiceActivator pour l’exécution des méthodes au temps d’exécution important et ne nécessitant pas de valeur de retour.

� Utiliser le Pattern Transfer Object/Value Object pour diminuer le nombre d’invocations de méthodes à travers le réseau.

� Utiliser le Pattern TransferObjectAssembler/ValueObjectAssembler pour agréger les informations dans le but d’éviter des appels réseau multiples.

� Utiliser le Pattern ValueListHandler pour envoyer en mode « page par page », les données au client.

� Utiliser le pattern Data Access Object pour uniformiser la méthode d’accès à un ou plusieurs espaces de persistance même différent technologiquement.

Page 64: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 64 / 109

3 ANTI-PATTERNS Ce chapitre s’attarde sur les erreurs de conception répandues et habituelles : les anti-patterns. On les appels ainsi car ils apparaissent durant la phase de conception. Un anti-pattern est considéré comme tel lorsqu’il produit plus de problèmes qu’il n’en résout.

3.1 Code source 3.1.1 Code court

Certains codeurs estiment (à ce niveau ça devient de la compétition) que plus le code est compact, meilleur sera le programme. Le code est compact, sans espace (ou le strict minimum) et peu lisible (voir illisible). C’est une pratique répandue et sans grand intérêt : oubliez la, faites simple et lisible.

3.1.2 Optimisation Sous prétexte d’optimiser le code, un développeur peut rendre un programme totalement illisible. Dans la plupart des cas préférez la lisibilité à l’optimisation. Si l’optimisation fait partie des contraintes du système, commentez copieusement votre code.

3.1.3 Copier/Coller Certainement responsable d’une grande partie des bugs applicatifs à ce jour. Problématique car le contexte source et le contexte cible ne sont pas toujours les mêmes. Cette méthode va d’ailleurs contre un principe essentiel de la conception objet : la réutilisation. A éviter donc.

3.1.4 Variables Les variables sont privées, des variables publics non justifiées sont plutôt le signe d’une mauvaise conception : on y accède via des getters et de setters uniquement. Dans le même esprit les variables globales dans des « Pure Abstraction (cf. GRASP)» ont rarement de raison d’être : elles maintiennent inutilement des références et alourdissent le système.

3.1.5 Concaténation de chaînes de caractères Malgré ce que l’on pense, le code suivant n’est pas mauvais ; il est même optimum : String s = "In memory-constrained situations" + ", you should pay close attention" + " to the" + " proliferation " + "of"+ " strings"; En effet, le compilateur va optimiser l’expression en la transformant en une seule chaîne ou utiliser un StringBuffer. Par contre celui-ci est plus coûteux car le compilateur ne sait pas l’optimiser. String s = "In memory-constrained situations"; s+= ", you should pay close attention"; s+= " to the"; s+= " proliferation "; s+= "of"; s+= " strings";

3.1.6 Cohérence du système Le bloc finally est sous employé dans le code des méthodes. Pourtant il permet, même en cas d’erreurs, de libérer proprement les ressources, laissant le système dans un état cohérent.

Page 65: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 65 / 109

Dans cet exemple si la boucle while génère une erreur, les fermetures de resultset, statement et connection seront compromises.

public void execute() throws IOException, DataException { try { // retrieve data from the database Statement statement = connection.createStatement(); result = statement.executeQuery("SELECT subject, author, board from posts"); while (result.next()) { subject.addElement(result.getString(SUBJECT_COLUMN)); author.addElement(result.getString(AUTHOR_COLUMN)); board.addElement(result.getString(BOARD_COLUMN)); } result.close(); statement.close(); connection.close(); } catch (Throwable theException) { theException.printStackTrace(); } }

Il est préférable d’utiliser le bloc finally pour effectuer l’opération de libération des ressources.

public void execute() throws IOException, DataException { try { // retrieve data from the database Statement statement = connection.createStatement(); result = statement.executeQuery("SELECT subject, author, board from posts"); while (result.next()) { subject.addElement(result.getString(SUBJECT_COLUMN)); author.addElement(result.getString(AUTHOR_COLUMN)); board.addElement(result.getString(BOARD_COLUMN)); } } catch (Throwable theException) { theException.printStackTrace(); } finally { result.close(); statement.close(); connection.close(); } }

3.1.7 Typage fort/faible Lorsque vous déclarez et utilisez un type défini, il existe souvent une interface plus abstraite pour le manipuler, facilitant les modifications sans remettre en cause fondamentalement l’algorithme. Exemple avec la classe ArrayList qui hérite de List. Le code suivant :

private ArrayList list = new ArrayList();

S’écrira plutôt :

private List list = new ArrayList();

En effet si nous souhaitons utiliser une LinkedList, le code de la classe qui utilise la variable « list » reste inchangé, seule la déclaration change.

private List list = new LinkedList();

Page 66: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 66 / 109

Préférez donc si possible un typage faible pour la déclaration de vos variables.

3.1.8 Synchronized Le mot clé synchronized permet de définir des sections critiques thread safe. Il est souvent utilisé avec la déclaration de la méthode d’une classe

public synchronized void theMethod() { }

Cette écriture est équivalente à

public void theMethod() { synchronized (this) { … } }

Plutôt que d’utiliser synchronized au niveau de la méthode, il est préférable de l’utiliser uniquement sur la section de la méthode qui nécessite un accès thread safe. D’ailleurs, rien n’oblige le développeur à utiliser this comme objet de synchronisation. On peut par exemple utiliser un singleton si l’on souhaite synchroniser plusieurs méthodes disséminées dans plusieurs objets. Le principal reproche que l’on puisse faire à la technique du synchronized, c’est le manque de granularité. En effet, si un système multi thread tente de lire et de modifier une variable en même temps, le verrou sera le même pour la lecture ainsi que pour l’écriture, provoquant un goulot d’étranglement. Un verrou de lecture est différent d’un verrou d’écriture. Le verrou d’écriture doit attendre que les verrous de lecture soient tous levés pour être posé. Plusieurs verrous de lecture peuvent être posés en même temps alors qu’un seul verrou d’écriture ne peut être posé à un instant donné. Ci-dessous une des nombreuses implémentations existantes.

class RWLock { private int givenLocks; private int waitingWriters; private int waitingReaders; private Object mutex; public RWLock(Object mutex) { this.mutex = mutex; } public void getReadLock() throws InterruptedException { synchronized (mutex) { while ((givenLocks == -1) || (waitingWriters != 0)) { mutex.wait(); } givenLocks++; } }

Page 67: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 67 / 109

public void getWriteLock() throws InterruptedException { synchronized (mutex) { waitingWriters++; while (givenLocks != 0) { mutex.wait(); } waitingWriters--; givenLocks = -1; } } public void releaseLock() { synchronized (mutex) { if (givenLocks == 0) return; if (givenLocks == -1) givenLocks = 0; else givenLocks--; mutex.notifyAll(); } } }

3.1.9 Documentation Nombre de codes sources sont encore livrés sans documentation, pourtant le besoin en documentation technique sur un projet n’est plus à démontrer. Le souci principal est de savoir quand la rédiger. Nécessairement pas au début du projet, car la plupart du temps, le fonctionnel n’est pas stable et les commentaires bons auparavant, deviennent rapidement caduques. La maintenance de la documentation devient fastidieuse et donne au développeur l’impression de travailler pour rien. Il est préférable de le faire à la fin de chaque itération, c'est-à-dire chaque fois qu’un module est considéré comme stable et pratiquement finalisé.

3.1.10 Exception Les exceptions doivent refléter l’erreur levée. Trouvez la bonne granularité : une interface graphique se moque de savoir que c’est la base de données qui ne répond pas. Elle a juste besoin de savoir que le service est momentanément interrompu. Le niveau de précision (la granularité) de l’exception doit être adapté à la position du module dans une architecture en couche. Au passage n’hésitez pas à enrichir la classe d’Exception de données complémentaires (Objet Client, Facture, etc.) pour que les messages remontés soient plus clairs et que la recherche du bug soient facilitée.

3.1.11 Paramètres de méthodes Chaque objet à la responsabilité de ses données mais aussi de leurs cohérences (cf. pattern Expert du GRASP). Il convient donc de tester les valeurs des paramètres reçus dans une méthode ou un constructeur afin de ne pas introduire :

� D’incohérences dans les données de la classe par une affectation. � D’incohérences dans les traitements par son utilisation. � De levée d’Exception si une valeur nulle est utilisée.

Dans l’exemple ci-dessous si param1 est à null la méthode lèvera un « NullPointerException »

public void theMethod(String param1, String param2) { //si param1 est null, il y a levé d’Exception

Page 68: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 68 / 109

if(param1.compareTo("str")==0) { //... } }

Il est préférable d’écrire

public void theMethod(String param1, String param2) { if(param1 == null) { throw new IllegalArgumentException("param1 can't be null"); } if(param2 == null) { throw new IllegalArgumentException("param2 can't be null"); } //... if(param1.compareTo("str")==0) { //... } }

Le client de la méthode va recevoir une exception de type « IllegalArgumentException » avec un message représentatif de l’erreur.

3.2 Principes 3.2.1 Ressources

Il arrive que certaines ressources (stream, connexion, …) soient initialisées à un endroit de l’application et libérées à un autre. Le contrôle de la réservation et de la libération de la ressource est difficile car disséminé. Placez l’acquisition et la libération des ressources si possible dans la même méthode. En effet, si le programme lève une exception, rien ne certifie que la ou les ressources seront libérées.

3.2.2 Héritage Il n’est pas rare de découvrir des arbres d’héritages à plusieurs niveaux. Au-delà de 3, dites-vous que c’est trop et cela nuit à la lisibilité et à la compréhension du code. Préférez la délégation à l’héritage : on n’expose que le nécessaire et par-là même on simplifie les objets. Souvenez-vous : l’héritage ne sert pas à factoriser du code (cf. pattern Liskov substitution du GRAP).

Figure 82 Héritage trop complexe !!

Page 69: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 69 / 109

3.2.3 Habitude L’ennemi du développeur. L’habitude d’utiliser le même algorithme quelque soit le cas d’utilisation (tri bulle sur des millions de lignes), utiliser la même classe pour faire des choses différentes (Utiliser un Vector alors qu’il faudrait un Stack ou un ArrayList).

3.2.4 Simplicité Tout logiciel est appelé à évoluer. Le risque serait de développer beaucoup de point d’ouverture (cf. Open/Close principle) dans l’espoir que l’application évoluera sur ces points d’ouverture. C’est une perte de temps : il est préférable de faire un refactoring de la partie concernée en temps voulu. Evidemment, ce principe doit être accompagné d’un couplage faible des modules. Finalement, choisissez toujours la simplicité, ne codez que ce qui est nécessaire car tout code supplémentaire devra quand même être maintenu, documenté et testé sans ne jamais servir un jour.

3.3 Mémoire

3.3.1 Durée de vie des objets Là où C++ doit explicitement allouer et désallouer les objets, java offre le garbage collector. C'est-à-dire qu’un objet est libéré lorsqu’il n’est plus référencé par aucun autre objet. Ce concept simple est pourtant pernicieux : en effet sur un modèle objet évènementiel où les références sont en dépendance inverse (Observer/Observable) elles ne sont jamais relâchées. Dans ce cas de figure, plusieurs solutions : 1. Utilisez des « Weak references ». 2. Relâchez explicitement le listener. 3. Jouez sur la durée de vie de la poigné sur l’objet. Ne pas profiter de l’existence du garbage collector pour instancier beaucoup d’objets sans se soucier de l’occupation mémoire. La machine virtuelle est obligée de consommer une partie des ressources pour libérer les objets, donc moins vous instanciez d’objets, moins cela est coûteux en mémoire mais aussi en temps. Parfois positionner à « null » en cours de traitement un attribut de classe peut être une optimisation si celui-ci pointe vers un objet gourmand (globalement le système est plus léger).

3.3.2 Allocation mémoire Si votre application nécessite l’utilisation de beaucoup d’objets, ne faites pas de « new » systématique. Utiliser plutôt un pool d’objets (beaucoup d’implémentations existent), vous économiserez le temps du garbage collector pour allouer/désallouer les espaces mémoires.

3.4 Conception 3.4.1 The God Class

La « classe divine » (ou blob) est un anti-pattern facile à reconnaître par sa forme. Il est souvent composé d’une classe principale de traitement. Sur le diagramme de classe de l’application il est facile à repérer puisque cette classe est la plus grande (en nombre de méthodes) et référence quasiment toutes les autres. Le blob contient la majorité des processus et utilise une multitude de classes qui ne servent qu’à porter les données.

Page 70: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 70 / 109

La « classe divine » va à l’encontre de la conception objet. La classe n’est pas réutilisable, complexe à maintenir et difficile à tester (classe peu cohésive). De plus l’utilisation de cette classe risque d’entraîner un coût mémoire important dès qu’on l’utilise, pour peu que l’initialisation de toutes les variables soit dans le constructeur. Ce type de classe peut malgré tout être toléré si elle n’est qu’un point d’entrée vers un système central ou distant. Pour corriger ce problème, la classe doit être éclatée en plusieurs autres plus cohésives. La répartition des méthodes doit être faite en respectant une certaine cohésion technique ou fonctionnelle (cf. High cohesion GRASP).

3.4.2 Swiss Army Knife L’anti-pattern couteau suisse est le résultat d’une interface ou d’une classe dont le concepteur souhaite répondre à toutes les attentes ou tous les cas possibles même futurs. Le nombre de méthodes est élevé est la prise en main difficile. Proche du blob à la différence que le couteau suisse tente de répondre à tous les besoins possibles alors que le blob monopolise les traitements du système.

3.4.3 Lava Flow L’écoulement de lave est le résultat d’un développement sans conception plus communément appelé « Quick and Dirty » qui a été mis en production avant toute stabilisation. Le résultat est que la coulée de lave se solidifie et que les modifications ne sont plus possible puisque utilisé par d’autres modules. On peut aisément se représenter le résultat de cette pratique sur une application au bout de quelques années : l’application devient couteuse à maintenir et le refactoring devient inévitable.

3.4.4 No OO Ce pattern non « Object Oriented » est le résultat d’un développement non-objet d’une application. Le tout ressemble à un programme procédural et ne possède aucune construction objet. L’héritage est bien sûr absent et le tout est d’une forte complexité. Des noms de méthodes et de variables utilisant les normes d’un autre langage et un diagramme de classe sans relations vous mettront sur la piste de ce type d’anti-patterns. Face à ce type de problème, il existe peu de solutions car l’application est « architecturalement défectueuse » donc quasiment irrécupérable.

3.4.5 Proliferation of Classes La prolifération de classes peut s’observer dans certaines conceptions objets où les pratiques de patterns sont excessives. Le principe de répartition des responsabilités est poussé à l’extrême et il arrive que certaines classes n’existent que le temps d’une invocation de méthode. On les appelle aussi classes « poltergeists » en comparaison aux phénomènes paranormaux qui apparaissent et disparaissent en quelques secondes. Globalement, ces classes augmentent inutilement le nombre

Page 71: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 71 / 109

total de classes et alourdissent le système puisque le garbage collector doit passer du temps pour libérer les ressources. Il est préférable de centraliser tous les messages par type dans une classe (cf. pattern Mediator du GOF).

3.4.6 Golden Hammer Ou comment tout faire avec un seul outil. Il est difficile, une fois que l’on maîtrise parfaitement un outil, d’explorer des solutions alternatives. A chaque nouveau développement on tente d’utiliser cet outil. Par exemple, vous maîtrisez parfaitement les EJB Entity et le projet suivant nécessite des requêtes dynamiques avec un grand volume de données. L’erreur serait de vouloir absolument utiliser les EJB alors qu’a priori un ORM, serait plus approprié. Au final, la solution est inadaptée et sûrement moins performante, les développeurs ne sortent pas de leur technologie fétiche et finissent rapidement par être « dépassés ». La solution est de faire une étude (même minimale) avant d’utiliser systématiquement le même outil. Si le travail est déjà réalisé, le mal est déjà fait et des pans entiers de l’application devront être refondus.

3.4.7 Spaghetti code Certainement un des anti-patterns les plus célèbres car il existe depuis la nuit des temps informatiques. Le couplage de l’application est très fort et la cohésion faible ce qui rend la maintenance hasardeuse. La réutilisation est inexistante et aucun des principes objets n’est respecté. On remarque aussi une utilisation forte de méthodes sans paramètres et de variables globales et publiques. Au même titre que l’anti-pattern No OO le refactoring est quasiment impossible.

3.4.8 Reinvent The Wheel L’une des grandes promesses de l’approche objet c’est de pouvoir réutiliser les éléments logiciels. Dans le même esprit il est préférable d’utiliser une brique logicielle (ou Framework) déjà testée et développée par un tiers plutôt que de partir d’une feuille blanche. Aujourd’hui commencer un projet en codant de A à Z une couche de persistance serait une hérésie. Avant de coder un Framework ou une fonctionnalité complexe, faite une recherche sur internet pour voir si une librairie ne propose pas déjà la solution.

Page 72: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 72 / 109

4 CONCLUSION

4.1 A retenir Les design patterns représentent un espace d’exploration très riche pour simplifier ou optimiser votre code. Attention cependant de ne pas tomber dans la « patternite aïgue » : la conception objet est un juste équilibre entre ce qui est possible et ce qui est nécessaire ; à vous de le trouver. Les patterns sont là pour vous faire gagner du temps : ils améliorent la flexibilité et la réutilisation, à vous de trouver aussi la bonne granularité. Au final souvenez-vous que les patterns sont la capitalisation d’expériences et qu’il y a toujours intérêt à les utiliser. Ils ne sont que des formes qui se mémorisent et s’adaptent facilement. Bonne conception ;-)

4.2 Et après ? Comme nous avons put le voir, les designs patterns simplifient l’approche par des motifs et une conception standardisée. Peut-on alors conclure que les patterns sont (enfin ?) la réponse ultime aux problématiques récurrentes de conception ? Pas vraiment. En effet ils apportent un langage commun aux développeurs mais ne règlent pas l’enchevêtrement du code métier et du pattern associé qui en résulte. Lorsque vous utilisez un motif Strategy, vous répartissez le code métier dans plusieurs classes distinctes. Le couplage entre les classes est toujours présent et les aspects techniques et métiers sont toujours mélangés. Un début de solution semble émerger avec la technologie AOP (Aspect Oriented Programmming). Le couplage entre les classes est quasi inexistant et la séparation entre les différents aspects (technique et métier) est possible via l’utilisation d’un tisseur applicatif. Vous trouverez de plus amples renseignements sur l’AOP à l’adresse suivante : http://aosd.net/

4.3 Code source 4.3.1 Création 4.3.1.1 AbstractFactory

4.3.1.1.1 Factory 01 package com.arnbe.patterns.creational.abstractfactory; 02 03 /** 04 * interface des Factory 05 */ 06 public interface Factory { 07 08 /** 09 * fabrique un objet 10 * 11 * @return l'objet construit 12 */ 13 public abstract Vehicule build(); 14 }

4.3.1.1.2 TruckFactory 01 package com.arnbe.patterns.creational.abstractfactory; 02 03 /** 04 * fabrique d'objet de type Truck 05 * 06 */ 07 public class TruckFactory implements Factory {

Page 73: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 73 / 109

08 09 /* 10 * (non-Javadoc) 11 * @see com.arnbe.patterns.creational.abstractfactory.Factory#build() 12 */ 13 public Vehicule build() { 14 return new Truck(); 15 } 16 }

4.3.1.1.3 PlaneFactory 01 package com.arnbe.patterns.creational.abstractfactory; 02 03 /** 04 * fabrique d'objet de type Plane 05 * 06 */ 07 public class PlaneFactory implements Factory { 08 09 /* 10 * (non-Javadoc) 11 * @see com.arnbe.patterns.creational.abstractfactory.Factory#build() 12 */ 13 public Vehicule build() { 14 return new Plane(); 15 } 16 }

4.3.1.1.4 Vehicule 01 package com.arnbe.patterns.creational.abstractfactory; 02 03 /** 04 * interface des véhicules 05 */ 06 public interface Vehicule { 07 08 09 /** 10 * démarre le moteur 11 * 12 */ 13 public void startEngine(); 14 }

4.3.1.1.5 Truck 01 package com.arnbe.patterns.creational.abstractfactory; 02 03 /** 04 * classe de type Truck 05 */ 06 public class Truck implements Vehicule { 07 08 public void drive() { 09 //code... 10 } 11 12 /* 13 * (non-Javadoc) 14 * @see com.arnbe.patterns.creational.abstractfactory.Vehicule#startEngine() 15 */ 16 public void startEngine() { 17 //code... 18 } 19 }

4.3.1.1.6 Plane 01 package com.arnbe.patterns.creational.abstractfactory; 02 03 /** 04 * classe de type Plane 05 */ 06 public class Plane implements Vehicule { 07 08 public void fly() { 09 //code... 10 } 11 12 /* 13 * (non-Javadoc) 14 * @see com.arnbe.patterns.creational.abstractfactory.Vehicule#startEngine() 15 */ 16 public void startEngine() { 17 //code... 18 } 19 }

Page 74: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 74 / 109

4.3.1.2 Builder

4.3.1.2.1 A380 01 package com.arnbe.patterns.creational.builder; 02 03 /** 04 * Classe d'un produit concret : l'A380 05 */ 06 07 public class A380 implements Plane { 08 09 public void startEngine() { 10 //code... 11 } 12 13 public void openFlap() { 14 //code... 15 } 16 }

4.3.1.2.2 A380Builder 01 package com.arnbe.patterns.creational.builder; 02 03 /** 04 * crée les différentes parties de l'objet 05 */ 06 public class A380Builder implements PlaneBuilder { 07 08 /** 09 * Référence vers le produit à construire 10 */ 11 private A380 resultProduct = null; 12 13 /* 14 * (non-Javadoc) 15 * @see com.arnbe.patterns.creational.builder.PlaneBuilder#getResult() 16 */ 17 public Plane getResult() { 18 return resultProduct; 19 } 20 21 public void buildEngine() { 22 //code... 23 } 24 25 public void buildWing() { 26 //code... 27 } 28 }

4.3.1.2.3 Director 01 package com.arnbe.patterns.creational.builder; 02 03 /** 04 * prend en charge l'ordre de construction de l'objet 05 * à partir des méthodes des monteurs 06 */ 07 08 public class Director { 09 10 /** 11 * @link aggregationByValue 12 */ 13 14 private PlaneBuilder builder = null; 15 16 /** 17 * constructeur 18 * 19 * @param builder 20 */ 21 public Director(PlaneBuilder builder) { 22 this.builder = builder; 23 } 24 25 /** 26 * controler la construction de l'objet 27 * 28 */ 29 public void build() { 30 //on construit les ailes avant de poser les moteurs en dessous 31 builder.buildWing(); 32 builder.buildEngine(); 33 //... 34 } 35 }

Page 75: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 75 / 109

4.3.1.2.4 Plane 01 package com.arnbe.patterns.creational.builder; 02 03 /** 04 * représente l'interface communes aux avions 05 * 06 */ 07 public interface Plane { 08 /** 09 * démarre le moteur 10 */ 11 public void startEngine(); 12 13 /** 14 * ouverture des flaps 15 */ 16 public void openFlap(); 17 }

4.3.1.2.5 PlaneBuilder 01 package com.arnbe.patterns.creational.builder; 02 03 /** 04 * interface de chaque monteur d'avion 05 */ 06 public interface PlaneBuilder { 07 08 /** 09 * Construit les moteurs 10 */ 11 public void buildEngine(); 12 13 /** 14 * construit les ailes de l'avion 15 */ 16 public void buildWing(); 17 18 /** 19 * @return le produit construit 20 */ 21 Plane getResult(); 22 }

4.3.1.3 FactoryMethod

4.3.1.3.1 Product 1 package com.arnbe.patterns.creational.factorymethod; 2 3 /** 4 * interface des produits 5 */ 6 public interface Product { 7 }

4.3.1.3.2 Truck 1 package com.arnbe.patterns.creational.factorymethod; 2 3 /** 4 * Produit concret utilisé par TruckCreator 5 */ 6 public class Truck implements Product { 7 }

4.3.1.3.3 AbstractCreator 01 package com.arnbe.patterns.creational.factorymethod; 02 03 /** 04 * Classe mère 05 */ 06 public abstract class AbstractCreator { 07 08 /** 09 * méthode de construction à implémenter 10 * 11 * @return 12 */ 13 public abstract Product factoryMethod(); 14 15 /** 16 * utilisation 17 * 18 */ 19 public void sampleUse() { 20 // ...

Page 76: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 76 / 109

21 Product product = factoryMethod(); 22 // ... 23 } 24 }

4.3.1.3.4 TruckCreator 01 package com.arnbe.patterns.creational.factorymethod; 02 03 /** 04 * Hérite pour fournir un créateur concret 05 */ 06 07 public class TruckCreator extends AbstractCreator { 08 09 /* 10 * (non-Javadoc) 11 * @see com.arnbe.patterns.creational.factorymethod.AbstractCreator#factoryMethod() 12 */ 13 public Product factoryMethod() { 14 return new Truck(); 15 } 16 }

4.3.1.4 Prototype

4.3.1.4.1 Prototype 1 package com.arnbe.patterns.creational.prototype; 2 3 /** 4 * Classe mère 5 */ 6 public interface Prototype { 7 8 Prototype clone(); 9 }

4.3.1.4.2 Plane 01 package com.arnbe.patterns.creational.prototype; 02 03 /** 04 * Classe ayant la capacité de se cloner 05 */ 06 public class Plane implements Prototype { 07 08 protected Plane(Plane prototype) { 09 //constructeur permettant d'initialiser un prototype à partir d'un autre 10 } 11 12 public Prototype clone() { 13 return new Plane(this); 14 } 15 }

4.3.1.4.3 Truck 01 package com.arnbe.patterns.creational.prototype; 02 03 public class Truck implements Prototype { 04 05 protected Truck(Truck prototype) { 06 //constructeur permettant d'initialiser un prototype à partir d'un autre 07 } 08 09 public Prototype clone() { 10 return new Truck(this); 11 } 12 }

4.3.1.5 Singleton

4.3.1.5.1 Singleton 01 package com.arnbe.patterns.creational.singleton; 02 03 /** 04 * Represente un singleton. 05 */ 06 07 public class Singleton { 08 09 /** 10 * Poignée sur le singleton 11 */ 12 private static Singleton instance=null; 13 14 /**

Page 77: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 77 / 109

15 * Constructeur privé pour interdire toute instanciation extérieure 16 */ 17 private Singleton() { 18 //code... 19 } 20 21 /** 22 * Retourne l'instance du singleton : si elle n'existe pas, elle est crée 23 @return le singleton 24 */ 25 static public Singleton getInstance() { 26 if (instance == null) { 27 instance = new Singleton(); 28 } 29 return instance; 30 } 31 }

4.3.2 Structure 4.3.2.1 Adapter

4.3.2.1.1 IteratorEnumerationAdapter 01 package com.arnbe.patterns.structural.adapter; 02 03 import java.util.Enumeration; 04 import java.util.Iterator; 05 06 public class IteratorEnumerationAdapter implements Iterator { 07 08 /** énumération interne */ 09 private Enumeration internEnum = null; 10 11 /** 12 * constructeur 13 * 14 * @param enumeration 15 */ 16 public IteratorEnumerationAdapter(Enumeration enumeration) { 17 internEnum = enumeration; 18 } 19 20 /* 21 * (non-Javadoc) 22 * @see java.util.Iterator#hasNext() 23 */ 24 public boolean hasNext() { 25 return internEnum.hasMoreElements(); 26 } 27 28 /* 29 * (non-Javadoc) 30 * @see java.util.Iterator#next() 31 */ 32 public Object next() { 33 return internEnum.nextElement(); 34 } 35 36 /* 37 * (non-Javadoc) 38 * @see java.util.Iterator#remove() 39 */ 40 public void remove() { 41 //ici une exception de type Runtime est lancée, car la notion de remove n'existe pas 42 //pour l'interface Enumeration 43 throw new UnsupportedOperationException("IteratorEnumerationAdapter ne supporte pas la suppression d'éléments"); 44 } 45 }

4.3.2.2 Bridge

4.3.2.2.1 AbstractCustomers 01 package com.arnbe.patterns.structural.bridge; 02 03 /** 04 * Classe mère 05 */ 06 public abstract class AbstractCustomers { 07 08 /** 09 * Reference vers l'Implémentor 10 */ 11 private DataObject impl = null; 12 protected String group = null; 13 14 /** 15 * constructeur 16 * 17 * @param group nom du groupe de client 18 */ 19 public AbstractCustomers(String group) {

Page 78: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 78 / 109

20 this.group = group; 21 } 22 23 /** 24 * @return implementation-in-action. 25 */ 26 protected DataObject getImplementor() { 27 return impl; 28 } 29 30 /** 31 * 32 * @param impl 33 */ 34 public void setImplementor(DataObject impl) { 35 this.impl = impl; 36 } 37 38 /** 39 * ajoute un client 40 * @param name nom du client 41 */ 42 public void add(String name) { 43 impl.addRecord(name); 44 } 45 46 /** 47 * se positionne sur l'enregistrement suivant 48 */ 49 public void next() { 50 impl.nextRecord(); 51 } 52 53 /** 54 * se positionne sur l'enregistrement précédent 55 */ 56 public void previous() { 57 impl.previousRecord(); 58 } 59 60 /** 61 * supprime le client correspondant au nom 62 * @param name nom du client à supprimer 63 */ 64 public void remove(String name) { 65 impl.removeRecord(name); 66 } 67 }

4.3.2.2.2 Customers 01 package com.arnbe.patterns.structural.bridge; 02 03 /** 04 * Classe concrète Customers 05 */ 06 public class Customers extends AbstractCustomers { 07 08 public Customers(String group) { 09 super(group); 10 } 11 }

4.3.2.2.3 DataObject 01 package com.arnbe.patterns.structural.bridge; 02 03 /** 04 * classe mère des containers de données 05 */ 06 public abstract class DataObject { 07 08 /** 09 * supprime l'enregistrement correspondant au nom 10 * 11 * @param name nom de l'objet à supprimer 12 */ 13 public abstract void removeRecord(String name); 14 15 /** 16 * ajoute un enregistrement avec le nom en paramètre 17 * 18 * @param name nom de l'enregistrement à ajouter 19 */ 20 public abstract void addRecord(String name); 21 22 /** 23 * se positionne sur l'enregistrement suivant 24 */ 25 public abstract void nextRecord(); 26 27 /** 28 * se positionne sur l'enregistrement précédent 29 */ 30 public abstract void previousRecord(); 31 }

Page 79: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 79 / 109

4.3.2.2.4 InMemoryData 02 package com.arnbe.patterns.structural.bridge; 03 04 import java.util.ArrayList; 05 06 /** 07 * implémentation du DataObject sous forme d'un tableau mémoire 08 */ 09 public class InMemoryData extends DataObject { 10 11 private ArrayList<String> dataList = new ArrayList<String>(); 12 private int current = 0; 13 14 public InMemoryData() { 15 super(); 16 //ce code est ici uniquement pour l'exemple 17 //les données pourront être lues depuis une base de données par exemple 18 //ou fourni par une classe dédiée 19 dataList.add("bertrand dupond"); 20 dataList.add("daffy duck"); 21 dataList.add("georges laforet"); 22 dataList.add("frederic lepage"); 23 } 24 25 /* 26 * (non-Javadoc) 27 * @see com.arnbe.patterns.structural.bridge.DataObject#nextRecord() 28 */ 29 @Override 30 public void nextRecord() { 31 if(current <= dataList.size()-1) { 32 current++; 33 } 34 } 35 36 /* 37 * (non-Javadoc) 38 * @see com.arnbe.patterns.structural.bridge.DataObject#previousRecord() 39 */ 40 @Override 41 public void previousRecord() { 42 if(current > 0) { 43 current--; 44 } 45 } 46 47 /* 48 * (non-Javadoc) 49 * @see com.arnbe.patterns.structural.bridge.DataObject#removeRecord() 50 */ 51 @Override 52 public void removeRecord(String name) { 53 dataList.remove(name); 54 } 55 56 /* 57 * (non-Javadoc) 58 * @see com.arnbe.patterns.structural.bridge.DataObject#addRecord(java.lang.String) 59 */ 60 @Override 61 public void addRecord(String name) { 62 dataList.add(name); 63 } 64 }

4.3.2.3 Composite

4.3.2.3.1 Product 001 package com.arnbe.patterns.structural.composite; 002 003 import java.util.HashMap; 004 import java.util.Iterator; 005 import java.util.Map; 006 007 /** 008 * Classe des produits 009 * elle peut contenir des objets de type Produit 010 * 011 */ 012 public class Product { 013 014 /** parent du produit */ 015 private Product parent = null; 016 017 private String id = null; 018 019 private String label = null; 020 021 /** map des produits enfants */ 022 private Map<String, Product> lnkProduct = new HashMap<String, Product>(); 023

Page 80: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 80 / 109

024 /** 025 * constructeur 026 * @param id du produit 027 * @param label du produit 028 */ 029 public Product(String id, String label) { 030 super(); 031 this.id = id; 032 this.label = label; 033 } 034 035 /** 036 * ajoute un produit 037 * 038 * @param product 039 */ 040 protected void addProduct(Product productChild) { 041 productChild.setParent(this); 042 lnkProduct.put(productChild.getId(), productChild); 043 } 044 045 /** 046 * recherche un produit parmis les enfants 047 * @param id 048 * @return 049 */ 050 protected Product getProduct(String id) { 051 return lnkProduct.get(id); 052 } 053 054 /** 055 * retourne une itération des produits enfants 056 * @return 057 */ 058 protected Iterator<Product> getProducts() { 059 return lnkProduct.values().iterator(); 060 } 061 062 /** 063 * retire un produit parmis les enfants 064 * 065 * @param id 066 * @return 067 */ 068 protected void removeProduct(String id) { 069 Product p = lnkProduct.remove(id); 070 if(p!=null) { 071 p.setParent(null); 072 } 073 } 074 075 /** 076 * retourne le label 077 * 078 * @return 079 */ 080 public String getLabel() { 081 return label; 082 } 083 084 /** 085 * retourne l'id du produit 086 * 087 * @return 088 */ 089 public String getId() { 090 return id; 091 } 092 093 /** 094 * retourne le parent du produit 095 * 096 * @return 097 */ 098 public Product getParent() { 099 return parent; 100 } 101 102 /** 103 * positionne le parent du produit 104 * 105 * @param parent 106 */ 107 protected void setParent(Product parent) { 108 this.parent = parent; 109 } 110 }

4.3.2.3.2 ProductComposite

01 package com.arnbe.patterns.structural.composite; 02 03 import java.util.Iterator; 04 05 public class ProductComposite { 06

Page 81: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 81 / 109

07 /** produit racine de l'arborescence */ 08 Product rootProduct = null; 09 10 /** 11 * constructeur 12 * 13 * @param root 14 */ 15 public ProductComposite(Product root) { 16 rootProduct = root; 17 } 18 19 /** 20 * ajoute un produit sous le parent définie par l'id 21 * 22 * @param child 23 * @param parent 24 */ 25 public void addProduct(Product child, String parentId) { 26 Product p = findProduct(rootProduct, parentId); 27 if(p != null) { 28 p.addProduct(child); 29 } else { 30 throw new IllegalStateException("can't find parent product"); 31 } 32 } 33 34 /** 35 * supprime le produit de l'arborescence 36 * 37 * @param id 38 * @return 39 */ 40 public void removeProduct(String id) { 41 Product product = findProduct(rootProduct, id); 42 if(product!=null) { 43 Product parent = product.getParent(); 44 parent.removeProduct(id); 45 } 46 } 47 48 /** 49 * recherche un produit dans le graph d'objet 50 * la méthode n'est pas efficace mais suffit pour l'exemple 51 * 52 * @param product 53 * @param id 54 * @return 55 */ 56 private Product findProduct(Product product, String id) { 57 if(product.getId().equals(id)) { 58 return product; 59 } 60 //cherche dans les childs 61 for (Iterator<Product> iterProduct = product.getProducts(); iterProduct.hasNext();) { 62 Product productChild = findProduct(iterProduct.next(), id); 63 if(productChild!=null) { 64 return productChild; 65 } 66 } 67 return null; 68 } 69 }

4.3.2.4 Decorator

4.3.2.4.1 Book 01 02 package com.arnbe.patterns.structural.decorator; 03 04 /** 05 * Classe Book 06 */ 07 public class Book extends LibraryItem { 08 09 private String author = null; 10 private String title = null; 11 12 /** 13 * constructeur 14 * 15 * @param author 16 * @param title 17 * @param numCopies 18 */ 19 public Book(String author, String title, int numCopies) { 20 super(); 21 this.author = author; 22 this.title = title; 23 this.numCopies = numCopies; 24 } 25 26 /* 27 * (non-Javadoc) 28 * @see com.arnbe.patterns.structural.decorator.LibraryItem#display() 29 */

Page 82: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 82 / 109

30 @Override 31 public void display() { 32 System.out.println(""); 33 System.out.println("Books"); 34 System.out.println("Author : " + author); 35 System.out.println("Title : " + title); 36 System.out.println("Copies : " + numCopies); 37 } 38 }

4.3.2.4.2 Video 02 package com.arnbe.patterns.structural.decorator; 03 04 /** 05 * Classe Video 06 */ 07 public class Video extends LibraryItem { 08 09 private String director = null; 10 private String title = null; 11 private int playTime = 0; 12 13 /** 14 * constructeur 15 * 16 * @param director 17 * @param title 18 * @param playTime 19 * @param numCopies 20 */ 21 public Video(String director, String title, int playTime, int numCopies) { 22 super(); 23 this.director = director; 24 this.title = title; 25 this.playTime = playTime; 26 this.numCopies = numCopies; 27 } 28 29 /* 30 * (non-Javadoc) 31 * @see com.arnbe.patterns.structural.decorator.LibraryItem#display() 32 */ 33 @Override 34 public void display() { 35 System.out.println(""); 36 System.out.println("Videos"); 37 System.out.println("Director : " + director); 38 System.out.println("Title : " + title); 39 System.out.println("Copies : " + numCopies); 40 System.out.println("PlayTime : " + playTime); 41 } 42 }

4.3.2.4.3 Borrowable 01 package com.arnbe.patterns.structural.decorator; 02 03 import java.util.ArrayList; 04 05 /** 06 * Cette classe ajoute des responsabilités aux classes qui hérite de LibraryItem 07 */ 08 09 public class Borrowable extends Decorator { 10 11 /** liste des emprunteurs du média */ 12 protected ArrayList<String> borrowers = new ArrayList<String>(); 13 14 /** 15 * constructeur 16 * 17 * @param component 18 */ 19 public Borrowable(LibraryItem component) { 20 super(component); 21 } 22 23 /** 24 * le média est emprunté 25 * 26 * @param name 27 */ 28 public void borrowItem(String name) { 29 borrowers.add(name); 30 librairyItem.numCopies--; 31 } 32 33 /** 34 * le média est rendu 35 * 36 * @param name 37 */ 38 public void returnItem(String name) { 39 borrowers.remove(name); 40 librairyItem.numCopies++;

Page 83: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 83 / 109

41 } 42 43 /* (non-Javadoc) 44 * @see com.arnbe.patterns.structural.decorator.Decorator#display() 45 */ 46 @Override 47 public void display() { 48 super.display(); 49 //ici commence l'ajout de responsabilité 50 for (String borrower : borrowers) { 51 System.out.println("Borrower : " + borrower); 52 } 53 } 54 }

4.3.2.4.4 Decorator 01 package com.arnbe.patterns.structural.decorator; 02 03 /** 04 * 05 */ 06 public abstract class Decorator extends LibraryItem { 07 08 /** 09 * référence vers l'élément à décorer 10 */ 11 protected LibraryItem librairyItem = null; 12 13 /** 14 * constructeur 15 * 16 * @param librairyItem 17 */ 18 public Decorator(LibraryItem librairyItem) { 19 this.librairyItem = librairyItem; 20 } 21 22 /* (non-Javadoc) 23 * @see com.arnbe.patterns.structural.decorator.LibraryItem#display() 24 */ 25 @Override 26 public void display() { 27 librairyItem.display(); 28 } 29 }

4.3.2.4.5 LibraryItem 01 package com.arnbe.patterns.structural.decorator; 02 03 /** 04 * Element de la librairie 05 */ 06 public abstract class LibraryItem { 07 08 /** nombre de copies libre dans la librairie */ 09 protected int numCopies = 0; 10 11 /** affiche le contenu de l'objet */ 12 public abstract void display(); 13 14 }

4.3.2.5 Facade

4.3.2.5.1 Compiler 01 package com.arnbe.patterns.structural.facade; 02 03 public class Compiler { 04 05 private CodeGenerator lnkCodeGenerator = null; 06 private Parser lnkParser = null; 07 private Scanner lnkScanner = null; 08 09 public Compiler() { 10 super(); 11 lnkCodeGenerator = new CodeGenerator(); 12 lnkScanner = new Scanner(); 13 lnkParser = new Parser(); 14 } 15 16 public void compile() { 17 //code... 18 lnkScanner.scan(); 19 lnkParser.parse(); 20 lnkCodeGenerator.generate(); 21 //code... 22 } 23 }

Page 84: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 84 / 109

4.3.2.6 Flyweight

4.3.2.6.1 Character 01 package com.arnbe.patterns.structural.flyweight; 02 03 /** 04 * composant FlyWeight etclasse mère des caractères 05 */ 06 public abstract class Character implements GraphicalItem { 07 08 /** code du symbol */ 09 protected char symbol; 10 11 /** largeur en point */ 12 protected int width; 13 14 /** hauteur en point */ 15 protected int height; 16 17 18 /* 19 * (non-Javadoc) 20 * @see com.arnbe.patterns.structural.flyweight.GraphicalItem#display(com.arnbe.patterns.structural.flyweight.Context) 21 */ 22 public void display(Context context) { 23 System.out.println(symbol + " (Font " + context.getFont() + ")"); 24 } 25 }

4.3.2.6.2 CharacterA 01 package com.arnbe.patterns.structural.flyweight; 02 03 /** 04 * 05 * 06 */ 07 public class CharacterA extends Character { 08 09 public CharacterA() { 10 this.symbol = 'A'; 11 this.height = 100; 12 this.width = 120; 13 } 14 }

4.3.2.6.3 CharacterB 01 package com.arnbe.patterns.structural.flyweight; 02 /** 03 * 04 * 05 */ 06 public class CharacterB extends Character { 07 08 public CharacterB() { 09 this.symbol = 'B'; 10 this.height = 100; 11 this.width = 140; 12 } 13 }

4.3.2.6.4 CharacterZ 01 package com.arnbe.patterns.structural.flyweight; 02 03 /** 04 * 05 */ 06 public class CharacterZ extends Character { 07 08 public CharacterZ() { 09 this.symbol = 'Z'; 10 this.height = 100; 11 this.width = 100; 12 } 13 }

4.3.2.6.5 Context 01 package com.arnbe.patterns.structural.flyweight; 02 03 public interface Context { 04 05 /** retourne la Font sur la position en cours */ 06 public String getFont(); 07 08 /** positionne le type de Font sur une plage de caractère pour une ligne donnée*/ 09 public void setFont(String fontName, int rowNumber, int from, int to); 10 }

Page 85: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 85 / 109

4.3.2.6.6 GraphicalItem 01 package com.arnbe.patterns.structural.flyweight; 02 03 /** 04 * 05 */ 06 public interface GraphicalItem { 07 /** 08 * méthode d'affichage de l'élément graphique 09 * 10 * @param context 11 */ 12 public void display(Context context); 13 }

4.3.2.6.7 GraphicalItemFactory 01 package com.arnbe.patterns.structural.flyweight; 02 03 import java.util.HashMap; 04 05 /** 06 * Fabrique des caractères 07 */ 08 09 public class GraphicalItemFactory { 10 11 /** stock des éléments graphiques déjà crée */ 12 private HashMap<Integer, GraphicalItem> characters = null; 13 14 /** 15 * constructeur 16 * 17 */ 18 public GraphicalItemFactory() { 19 characters = new HashMap<Integer, GraphicalItem>(); 20 } 21 22 /** 23 * retourne le caractère demandé 24 * si il existe on ne crée pas de nouvelle instance 25 * sinon on en crée une nouvelle 26 * on peut parler de "lazy initialization" 27 * 28 * @param key 29 * @return 30 */ 31 public Character getCharacter(char key) { 32 //verifie si l'objet n'existe pas déjà 33 Integer intKey = new Integer((int)key); 34 GraphicalItem gItem = characters.get(intKey); 35 if(gItem!=null) { 36 return (Character) gItem; 37 } else { 38 //ici le choix du type de classe à retourner 39 switch(key) { 40 case 'A' : gItem = new CharacterA(); break; 41 case 'B' : gItem = new CharacterB(); break; 42 //etc... 43 case 'Z' : gItem = new CharacterZ(); break; 44 } 45 characters.put(intKey, gItem); 46 return (Character) gItem; 47 } 48 } 49 50 /** 51 * @return new instance of unshared flyweight 52 */ 53 public Row getRow(String content) { 54 return new Row(content); 55 } 56 }

4.3.2.6.8 PageContext 01 package com.arnbe.patterns.structural.flyweight; 02 03 public class PageContext implements Context { 04 05 /** position du caractère courant */ 06 private int currentCar = 0; 07 /** numéro de la ligne courante */ 08 private int currentRow = 0; 09 10 /** 11 * constructeur 12 * 13 */ 14 public PageContext() { 15 super(); 16 }

Page 86: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 86 / 109

17 18 /** 19 * positionne sur le caractère suivant de la ligne en cours 20 * 21 */ 22 public void next() { 23 currentCar++; 24 } 25 26 /** 27 * passe à la ligne suivante 28 * 29 */ 30 public void nextLine() { 31 currentRow++; 32 currentCar = 0; 33 } 34 35 /* 36 * (non-Javadoc) 37 * @see com.arnbe.patterns.structural.flyweight.Context#getFont() 38 */ 39 public String getFont() { 40 //mettre ici le code pour retrouver la Font pour la ligne 41 //et la position donnée 42 return "Arial"; 43 } 44 45 /* 46 * (non-Javadoc) 47 * @see com.arnbe.patterns.structural.flyweight.Context#setFont(java.lang.String, int, int, int) 48 */ 49 public void setFont(String fontName, int rowNumber, int from, int to) { 50 //mettre ici le code pour stocker la Font sur la ligne 51 } 52 }

4.3.2.6.9 Row 01 package com.arnbe.patterns.structural.flyweight; 02 03 /** 04 * Classe FlyWeight non partagée 05 * représente une ligne d'une page 06 */ 07 08 public class Row implements GraphicalItem { 09 10 /** contenu de la ligne */ 11 private String content = null; 12 13 /** 14 * constructeur 15 * 16 * @param content 17 */ 18 public Row(String content) { 19 super(); 20 this.content = content; 21 } 22 23 /** 24 * retourne le contenu de la ligne 25 * @return 26 */ 27 public String getContent() { 28 return content; 29 } 30 31 /** 32 * 33 * @return 34 */ 35 public char[] charArray() { 36 return content.toCharArray(); 37 } 38 39 /* 40 * (non-Javadoc) 41 * @see com.arnbe.patterns.structural.flyweight.GraphicalItem#display(com.arnbe.patterns.structural.flyweight.Context) 42 */ 43 public void display(Context context) { 44 System.out.println(content + " (length " + content.length() + ")"); 45 } 46 }

4.3.2.7 Proxy

4.3.2.7.1 Graphic 01 package com.arnbe.patterns.structural.proxy; 02 03 /** 04 * Interface d'élément graphic 05 */

Page 87: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 87 / 109

06 public interface Graphic { 07 08 /** 09 * méthode fonctionnelle 10 * 11 */ 12 public void draw(); 13 14 public void load(); 15 16 public void store(); 17 18 public void getExtent(); 19 }

4.3.2.7.2 Image 01 package com.arnbe.patterns.structural.proxy; 02 03 /** 04 * Classe de l'objet réel 05 */ 06 07 public class Image implements Graphic { 08 09 /* 10 * (non-Javadoc) 11 * @see com.arnbe.patterns.structural.proxy.Graphic#draw() 12 */ 13 public void draw() { 14 lockWhileCompleted(); 15 //code... 16 } 17 18 /* 19 * (non-Javadoc) 20 * @see com.arnbe.patterns.structural.proxy.Graphic#load() 21 */ 22 public void load() { 23 lockWhileCompleted(); 24 //code... 25 } 26 27 /* 28 * (non-Javadoc) 29 * @see com.arnbe.patterns.structural.proxy.Graphic#store() 30 */ 31 public void store() { 32 lockWhileCompleted(); 33 //code... 34 } 35 36 /* 37 * (non-Javadoc) 38 * @see com.arnbe.patterns.structural.proxy.Graphic#getExtent() 39 */ 40 public void getExtent() { 41 lockWhileCompleted(); 42 //code... 43 } 44 45 /** 46 * méthode bloquante tant que l'image n'est pas chargée 47 * 48 */ 49 private void lockWhileCompleted() { 50 //code... 51 } 52 }

4.3.2.7.3 ImageProxy 01 package com.arnbe.patterns.structural.proxy; 02 03 /** 04 * Classe de proxy pour une image 05 */ 06 07 public class ImageProxy implements Graphic { 08 09 /** 10 * Poignée sur l'objet graphique 11 */ 12 private Graphic subject = null; 13 14 /** 15 * constructeur 16 * 17 * @param subject 18 */ 19 public ImageProxy(Graphic subject) { 20 super(); 21 this.subject = subject; 22 } 23 24 /*

Page 88: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 88 / 109

25 * (non-Javadoc) 26 * @see com.arnbe.patterns.structural.proxy.Graphic#draw() 27 */ 28 public void draw() { 29 subject.draw(); 30 } 31 32 /* 33 * (non-Javadoc) 34 * @see com.arnbe.patterns.structural.proxy.Graphic#load() 35 */ 36 public void load() { 37 return; 38 } 39 40 /* 41 * (non-Javadoc) 42 * @see com.arnbe.patterns.structural.proxy.Graphic#store() 43 */ 44 public void store() { 45 return; 46 } 47 48 /* 49 * (non-Javadoc) 50 * @see com.arnbe.patterns.structural.proxy.Graphic#getExtent() 51 */ 52 public void getExtent() { 53 return; 54 } 55 }

4.3.3 Comportement

4.3.3.1 Chain of responsability

4.3.3.1.1 Approver 01 package com.arnbe.patterns.behavioural.chainofresponsability; 02 03 /** 04 * 05 */ 06 public abstract class Approver { 07 08 protected Approver successor = null; 09 10 /** 11 * méthode qui doit être surchargée 12 * 13 */ 14 public abstract void handleRequest(Project p); 15 16 /** 17 * fixe le maillon suivant dans la chaine 18 * 19 * @param successor 20 */ 21 public void setSuccessor(Approver successor) { 22 this.successor = successor; 23 } 24 25 /** 26 * retourne le maillon suivant de la chaine 27 * 28 * @return 29 */ 30 public Approver getSuccessor() { 31 return successor; 32 } 33 }

4.3.3.1.2 Employee 01 package com.arnbe.patterns.behavioural.chainofresponsability; 02 03 /** 04 * Classe d'un employée 05 * elle a la capacité de recevoir des messages via la méthode 06 * handleRequest 07 */ 08 09 public class Employee extends Approver { 10 11 private String function = null; 12 13 /** 14 * constructeur 15 * 16 * @param function 17 */ 18 public Employee(String function) { 19 super(); 20 this.function = function;

Page 89: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 89 / 109

21 } 22 23 /** 24 * Gère le message ou le passe au suivant 25 */ 26 @Override 27 public void handleRequest(Project p) { 28 boolean someCondition = false; 29 if (someCondition) { 30 //traite la requête 31 } else { 32 //passe au suivant dans la chaine si il existe 33 if (successor != null) { 34 successor.handleRequest(p); 35 } 36 } 37 } 38 }

4.3.3.1.3 Project 01 package com.arnbe.patterns.behavioural.chainofresponsability; 02 03 public class Project { 04 05 private String name = null; 06 private int amount = 0; 07 08 /** 09 * constructeur 10 * 11 * @param amount 12 * @param name 13 */ 14 public Project(int amount, String name) { 15 super(); 16 this.amount = amount; 17 this.name = name; 18 } 19 20 /** 21 * @return 22 */ 23 public int getAmount() { 24 return amount; 25 } 26 27 /** 28 * @return 29 */ 30 public String getName() { 31 return name; 32 } 33 }

4.3.3.2 Command

4.3.3.2.1 Calculator 01 package com.arnbe.patterns.behavioural.command; 02 03 /** 04 * classe contenant le traitement à mettre en oeuvre 05 * 06 * @author abarbe 07 * 08 */ 09 public class Calculator { 10 11 private int curr = 0; 12 13 public void Operation(char operator, int operand) { 14 switch(operator) { 15 case '+': curr += operand; break; 16 case '-': curr -= operand; break; 17 case '*': curr *= operand; break; 18 case '/': curr /= operand; break; 19 } 20 System.out.println("Current value = " + curr + "(following " + operator + " " + operand + ")"); 21 } 22 }

4.3.3.2.2 CalculatorCommand 01 package com.arnbe.patterns.behavioural.command; 02 03 /** 04 * Fait le lien entre le calculateur et une action 05 * à la connaissance pour défaire une action 06 */ 07 08 public class CalculatorCommand implements Command { 09

Page 90: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 90 / 109

10 char operator; 11 int operand; 12 Calculator calculator = null; 13 14 /** 15 * constructeur 16 * 17 * @param receiver 18 */ 19 public CalculatorCommand(Calculator calculator, char operator, int operand) { 20 this.calculator = calculator; 21 this.operator = operator; 22 this.operand = operand; 23 } 24 25 /* 26 * (non-Javadoc) 27 * @see com.arnbe.patterns.behavioural.command.Command#execute() 28 */ 29 public void execute() { 30 calculator.Operation(operator, operand); 31 32 } 33 34 /* 35 * (non-Javadoc) 36 * @see com.arnbe.patterns.behavioural.command.Command#unExecute() 37 */ 38 public void unExecute() { 39 calculator.Operation(undo(operator), operand); 40 } 41 42 /** 43 * annule une opération 44 * 45 * @param operator 46 * @return 47 */ 48 private char undo(char operator){ 49 char undo; 50 switch(operator){ 51 case '+': undo = '-'; break; 52 case '-': undo = '+'; break; 53 case '*': undo = '/'; break; 54 case '/': undo = '*'; break; 55 default : undo = ' '; break; 56 } 57 return undo; 58 } 59 }

4.3.3.2.3 Command 01 package com.arnbe.patterns.behavioural.command; 02 /** 03 * interface de la Commande 04 */ 05 public interface Command { 06 07 /** 08 * méthode d'éxécution de la commande 09 */ 10 public void execute(); 11 /** 12 * méthode pour annuler la commande 13 * 14 */ 15 public void unExecute(); 16 }

4.3.3.2.4 User 01 package com.arnbe.patterns.behavioural.command; 02 03 import java.util.ArrayList; 04 05 /** 06 * classe de type Invoker 07 * c'est le point d'entrée du pattern 08 * 09 */ 10 public class User { 11 12 private Calculator calculator = new Calculator(); 13 14 /** liste des commands */ 15 private ArrayList<Command> commands = new ArrayList<Command>(); 16 17 private int current = 0; 18 19 /** 20 * rejoue les commandes jusqu'à un certain niveau 21 * 22 * @param levels 23 */ 24 public void redo(int levels) {

Page 91: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 91 / 109

25 for (int i = 0; i < levels; i++) { 26 if (current < commands.size() - 1) { 27 commands.get(current++).execute(); 28 } 29 } 30 } 31 32 /** 33 * défait les commandes jusqu'à un certain niveau 34 * 35 * @param levels 36 */ 37 public void undo(int levels) { 38 for (int i = 0; i < levels; i++) { 39 if (current > 0) { 40 commands.get(--current).unExecute(); 41 } 42 } 43 } 44 45 /** 46 * créer un calcul par le biais d'une Command et l'execute 47 * 48 * @param operator 49 * @param operand 50 */ 51 public void compute(char operator, int operand) { 52 Command command = new CalculatorCommand(calculator, operator, operand); 53 command.execute(); 54 55 //ajoute la commande dans la list 56 commands.add(command); 57 current++; 58 } 59 }

4.3.3.3 Interpreter

4.3.3.3.1 Context 01 package com.arnbe.patterns.behavioural.interpreter; 02 03 public class Context { 04 05 private String input = null; 06 07 private int output; 08 09 public Context(String input) { 10 this.input = input; 11 } 12 13 /** 14 * retourne la chaine d'entrée 15 * @return 16 */ 17 public String getInput() { 18 return input; 19 } 20 21 /** 22 * affecte la chaine d'input 23 * @param 24 */ 25 public void setInput(String input) { 26 this.input = input; 27 } 28 29 /** 30 * retourne le contenu de la sortie 31 * @return 32 */ 33 public int getOutput() { 34 return output; 35 } 36 37 /** 38 * affecte le contenu de la sortie 39 * @param 40 */ 41 public void setOutput(int output) { 42 this.output = output; 43 } 44 }

4.3.3.3.2 Expression 01 package com.arnbe.patterns.behavioural.interpreter; 02 03 /** 04 * 05 */ 06 public abstract class Expression { 07 08 /**

Page 92: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 92 / 109

09 * interprète le contenu du context 10 * 11 * @param context 12 */ 13 public void interpret(Context context) { 14 if(context.getInput().length() == 0) { 15 return; 16 } 17 18 if(context.getInput().startsWith(nine())) { 19 context.setOutput(context.getOutput() + (9 * multiplier())); 20 context.setInput(context.getInput().substring(2)); 21 } 22 else if(context.getInput().startsWith(four())) { 23 context.setOutput(context.getOutput() + (4 * multiplier())); 24 context.setInput(context.getInput().substring(2)); 25 } 26 else if(context.getInput().startsWith(five())) { 27 context.setOutput(context.getOutput() + (5 * multiplier())); 28 context.setInput(context.getInput().substring(1)); 29 } 30 while(context.getInput().startsWith(one())) { 31 context.setOutput(context.getOutput() + (1 * multiplier())); 32 context.setInput(context.getInput().substring(1)); 33 } 34 35 } 36 public abstract String one(); 37 public abstract String four(); 38 public abstract String five(); 39 public abstract String nine(); 40 public abstract int multiplier(); 41 42 }

4.3.3.3.3 HundredExpression 01 package com.arnbe.patterns.behavioural.interpreter; 02 03 /** 04 * Implementation de la représentation d'un symbol 05 */ 06 07 public class HundredExpression extends Expression { 08 09 /* 10 * (non-Javadoc) 11 * @see com.arnbe.patterns.behavioural.interpreter.Expression#one() 12 */ 13 @Override 14 public String one() { 15 return "C"; 16 } 17 18 /* 19 * (non-Javadoc) 20 * @see com.arnbe.patterns.behavioural.interpreter.Expression#four() 21 */ 22 @Override 23 public String four() { 24 return "CD"; 25 } 26 27 28 29 /* 30 * (non-Javadoc) 31 * @see com.arnbe.patterns.behavioural.interpreter.Expression#five() 32 */ 33 @Override 34 public String five() { 35 return "D"; 36 } 37 38 /* 39 * (non-Javadoc) 40 * @see com.arnbe.patterns.behavioural.interpreter.Expression#nine() 41 */ 42 @Override 43 public String nine() { 44 return "CM"; 45 } 46 47 /* 48 * (non-Javadoc) 49 * @see com.arnbe.patterns.behavioural.interpreter.Expression#multiplier() 50 */ 51 @Override 52 public int multiplier() { 53 return 100; 54 } 55 }

Page 93: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 93 / 109

4.3.3.3.4 OneExpression 01 package com.arnbe.patterns.behavioural.interpreter; 02 03 /** 04 * Implementation de la représentation d'un symbol 05 */ 06 public class OneExpression extends Expression { 07 08 /* 09 * (non-Javadoc) 10 * @see com.arnbe.patterns.behavioural.interpreter.Expression#one() 11 */ 12 @Override 13 public String one() { 14 return "I"; 15 } 16 17 /* 18 * (non-Javadoc) 19 * @see com.arnbe.patterns.behavioural.interpreter.Expression#four() 20 */ 21 @Override 22 public String four() { 23 return "IV"; 24 } 25 26 /* 27 * (non-Javadoc) 28 * @see com.arnbe.patterns.behavioural.interpreter.Expression#five() 29 */ 30 @Override 31 public String five() { 32 return "V"; 33 } 34 35 /* 36 * (non-Javadoc) 37 * @see com.arnbe.patterns.behavioural.interpreter.Expression#nine() 38 */ 39 @Override 40 public String nine() { 41 return "IX"; 42 } 43 44 /* 45 * (non-Javadoc) 46 * @see com.arnbe.patterns.behavioural.interpreter.Expression#multiplier() 47 */ 48 @Override 49 public int multiplier() { 50 return 1; 51 } 52 }

4.3.3.3.5 TenExpression 01 package com.arnbe.patterns.behavioural.interpreter; 02 03 /** 04 * Implementation de la représentation d'un symbol 05 */ 06 public class TenExpression extends Expression { 07 08 /* 09 * (non-Javadoc) 10 * @see com.arnbe.patterns.behavioural.interpreter.Expression#one() 11 */ 12 @Override 13 public String one() { 14 return "X"; 15 } 16 17 /* 18 * (non-Javadoc) 19 * @see com.arnbe.patterns.behavioural.interpreter.Expression#four() 20 */ 21 @Override 22 public String four() { 23 return "XL"; 24 } 25 26 /* 27 * (non-Javadoc) 28 * @see com.arnbe.patterns.behavioural.interpreter.Expression#five() 29 */ 30 @Override 31 public String five() { 32 return "L"; 33 } 34 35 /* 36 * (non-Javadoc) 37 * @see com.arnbe.patterns.behavioural.interpreter.Expression#nine() 38 */ 39 @Override 40 public String nine() {

Page 94: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 94 / 109

41 return "XC"; 42 } 43 44 /* 45 * (non-Javadoc) 46 * @see com.arnbe.patterns.behavioural.interpreter.Expression#multiplier() 47 */ 48 @Override 49 public int multiplier() { 50 return 10; 51 } 52 }

4.3.3.3.6 ThousandExpression 01 package com.arnbe.patterns.behavioural.interpreter; 02 03 /** 04 * Implementation de la représentation d'un symbol 05 */ 06 public class ThousandExpression extends Expression { 07 08 /* 09 * (non-Javadoc) 10 * @see com.arnbe.patterns.behavioural.interpreter.Expression#one() 11 */ 12 @Override 13 public String one() { 14 return "M"; 15 } 16 17 /* 18 * (non-Javadoc) 19 * @see com.arnbe.patterns.behavioural.interpreter.Expression#four() 20 */ 21 @Override 22 public String four() { 23 return " "; 24 } 25 26 /* 27 * (non-Javadoc) 28 * @see com.arnbe.patterns.behavioural.interpreter.Expression#five() 29 */ 30 @Override 31 public String five() { 32 return " "; 33 } 34 35 /* 36 * (non-Javadoc) 37 * @see com.arnbe.patterns.behavioural.interpreter.Expression#nine() 38 */ 39 @Override 40 public String nine() { 41 return " "; 42 } 43 44 /* 45 * (non-Javadoc) 46 * @see com.arnbe.patterns.behavioural.interpreter.Expression#multiplier() 47 */ 48 @Override 49 public int multiplier() { 50 return 1000; 51 } 52 }

4.3.3.4 Iterator

4.3.3.4.1 AbstractCollection 1 package com.arnbe.patterns.behavioural.iterator; 2 3 public interface AbstractCollection { 4 5 public Iterator iterator(); 6 }

4.3.3.4.2 Collection 01 package com.arnbe.patterns.behavioural.iterator; 02 03 /** 04 * 05 */ 06 public class Collection implements AbstractCollection { 07 08 /** tableau des données */ 09 Object[] content = null; 10 11 /** 12 *

Page 95: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 95 / 109

13 */ 14 public Collection(Object[] object) { 15 super(); 16 this.content = object; 17 } 18 19 /** 20 * retourne un Iterator du contenu 21 */ 22 public Iterator iterator() { 23 return new RealIterator(this); 24 } 25 26 /** 27 * retourne le nombre d'éléments dans la collection 28 * 29 * @return 30 */ 31 public int size() { 32 return content.length; 33 } 34 35 /** 36 * retourne l'élément à la position donnée 37 * 38 * @param index 39 * @return 40 */ 41 public Object get(int index) { 42 return content[index]; 43 } 44 }

4.3.3.4.3 Iterator 1 package com.arnbe.patterns.behavioural.iterator; 2 3 public interface Iterator { 4 5 public Object next(); 6 7 public boolean hasNext(); 8 }

4.3.3.4.4 RealIterator 01 package com.arnbe.patterns.behavioural.iterator; 02 03 public class RealIterator implements Iterator { 04 05 private Collection aggregate = null; 06 private int current = 0; 07 08 /** 09 * constructeur 10 * 11 * @param aggregate 12 */ 13 public RealIterator(Collection aggregate) { 14 this.aggregate = aggregate; 15 } 16 17 /** 18 * retourne l'élément suivant dans l'itérateur 19 */ 20 public Object next() { 21 return aggregate.get(current++); 22 } 23 24 /** 25 * retourne true si il reste des éléments dans l'itérator 26 * false autrement 27 */ 28 public boolean hasNext() { 29 return current < aggregate.size(); 30 } 31 }

4.3.3.5 Mediator

4.3.3.5.1 AbstractChatroom 01 package com.arnbe.patterns.behavioural.mediator; 02 03 public interface AbstractChatroom { 04 05 /** 06 * enregistre un participant à la chatroom 07 * @param colleague 08 */ 09 public void register(Participant colleague); 10 11 /**

Page 96: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 96 / 109

12 * envoie un message aux participants de la chatroom 13 * @param from 14 * @param message 15 */ 16 public void send(String from, String message); 17 }

4.3.3.5.2 CounterStrikeChatRoom 02 package com.arnbe.patterns.behavioural.mediator; 03 04 import java.util.HashMap; 05 06 07 public class CounterStrikeChatroom implements AbstractChatroom { 08 09 /** 10 * @link aggregation 11 * @associates com.arnbe.patterns.behavioural.mediator.Fighter 12 * @directed directed 13 * @supplierCardinality 0..* 14 */ 15 private HashMap<String, Participant> participants = new HashMap<String, Participant>(); 16 17 /* 18 * (non-Javadoc) 19 * @see com.arnbe.patterns.behavioural.mediator.AbstractChatroom#register(com.arnbe.patterns.behavioural.mediator.Participant) 20 */ 21 public void register(Participant participant) { 22 participants.put(participant.getName(), participant); 23 participant.setChatRoom(this); 24 } 25 26 /* 27 * (non-Javadoc) 28 * @see com.arnbe.patterns.behavioural.mediator.AbstractChatroom#send(java.lang.String, java.lang.String) 29 */ 30 public void send(String from, String message) { 31 for (Participant participant : participants.values()) { 32 //on envoie pas le message à l'émetteur 33 if(!participant.getName().equals(from)) 34 participant.receive(from, message); 35 } 36 } 37 }

4.3.3.5.3 Fighter 01 package com.arnbe.patterns.behavioural.mediator; 02 03 public class Fighter extends Participant { 04 05 /** 06 * constructeur 07 * @param name 08 */ 09 public Fighter(String name) { 10 super(name); 11 } 12 13 /* 14 * (non-Javadoc) 15 * @see com.arnbe.patterns.behavioural.mediator.Participant#receive(java.lang.String, java.lang.String) 16 */ 17 @Override 18 public void receive(String from, String message) { 19 System.out.println(name + " received \"" + message + "\" from " + from); 20 //code... 21 22 } 23 }

4.3.3.5.4 Participant 01 package com.arnbe.patterns.behavioural.mediator; 02 03 public abstract class Participant { 04 05 private AbstractChatroom chatRoom = null; 06 protected String name = null; 07 08 /** 09 * constructeur 10 * @param name 11 */ 12 protected Participant(String name) { 13 this.name = name; 14 } 15 16 /** 17 * affecte la chatRoom 18 * @return 19 */ 20 public void setChatRoom(AbstractChatroom chatRoom) {

Page 97: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 97 / 109

21 this.chatRoom = chatRoom; 22 } 23 24 /** 25 * envoie un message 26 * @param message 27 */ 28 public void send(String message) { 29 chatRoom.send(name, message); 30 } 31 32 /** 33 * @return le nom de la chat room 34 */ 35 public String getName() { 36 return name; 37 } 38 39 /** 40 * recoie un message des participants 41 * @param message 42 */ 43 public abstract void receive(String from, String message); 44 }

4.3.3.6 Memento

4.3.3.6.1 Car 01 package com.arnbe.patterns.behavioural.memento; 02 03 /** 04 * Classe dont les données doivent être sauvegardées 05 */ 06 public class Car { 07 08 private String model = null; 09 private boolean airConditionning = false; 10 private boolean slidingRoof = false; 11 12 /** 13 * constructeur 14 * @param model 15 */ 16 public Car(String model) { 17 super(); 18 this.model = model; 19 } 20 21 /** 22 * @return 23 */ 24 public boolean isAirConditionning() { 25 return airConditionning; 26 } 27 28 /** 29 * @param airConditionning 30 */ 31 public void setAirConditionning(boolean airConditionning) { 32 this.airConditionning = airConditionning; 33 } 34 35 /** 36 * @return 37 */ 38 public boolean isSlidingRoof() { 39 return slidingRoof; 40 } 41 42 /** 43 * @param slidingRoof 44 */ 45 public void setSlidingRoof(boolean slidingRoof) { 46 this.slidingRoof = slidingRoof; 47 } 48 49 50 /** 51 * crée une sauvegarde de l'état des données à un instant 52 * @return 53 */ 54 public CarMemento createMemento() { 55 return new CarMemento(airConditionning, slidingRoof); 56 } 57 58 /** 59 * restore les données à partir du memento 60 * @param memento 61 */ 62 public void restoreMemento(CarMemento memento) { 63 if (memento instanceof CarMemento) { 64 CarMemento cm = memento; 65 this.airConditionning = cm.isAirConditionning(); 66 this.slidingRoof = cm.isSlidingRoof(); 67 }

Page 98: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 98 / 109

68 } 69 }

4.3.3.6.2 CarMemento 01 package com.arnbe.patterns.behavioural.memento; 02 03 /** 04 * Ne sauvegarde que les options du véhicule 05 */ 06 public class CarMemento { 07 08 private boolean airConditionning = false; 09 private boolean slidingRoof = false; 10 11 /** 12 * constructeur 13 * @param airConditionning 14 * @param slidingRoof 15 */ 16 public CarMemento(boolean airConditionning, boolean slidingRoof) { 17 super(); 18 this.airConditionning = airConditionning; 19 this.slidingRoof = slidingRoof; 20 } 21 /** 22 * @return 23 */ 24 public boolean isAirConditionning() { 25 return airConditionning; 26 } 27 /** 28 * @param airConditionning 29 */ 30 public void setAirConditionning(boolean airConditionning) { 31 this.airConditionning = airConditionning; 32 } 33 /** 34 * @return 35 */ 36 public boolean isSlidingRoof() { 37 return slidingRoof; 38 } 39 /** 40 * @param slidingRoof 41 */ 42 public void setSlidingRoof(boolean slidingRoof) { 43 this.slidingRoof = slidingRoof; 44 } 45 }

4.3.3.6.3 CarMemory 01 package com.arnbe.patterns.behavioural.memento; 02 03 /** 04 * maintient une poignée vers le memento 05 * 06 */ 07 public class CarMemory { 08 09 private CarMemento memento = null; 10 11 /** 12 * @return 13 */ 14 public CarMemento getMemento() { 15 return memento; 16 } 17 18 /** 19 * @param memento 20 */ 21 public void setMemento(CarMemento memento) { 22 this.memento = memento; 23 } 24 }

4.3.3.7 Observer

4.3.3.7.1 BerkShire 01 package com.arnbe.patterns.behavioural.observer; 02 03 public class BerkShire implements Investor { 04 05 private String name = null; 06 07 /** 08 * constructeur 09 * @param name 10 */ 11 public BerkShire(String name) {

Page 99: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 99 / 109

12 this.name = name; 13 } 14 15 /* 16 * (non-Javadoc) 17 * @see com.arnbe.patterns.behavioural.observer.Investor#update(com.arnbe.patterns.behavioural.observer.Stock) 18 */ 19 public void update(Stock stock) { 20 System.out.println("Notify : " + name + " of " + stock.getSymbol() + " change to " + stock.getPrice()); 21 } 22 }

4.3.3.7.2 IBM 1 package com.arnbe.patterns.behavioural.observer; 2 3 public class IBM extends Stock { 4 5 public IBM(String symbol, double price) { 6 super(symbol, price); 7 } 8 }

4.3.3.7.3 Investor 1 package com.arnbe.patterns.behavioural.observer; 2 3 public interface Investor { 4 5 void update(Stock stock); 6 }

4.3.3.7.4 Sorros 01 package com.arnbe.patterns.behavioural.observer; 02 03 public class Sorros implements Investor { 04 05 private String name = null; 06 07 /** 08 * constructeur 09 * @param name 10 */ 11 public Sorros(String name) { 12 this.name = name; 13 } 14 15 /* 16 * (non-Javadoc) 17 * @see com.arnbe.patterns.behavioural.observer.Investor#update(com.arnbe.patterns.behavioural.observer.Stock) 18 */ 19 public void update(Stock stock) { 20 System.out.println("Notify : " + name + " of " + stock.getSymbol() + " change to " + stock.getPrice()); 21 } 22 }

4.3.3.7.5 Stock 01 package com.arnbe.patterns.behavioural.observer; 02 03 import java.util.ArrayList; 04 import java.util.List; 05 06 public class Stock { 07 08 private List<Investor> investors = new ArrayList<Investor>(); 09 10 protected String symbol; 11 12 protected double price; 13 14 /** 15 * constructeur 16 * 17 * @param price 18 * @param symbol 19 */ 20 public Stock(String symbol, double price) { 21 super(); 22 this.price = price; 23 this.symbol = symbol; 24 } 25 26 /** 27 * attache un observateur 28 * 29 * @param observer 30 */ 31 public void attach(Investor observer) { 32 investors.add(observer); 33 }

Page 100: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 100 / 109

34 35 /** 36 * détache un observateur 37 * 38 * @param observer 39 */ 40 public void detach(Investor observer) { 41 investors.remove(observer); 42 } 43 44 /** 45 * notifie tous les observateurs 46 */ 47 public void notifyObservers() { 48 for (Investor investor : investors) { 49 investor.update(this); 50 } 51 } 52 53 /** 54 * retourne le prix 55 * @return 56 */ 57 public double getPrice() { 58 return price; 59 } 60 61 /** 62 * change le prix 63 * @param price 64 */ 65 public void setPrice(double price) { 66 this.price = price; 67 notifyObservers(); 68 } 69 70 /** 71 * retourne le nom de l'action 72 * @return 73 */ 74 public String getSymbol() { 75 return symbol; 76 } 77 }

4.3.3.8 State

4.3.3.8.1 Agency 01 package com.arnbe.patterns.behavioural.state; 02 03 /** 04 * représente une agence 05 * 06 */ 07 public class Agency { 08 09 private String name = null; 10 11 /** 12 * constructeur 13 * @param name 14 */ 15 public Agency(String name) { 16 super(); 17 if(name==null) { 18 throw new IllegalArgumentException("name can't be null"); 19 } 20 this.name = name; 21 } 22 23 /** 24 * retourne le nom de l'agence 25 * @return 26 */ 27 public String getName() { 28 return name; 29 } 30 31 /* (non-Javadoc) 32 * @see java.lang.Object#equals(java.lang.Object) 33 */ 34 @Override 35 public boolean equals(Object arg) { 36 if(arg instanceof Agency) { 37 return ((Agency)arg).getName().equals(name); 38 } 39 return false; 40 } 41 42 /* (non-Javadoc) 43 * @see java.lang.Object#hashCode() 44 */ 45 @Override 46 public int hashCode() { 47 return name.hashCode();

Page 101: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 101 / 109

48 } 49 }

4.3.3.8.2 Bank 01 package com.arnbe.patterns.behavioural.state; 02 03 import java.util.ArrayList; 04 import java.util.List; 05 06 /** 07 * représente une banque 08 * 09 */ 10 public class Bank { 16 private List<Agency> agencies = null; 17 18 private BankState state = null; 19 20 private String name = null; 21 22 /** 23 * constructeur 24 * @param name 25 */ 26 public Bank(String name) { 27 super(); 28 //état par défaut 29 this.state = new UnInitializeState(this); 30 // initialise la liste des agences 31 this.agencies = new ArrayList<Agency>(); 32 setName(name); 33 } 34 35 /** 36 * ajoute une agence 37 * @param agency 38 */ 39 public void addAgency(Agency agency) { 40 state.addAgency(agency); 41 } 42 43 /** 44 * supprime une agence 45 * @param agency 46 */ 47 public void removeAgency(Agency agency) { 48 state.removeAgency(agency); 49 } 50 51 /** 52 * retourne le nom de l'état en cours 53 * @return 54 */ 55 public String getStateName() { 56 return state.getStateName(); 57 } 58 59 /** 60 * retourne le nom de l'agence 61 * @return 62 */ 63 public String getName() { 64 return name; 65 } 66 67 /** 68 * affecte le nom de la banque 69 * @param name 70 */ 71 public void setName(String name) { 72 state.setName(name); 73 } 74 75 /** 76 * définie l'état en cours 77 * @param state 78 */ 79 void setState(BankState state) { 80 this.state = state; 81 } 82 83 /** 84 * retourne la liste des agences 85 * @return 86 */ 87 List<Agency> getAgencies() { 88 return agencies; 89 } 90 }

4.3.3.8.3 BankState 01 package com.arnbe.patterns.behavioural.state; 02 03 /**

Page 102: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 102 / 109

04 * Classe abstraite des états 05 */ 06 public abstract class BankState { 07 08 protected Bank bank = null; 09 10 /** 11 * supprime une agence 12 * @param agency 13 */ 14 public abstract void removeAgency(Agency agency); 15 16 /** 17 * ajoute une agence 18 * @param agency 19 */ 20 public abstract void addAgency(Agency agency); 21 22 /** 23 * retourne le nom de l'état 24 */ 25 /* 26 * (non-Javadoc) 27 * @see com.arnbe.patterns.behavioural.state.BankState#getStateName() 28 */ 29 public String getStateName() { 30 return getClass().getSimpleName(); 31 } 32 33 /** 34 * change le nom de la banque 35 * @param name 36 */ 37 public void setName(String name) { 38 if(name!=null) { 39 bank.setState(new InitializeState(bank)); 40 } else { 41 bank.setState(new UnInitializeState(bank)); 42 } 43 } 44 }

4.3.3.8.4 HaveAgencyState 01 package com.arnbe.patterns.behavioural.state; 02 03 /** 04 * Etat ou la banque a un nom et des agences 05 */ 06 07 public class HaveAgencyState extends BankState { 08 09 /** 10 * constructeur 11 * @param bank 12 */ 13 public HaveAgencyState(Bank bank) { 14 super(); 15 if (bank == null) { 16 throw new IllegalArgumentException("Bank must be initilized"); 17 } 18 this.bank = bank; 19 } 20 21 /* 22 * (non-Javadoc) 23 * @see com.arnbe.patterns.behavioural.state.BankState#removeAgency(com.arnbe.patterns.behavioural.state.Agency) 24 */ 25 @Override 26 public void removeAgency(Agency agency) { 27 if (agency == null) { 28 throw new IllegalArgumentException("Agency can't be null"); 29 } 30 if (!bank.getAgencies().contains(agency)) { 31 throw new IllegalArgumentException("can't delete unknown agency"); 32 } 33 bank.getAgencies().remove(agency); 34 //si il ne reste plus d'agence on change d'état 35 if (bank.getAgencies().size() == 0) { 36 bank.setState(new InitializeState(bank)); 37 } 38 } 39 40 /* 41 * (non-Javadoc) 42 * @see com.arnbe.patterns.behavioural.state.BankState#addAgency(com.arnbe.patterns.behavioural.state.Agency) 43 */ 44 @Override 45 public void addAgency(Agency agency) { 46 if (agency == null) { 47 throw new IllegalArgumentException("Agency can't be null"); 48 } 49 bank.getAgencies().add(agency); 50 } 51 52 }

Page 103: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 103 / 109

4.3.3.8.5 InitializeState 01 package com.arnbe.patterns.behavioural.state; 02 03 /** 04 * La banque possède un nom mais pas d'agences 05 */ 06 public class InitializeState extends BankState { 07 08 /** 09 * constructeur 10 * @param bank 11 */ 12 public InitializeState(Bank bank) { 13 super(); 14 if (bank == null) { 15 throw new IllegalArgumentException("Bank must be initilized"); 16 } 17 this.bank = bank; 18 } 19 20 /* 21 * (non-Javadoc) 22 * @see com.arnbe.patterns.behavioural.state.BankState#removeAgency(com.arnbe.patterns.behavioural.state.Agency) 23 */ 24 @Override 25 public void removeAgency(Agency agency) { 26 throw new IllegalStateException("Add agency before remove"); 27 } 28 29 /* 30 * (non-Javadoc) 31 * @see com.arnbe.patterns.behavioural.state.BankState#addAgency(com.arnbe.patterns.behavioural.state.Agency) 32 */ 33 @Override 34 public void addAgency(Agency agency) { 35 if (agency == null) { 36 throw new IllegalArgumentException("Agency can't be null"); 37 } 38 bank.getAgencies().add(agency); 39 //changement d'état 40 bank.setState(new HaveAgencyState(bank)); 41 } 42 }

4.3.3.8.6 UnInitializeState 01 package com.arnbe.patterns.behavioural.state; 02 03 /** 04 * Etat non initialisé de la banque 05 * elle ne possède pas de nom 06 * donc on ne peut ajouter des agences 07 */ 08 09 public class UnInitializeState extends BankState { 10 11 /** 12 * constructeur 13 * @param bank 14 */ 15 public UnInitializeState(Bank bank) { 16 super(); 17 if (bank == null) { 18 throw new IllegalArgumentException("Bank must be initilized"); 19 } 20 this.bank = bank; 21 } 22 23 /* 24 * (non-Javadoc) 25 * @see com.arnbe.patterns.behavioural.state.BankState#removeAgency(com.arnbe.patterns.behavioural.state.Agency) 26 */ 27 @Override 28 public void removeAgency(Agency agency) { 29 throw new IllegalStateException("Agency must be initialized before"); 30 } 31 32 /* 33 * (non-Javadoc) 34 * @see com.arnbe.patterns.behavioural.state.BankState#addAgency(com.arnbe.patterns.behavioural.state.Agency) 35 */ 36 @Override 37 public void addAgency(Agency agency) { 38 throw new IllegalStateException("Agency must be initialized before"); 39 } 40 }

4.3.3.9 Strategy

4.3.3.9.1 InflateStrategy 01 package com.arnbe.patterns.behavioural.strategy; 02 03 import java.io.IOException;

Page 104: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 104 / 109

04 import java.io.InputStream; 05 import java.io.OutputStream; 06 07 /** 08 * Interface des strategies 09 */ 10 public interface InflateStrategy { 11 12 /** 13 * retourne le nom de l'algorythme 14 * @return 15 */ 16 public abstract String getName(); 17 18 /** 19 * conpresse le flux d'entrée vers le flux de sortie 20 * @param file 21 * @param in 22 * @param out 23 * @throws IOException 24 */ 25 public abstract void inflate(String file, InputStream in, OutputStream out) throws IOException; 26 }

4.3.3.9.2 RarStrategy 01 package com.arnbe.patterns.behavioural.strategy; 02 03 import java.io.InputStream; 04 import java.io.IOException; 05 import java.io.OutputStream; 06 public class RarStrategy implements InflateStrategy { 07 08 public String getName() { 09 return "Rar"; 10 } 11 12 public void inflate(String file, InputStream in, OutputStream out) throws IOException { 13 // code... 14 15 } 16 17 }

4.3.3.9.3 TarGunzipStrategy 01 package com.arnbe.patterns.behavioural.strategy; 02 03 import java.io.IOException; 04 import java.io.InputStream; 05 import java.io.OutputStream; 06 07 public class TarGunzipStrategy implements InflateStrategy { 08 09 public String getName() { 10 return "TarGunZip"; 11 } 12 13 public void inflate(String file, InputStream in, OutputStream out) throws IOException { 14 // code... 15 16 } 17 18 }

4.3.3.9.4 ZipStrategy 01 package com.arnbe.patterns.behavioural.strategy; 02 03 import java.io.File; 04 import java.io.IOException; 05 import java.io.InputStream; 06 import java.io.OutputStream; 07 import java.util.zip.ZipEntry; 08 import java.util.zip.ZipOutputStream; 09 10 /** 11 * Implements an algorithm using the Strategy interface. 12 */ 13 14 public class ZipStrategy implements InflateStrategy { 15 16 /* 17 * (non-Javadoc) 18 * @see com.arnbe.patterns.behavioural.strategy.InflateStrategy#getName() 19 */ 20 public String getName() { 21 return "Zip"; 22 } 23 24 /* 25 * (non-Javadoc) 26 * @see com.arnbe.patterns.behavioural.strategy.InflateStrategy#inflate(java.lang.String, java.io.InputStream, java.io.OutputStream) 27 */

Page 105: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 105 / 109

28 public void inflate(String file, InputStream in, OutputStream out) throws IOException { 29 ZipOutputStream zOut = new ZipOutputStream(out); 30 zOut.putNextEntry(new ZipEntry(new File(file).getName())); 31 int c; 32 while((c = in.read()) != -1) { 33 zOut.write(c); 34 } 35 zOut.closeEntry(); 36 zOut.close(); 37 } 38 }

4.3.3.10 Template Method

4.3.3.10.1 DescSorter 01 package com.arnbe.patterns.behavioural.templatemethod; 02 03 /** 04 * Implements the primitive operations to carry out 05 * subclass-specific steps of the algorithm. 06 */ 07 08 public class DescSorter extends Sorter { 09 10 /* 11 * (non-Javadoc) 12 * @see com.arnbe.patterns.behavioural.templatemethod.Sorter#compare(java.lang.String, java.lang.String) 13 */ 14 @Override 15 public boolean compare(int item1, int item2) { 16 return item1 < item2; 17 } 18 19 /* 20 * (non-Javadoc) 21 * @see com.arnbe.patterns.behavioural.templatemethod.Sorter#getDescription() 22 */ 23 @Override 24 public String getDescription() { 25 return "DescSorter"; 26 } 27 28 }

4.3.3.10.2 Sorter 01 package com.arnbe.patterns.behavioural.templatemethod; 02 03 /** 04 * @role __TemplateContext 05 */ 06 07 public abstract class Sorter { 08 09 /** 10 * méthode de comparaison 11 */ 12 protected abstract boolean compare(int item1, int item2); 13 14 /** 15 * retourne la description du trieur 16 */ 17 public abstract String getDescription(); 18 19 /** 20 * tri le tableau d'entier 21 */ 22 public void sort(int[] ints) { 23 for (int i = 0; i < (ints.length - 1); i++) { 24 if(compare(ints[i], ints[i+1])) { 25 //permutation 26 int tmp = ints[i]; 27 ints[i] = ints[i+1]; 28 ints[i+1] = tmp; 29 } 30 } 31 } 32 }

4.3.3.11 Visitor

4.3.3.11.1 Agency 01 package com.arnbe.patterns.behavioural.visitor; 02 /** 03 * représente une agence 04 * 05 */ 06 public class Agency { 07 08 private String name = null; 09 10 /**

Page 106: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 106 / 109

11 * constructeur 12 * @param name 13 */ 14 public Agency(String name) { 15 super(); 16 this.name = name; 17 } 18 19 /** 20 * retourne le nom de l'agence 21 * @return 22 */ 23 public String getName() { 24 return name; 25 } 26 27 /* (non-Javadoc) 28 * @see java.lang.Object#equals(java.lang.Object) 29 */ 30 @Override 31 public boolean equals(Object arg) { 32 if (arg instanceof Agency) { 33 return ((Agency) arg).getName().equals(name); 34 } 35 return false; 36 } 37 38 /* (non-Javadoc) 39 * @see java.lang.Object#hashCode() 40 */ 41 @Override 42 public int hashCode() { 43 return name.hashCode(); 44 } 45 }

4.3.3.11.2 Bank 01 package com.arnbe.patterns.behavioural.visitor; 02 03 import java.util.ArrayList; 04 import java.util.List; 05 06 /** 07 * représente une banque 08 * 09 */ 10 public class Bank implements Element { 11 12 protected List<Agency> agencies = null; 13 14 protected String name = null; 15 16 /** 17 * constructeur 18 * 19 * @param name 20 */ 21 public Bank(String name) { 22 super(); 23 // initialise la liste des agences 24 this.agencies = new ArrayList<Agency>(); 25 this.name = name; 26 } 27 28 /** 29 * ajoute une agence 30 * 31 * @param agency 32 */ 33 public void addAgency(Agency agency) { 34 agencies.add(agency); 35 } 36 37 /** 38 * supprime une agence 39 * 40 * @param agency 41 */ 42 public void removeAgency(Agency agency) { 43 agencies.remove(agency); 44 } 45 46 /** 47 * retourne le nom de l'agence 48 * 49 * @return 50 */ 51 public String getName() { 52 return name; 53 } 54 55 /** 56 * @return 57 */ 58 protected List<Agency> getAgencies() { 59 return agencies; 60 }

Page 107: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 107 / 109

61 62 /* 63 * (non-Javadoc) 64 * @see com.arnbe.patterns.behavioural.visitor.Element#accept(com.arnbe.patterns.behavioural.visitor.BankVisitor) 65 */ 66 public void accept(BankVisitor visitor) { 67 visitor.visit(this); 68 } 69 }

4.3.3.11.3 BankVisitor 01 package com.arnbe.patterns.behavioural.visitor; 02 03 /** 04 * interface visiteur 05 */ 06 public interface BankVisitor { 07 08 /** 09 * méthode de visite pour l'élément Bank 10 */ 11 void visit(Bank element); 12 }

4.3.3.11.4 CSVBankVisitor 01 package com.arnbe.patterns.behavioural.visitor; 02 03 /** 04 * classe générant du CSV à partir d'un objet banque 05 * 06 */ 07 public class CSVBankVisitor implements BankVisitor { 08 09 /* 10 * (non-Javadoc) 11 * @see com.arnbe.patterns.behavioural.visitor.BankVisitor#visit(com.arnbe.patterns.behavioural.visitor.Bank) 12 */ 13 public void visit(Bank bank) { 14 StringBuffer buf = new StringBuffer(); 15 16 for (Agency agency : bank.getAgencies()) { 17 buf.append(bank.getName()) 18 .append(';') 19 .append(agency.getName()) 20 .append('\n'); 21 } 22 23 System.out.println(buf.toString()); 24 } 25 }

4.3.3.11.5 Element 1 package com.arnbe.patterns.behavioural.visitor; 2 3 public interface Element { 4 5 /** 6 * accept un visiteur 7 */ 8 void accept(BankVisitor visitor); 9 }

4.3.3.11.6 XMLBankVisitor 01 package com.arnbe.patterns.behavioural.visitor; 02 03 /** 04 * classe générant du XML à partir d'un objet banque 05 * 06 */ 07 public class XMLBankVisitor implements BankVisitor { 08 09 /* 10 * (non-Javadoc) 11 * @see com.arnbe.patterns.behavioural.visitor.BankVisitor#visit(com.arnbe.patterns.behavioural.visitor.Bank) 12 */ 13 public void visit(Bank bank) { 14 StringBuffer buf = new StringBuffer(); 15 buf.append("<bank name=\"") 16 .append(bank.getName()) 17 .append("\">\n"); 18 19 for (Agency agency : bank.getAgencies()) { 20 buf.append(" <agency name=\"") 21 .append(agency.getName()) 22 .append("\"/>\n"); 23 } 24 25 buf.append("</bank>");

Page 108: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 108 / 109

26 System.out.println(buf.toString()); 27 } 28 }

Page 109: java entreprise et les design patterns 2 0 2007 french

Conception objet et Design Patterns avec Java

Arnaud BARBE 109 / 109

5 L’AUTEUR Nom : BARBE Prénom : Arnaud Email : [email protected] PROFIL GENERAL

� Accompagnement et encadrement d’équipes de développement. � Analyse et conception de cahiers des charges. � Etudes techniques et définition d’architecture I-NET. � Réalisation d’études préalables et qualification de produits et solutions techniques. � Réponse à appel d’offre. � Rédaction de documents techniques. � Réalisation de projets, sites et portails Internet.

PROFIL TECHNIQUE

� Expert java/JEE (7 ans d’expérience). � Forte connaissance des principaux serveurs d’applications du marché (Websphere,

Weblogic, JBoss, Jonas, Borland Enterprise Server). � Bonne connaissance des micros containers (HiveMind). � Forte connaissance des systèmes ORM (Hibernate, JDO, DAO, …) et des bases de

données (Oracle, DB2, MySQL). � Forte connaissance des Design Patterns et Patterns JEE. � Veille technologique continu. � Expertise technique. � Sensible à l’approche XP.