You are on page 1of 109

Java entreprise et les design

patterns

Version 2.0

Arnaud BARBE Page 1 sur 109


Conception objet et Design Patterns avec Java

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
Arnaud BARBE 2 / 109
Conception objet et Design Patterns avec Java

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

Arnaud BARBE 3 / 109


Conception objet et Design Patterns avec Java

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

Arnaud BARBE 4 / 109


Conception objet et Design Patterns avec Java

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

Arnaud BARBE 5 / 109


Conception objet et Design Patterns avec Java

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.

Arnaud BARBE 6 / 109


Conception objet et Design Patterns avec Java

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.

Arnaud BARBE 7 / 109


Conception objet et Design Patterns avec Java

2.5 General Responsability Assignement Software


Patterns [GRASP]
2.5.1Pré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.2Patterns 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);

Arnaud BARBE 8 / 109


Conception objet et Design Patterns avec Java

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.
Arnaud BARBE 9 / 109
Conception objet et Design Patterns avec Java

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.
Arnaud BARBE 10 / 109
Conception objet et Design Patterns avec Java

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.3Patterns 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 :

Arnaud BARBE 11 / 109


Conception objet et Design Patterns avec Java

 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() ».

Arnaud BARBE 12 / 109


Conception objet et Design Patterns avec Java

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é

Arnaud BARBE 13 / 109


Conception objet et Design Patterns avec Java

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.

Arnaud BARBE 14 / 109


Conception objet et Design Patterns avec Java

2.6 Design principles


2.6.1Pré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.2Gestion 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.

Arnaud BARBE 15 / 109


Conception objet et Design Patterns avec Java

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


Arnaud BARBE 16 / 109
Conception objet et Design Patterns avec Java

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.

Arnaud BARBE 17 / 109


Conception objet et Design Patterns avec Java

Figure 16 Répartition plus homogène

2.6.3Organisation 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).

Arnaud BARBE 18 / 109


Conception objet et Design Patterns avec Java

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.

Arnaud BARBE 19 / 109


Conception objet et Design Patterns avec Java

2.6.4Gestion 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.

Arnaud BARBE 20 / 109


Conception objet et Design Patterns avec Java

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.5Conclusion
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.

Arnaud BARBE 21 / 109


Conception objet et Design Patterns avec Java

2.7 Gang Of Four [GOF]


2.7.1Pré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.2Patterns 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.
Arnaud BARBE 22 / 109
Conception objet et Design Patterns avec Java

2.7.2.1.2.2 UML

Figure 24 UML implémentation Abstract Factory

Correspondance diagramme
théorique implémentation
AbstractFactory Factory
ConcreteFactory TruckFactory,
PlaneFactory
AbstractProduct Vehicule
AbstractProductImpl Truck, Plane
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


Arnaud BARBE 23 / 109
Conception objet et Design Patterns avec Java

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

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

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é.

Arnaud BARBE 24 / 109


Conception objet et Design Patterns avec Java

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

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

2.7.2.3.2.3 Utilisation
Cf. méthode sampleUse().

Arnaud BARBE 25 / 109


Conception objet et Design Patterns avec Java

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

Correspondance diagramme
théorique implémentation
Prototype Prototype
ConcretePrototype Plane, Truck
2.7.2.4.2.3 Utilisation
//code...
Prototype otherPlane = plane.clone();

Arnaud BARBE 26 / 109


Conception objet et Design Patterns avec Java

//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

Correspondance diagramme
théorique implémentation
Singleton Singleton

2.7.2.5.2.3 Utilisation
Singleton single = Singleton.getInstance();

Arnaud BARBE 27 / 109


Conception objet et Design Patterns avec Java

2.7.3Patterns 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

Arnaud BARBE 28 / 109


Conception objet et Design Patterns avec Java

Correspondance diagramme
théorique implémentation
Target Iterator
Adapter IteratorEnumerationAdapter
Adaptee Enumeration
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.

Arnaud BARBE 29 / 109


Conception objet et Design Patterns avec Java

2.7.3.2.2.2 UML

Figure 36 UML implémentation Bridge

Correspondance diagramme
théorique implémentation
Abstraction AbstractCustomer
RefinedAbstraction Customers
Implementor DataObject
ConcreteImplementor InMemoryData
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.

Arnaud BARBE 30 / 109


Conception objet et Design Patterns avec Java

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

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

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

Arnaud BARBE 31 / 109


Conception objet et Design Patterns avec Java

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.

Arnaud BARBE 32 / 109


Conception objet et Design Patterns avec Java

2.7.3.4.2.2 UML

Figure 40 UML implémentation Decorator

Correspondance diagramme
théorique implémentation
Component LibraryItem
ConcreteComponent Book, Video
Decorator Decorator
ConcreteDecorator Borrowable
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.

Arnaud BARBE 33 / 109


Conception objet et Design Patterns avec Java

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

Correspondance diagramme
théorique implémentation
Facade Compiler
Subsystem CodeGenerator, Scanner,
Parser
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.

Arnaud BARBE 34 / 109


Conception objet et Design Patterns avec Java

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

Arnaud BARBE 35 / 109


Conception objet et Design Patterns avec Java

Figure 44 UML implémentation FlyWeight

Correspondance diagramme
théorique implémentation
Flyweight GraphicalItem
ConcreteFlyweight Character, CharacterA, CharacterB,
CharacterZ
Unshared Row
ConcreteFlyweight
FlyweightContext Context, PageContext
FlyweightFactory GraphicalItemFactory

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);

Arnaud BARBE 36 / 109


Conception objet et Design Patterns avec Java

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

Arnaud BARBE 37 / 109


Conception objet et Design Patterns avec Java

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

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.4Patterns 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.

Arnaud BARBE 38 / 109


Conception objet et Design Patterns avec Java

2.7.4.1.2.2 UML

Figure 48 UML implémentation Chain of Responsability

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

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.

Arnaud BARBE 39 / 109


Conception objet et Design Patterns avec Java

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

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

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

Arnaud BARBE 40 / 109


Conception objet et Design Patterns avec Java

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

Arnaud BARBE 41 / 109


Conception objet et Design Patterns avec Java

Correspondance diagramme
théorique implémentation
Context Context
AbstractExpression Expression
TerminalExpression ThousandExpression,
HundredExpression,
TenExpression,
OneExpression
NonTerminalExpression

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

Arnaud BARBE 42 / 109


Conception objet et Design Patterns avec Java

Figure 54 UML implémentation Iterator

Correspondance diagramme
théorique implémentation
Iterator Iterator
ConcreteIterator RealIterator
Agregate AbstractCollection
ConcreteAgregate Collection
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

Arnaud BARBE 43 / 109


Conception objet et Design Patterns avec Java

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

Correspondance diagramme
théorique implémentation
Mediator AbstractChatroom
Colleague Participant
ConcreteMediator CounterStrikeChatRoom
ConcreateColleague Fighter
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.

Arnaud BARBE 44 / 109


Conception objet et Design Patterns avec Java

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

Correspondance diagramme
théorique implémentation
Originator Car
Memento CarMemento
CareTaker CarMemory
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

Arnaud BARBE 45 / 109


Conception objet et Design Patterns avec Java

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

Correspondance diagramme
théorique implémentation
Subject Stock
ConcreteSubject IBM
Observer Investor
ConcreteObserver Sorros, BerkShire
2.7.4.7.2.3 Utilisation
//crée les investisseurs
Sorros s = new Sorros("Sorros");
BerkShire b = new BerkShire("Berkshire");

Arnaud BARBE 46 / 109


Conception objet et Design Patterns avec Java

//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

Arnaud BARBE 47 / 109


Conception objet et Design Patterns avec Java

Figure 62 UML implémentation State

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

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.

Arnaud BARBE 48 / 109


Conception objet et Design Patterns avec Java

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

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

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();

Arnaud BARBE 49 / 109


Conception objet et Design Patterns avec Java

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

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

2.7.4.10.2.3 Utilisation

Sorter sorter = new DescSorter();

int[] ints = {1,3,2};

Arnaud BARBE 50 / 109


Conception objet et Design Patterns avec Java

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

Arnaud BARBE 51 / 109


Conception objet et Design Patterns avec Java

2.7.4.11.2.2 UML

Correspondance diagramme
théorique implémentation
Visitor BankVisitor
ConcreteVisitor XMLBankVisitor,
CSVBankVisitor
Element Element
ConcreteElement Bank

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.1Business 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.

Arnaud BARBE 52 / 109


Conception objet et Design Patterns avec Java

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.2Service 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
Arnaud BARBE 53 / 109
Conception objet et Design Patterns avec Java

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

Arnaud BARBE 54 / 109


Conception objet et Design Patterns avec Java

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,…)
...

Arnaud BARBE 55 / 109


Conception objet et Design Patterns avec Java

2.8.3Session 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.

Arnaud BARBE 56 / 109


Conception objet et Design Patterns avec Java

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.4Message 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

Arnaud BARBE 57 / 109


Conception objet et Design Patterns avec Java

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.5Transfer 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 :

Arnaud BARBE 58 / 109


Conception objet et Design Patterns avec Java

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;
}

Arnaud BARBE 59 / 109


Conception objet et Design Patterns avec Java

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.6Transfer 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.

Arnaud BARBE 60 / 109


Conception objet et Design Patterns avec Java

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.7Value 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.

Arnaud BARBE 61 / 109


Conception objet et Design Patterns avec Java

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.8Data 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).

Arnaud BARBE 62 / 109


Conception objet et Design Patterns avec Java

Figure 81 Dao proposant plusieurs implémentations

2.8.9Conclusion
 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.

Arnaud BARBE 63 / 109


Conception objet et Design Patterns avec Java

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.1Code 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.2Optimisation
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.3Copier/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.4Variables
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.5Concaté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.6Cohé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.

Arnaud BARBE 64 / 109


Conception objet et Design Patterns avec Java

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.7Typage 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();

Arnaud BARBE 65 / 109


Conception objet et Design Patterns avec Java

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

3.1.8Synchronized
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++;
}
}

Arnaud BARBE 66 / 109


Conception objet et Design Patterns avec Java

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.9Documentation
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

Arnaud BARBE 67 / 109


Conception objet et Design Patterns avec Java

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.1Ressources
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.2Hé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 !!


Arnaud BARBE 68 / 109
Conception objet et Design Patterns avec Java

3.2.3Habitude
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.4Simplicité
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.1Duré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.2Allocation 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.1The 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.

Arnaud BARBE 69 / 109


Conception objet et Design Patterns avec Java

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.2Swiss 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.3Lava 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.4No 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.5Proliferation 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
Arnaud BARBE 70 / 109
Conception objet et Design Patterns avec Java

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.6Golden 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.7Spaghetti 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.8Reinvent 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.

Arnaud BARBE 71 / 109


Conception objet et Design Patterns avec Java

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.1Cré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 {

Arnaud BARBE 72 / 109


Conception objet et Design Patterns avec Java

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 }

Arnaud BARBE 73 / 109


Conception objet et Design Patterns avec Java

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 }

Arnaud BARBE 74 / 109


Conception objet et Design Patterns avec Java

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 // ...

Arnaud BARBE 75 / 109


Conception objet et Design Patterns avec Java

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 /**

Arnaud BARBE 76 / 109


Conception objet et Design Patterns avec Java

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.2Structure
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) {

Arnaud BARBE 77 / 109


Conception objet et Design Patterns avec Java

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 }

Arnaud BARBE 78 / 109


Conception objet et Design Patterns avec Java

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

Arnaud BARBE 79 / 109


Conception objet et Design Patterns avec Java

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

Arnaud BARBE 80 / 109


Conception objet et Design Patterns avec Java

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 */

Arnaud BARBE 81 / 109


Conception objet et Design Patterns avec Java

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++;

Arnaud BARBE 82 / 109


Conception objet et Design Patterns avec Java

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 }

Arnaud BARBE 83 / 109


Conception objet et Design Patterns avec Java

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 }

Arnaud BARBE 84 / 109


Conception objet et Design Patterns avec Java

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 }

Arnaud BARBE 85 / 109


Conception objet et Design Patterns avec Java

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 */

Arnaud BARBE 86 / 109


Conception objet et Design Patterns avec Java

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 /*

Arnaud BARBE 87 / 109


Conception objet et Design Patterns avec Java

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.3Comportement
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;

Arnaud BARBE 88 / 109


Conception objet et Design Patterns avec Java

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

Arnaud BARBE 89 / 109


Conception objet et Design Patterns avec Java

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) {

Arnaud BARBE 90 / 109


Conception objet et Design Patterns avec Java

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 /**

Arnaud BARBE 91 / 109


Conception objet et Design Patterns avec Java

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 }

Arnaud BARBE 92 / 109


Conception objet et Design Patterns avec Java

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() {

Arnaud BARBE 93 / 109


Conception objet et Design Patterns avec Java

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 *

Arnaud BARBE 94 / 109


Conception objet et Design Patterns avec Java

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 /**

Arnaud BARBE 95 / 109


Conception objet et Design Patterns avec Java

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) {

Arnaud BARBE 96 / 109


Conception objet et Design Patterns avec Java

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 }

Arnaud BARBE 97 / 109


Conception objet et Design Patterns avec Java

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) {

Arnaud BARBE 98 / 109


Conception objet et Design Patterns avec Java

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 }

Arnaud BARBE 99 / 109


Conception objet et Design Patterns avec Java

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();

Arnaud BARBE 100 / 109


Conception objet et Design Patterns avec Java

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 /**

Arnaud BARBE 101 / 109


Conception objet et Design Patterns avec Java

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 }

Arnaud BARBE 102 / 109


Conception objet et Design Patterns avec Java

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;

Arnaud BARBE 103 / 109


Conception objet et Design Patterns avec Java

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 */

Arnaud BARBE 104 / 109


Conception objet et Design Patterns avec Java

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 /**

Arnaud BARBE 105 / 109


Conception objet et Design Patterns avec Java

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 }

Arnaud BARBE 106 / 109


Conception objet et Design Patterns avec Java

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>");

Arnaud BARBE 107 / 109


Conception objet et Design Patterns avec Java

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

Arnaud BARBE 108 / 109


Conception objet et Design Patterns avec Java

5 L’AUTEUR
Nom : BARBE
Prénom : Arnaud
Email : arnaud.barbe@free.fr

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.

Arnaud BARBE 109 / 109

You might also like