Vous êtes sur la page 1sur 451

Rfrence

Les

design patterns
en Java
Les 23 modles
de conception
fondamentaux

Steven John Metsker


William C. Wake

Rseaux
et tlcom
Programmation

Gnie logiciel

Scurit
Systme
dexploitation

pattern Livre Page I Vendredi, 9. octobre 2009 10:31 10

Les Design
Patterns en Java
Les 23 modles de conception fondamentaux
Steven John Metsker
et William C. Wake

pattern Livre Page II Vendredi, 9. octobre 2009 10:31 10

Pearson Education France a apport le plus grand soin la ralisation de ce livre afin de vous fournir une information complte et fiable. Cependant, Pearson Education France nassume de responsabilits, ni pour son utilisation, ni pour les contrefaons de brevets ou atteintes aux droits de tierces
personnes qui pourraient rsulter de cette utilisation.
Les exemples ou les programmes prsents dans cet ouvrage sont fournis pour illustrer les descriptions
thoriques. Ils ne sont en aucun cas destins une utilisation commerciale ou professionnelle.
Pearson Education France ne pourra en aucun cas tre tenu pour responsable des prjudices
ou dommages de quelque nature que ce soit pouvant rsulter de lutilisation de ces exemples ou
programmes.
Tous les noms de produits ou marques cits dans ce livre sont des marques dposes par leurs
propritaires respectifs.
Publi par Pearson Education France
47 bis, rue des Vinaigriers
75010 PARIS
Tl. : 01 72 74 90 00
www.pearson.fr

Titre original : Design Patterns in Java

Traduit de lamricain par Freenet Sofor ltd

Mise en pages : TyPAO


ISBN : 978-2-7440-4097-9
Copyright 2009 Pearson Education France
Tous droits rservs

ISBN original : 0-321-33302-0


Copyright 2006 by Addison-Wesley
Tous droits rservs

Aucune reprsentation ou reproduction, mme partielle, autre que celles prvues larticle L. 122-5 2 et 3 a) du code de la
proprit intellectuelle ne peut tre faite sans lautorisation expresse de Pearson Education France ou, le cas chant, sans
le respect des modalits prvues larticle L. 122-10 dudit code.
All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or
mechanical, including photocopying, recording or by any information storage retrieval system, without permission from
Pearson Education, Inc.

pattern Livre Page III Vendredi, 9. octobre 2009 10:31 10

Table des matires


Prface ..............................................................................................................................

Conventions de codage .................................................................................................


Remerciements .............................................................................................................

1
2

Chapitre 1. Introduction .................................................................................................

Quest-ce quun pattern ? ..............................................................................................


Quest-ce quun pattern de conception ? ......................................................................
Liste des patterns dcrits dans louvrage ................................................................
Java ...............................................................................................................................
UML .............................................................................................................................
Exercices .......................................................................................................................
Organisation du livre .....................................................................................................
Oozinoz .........................................................................................................................
Rsum .........................................................................................................................

3
4
5
7
7
8
9
10
11

Partie I
Patterns dinterface
Chapitre 2. Introduction aux interfaces ........................................................................

15

Interfaces et classes abstraites .......................................................................................


Interfaces et obligations ................................................................................................
Rsum .........................................................................................................................
Au-del des interfaces ordinaires ..................................................................................

16
17
19
19

Chapitre 3. ADAPTER ....................................................................................................

21

Adaptation une interface ............................................................................................


Adaptateurs de classe et dobjet ...................................................................................

21
25

pattern Livre Page IV Vendredi, 9. octobre 2009 10:31 10

IV

Table des matires

Adaptation de donnes pour un widget JTable ..........................................................

29

Identification dadaptateurs ..........................................................................................

33

Rsum .........................................................................................................................

34

Chapitre 4. FACADE .......................................................................................................

35

Faades, utilitaires et dmos .........................................................................................

36

Refactorisation pour appliquer FACADE .......................................................................

37

Rsum .........................................................................................................................

46

Chapitre 5. COMPOSITE ..............................................................................................

47

Un composite ordinaire .................................................................................................

47

Comportement rcursif dans les objets composites ......................................................

48

Objets composites, arbres et cycles ..............................................................................

50

Des composites avec des cycles ....................................................................................

55

Consquences des cycles ..............................................................................................

59

Rsum .........................................................................................................................

60

Chapitre 6. BRIDGE .......................................................................................................

61

Une abstraction ordinaire ..............................................................................................

61

De labstraction au pattern BRIDGE .............................................................................

64

Des drivers en tant que BRIDGE ...................................................................................

66

Drivers de base de donnes ...........................................................................................

67

Rsum .........................................................................................................................

69

Partie II
Patterns de responsabilit
Chapitre 7. Introduction la responsabilit .................................................................

73

Responsabilit ordinaire ...............................................................................................

73

Contrle de la responsabilit grce la visibilit .........................................................

75

Rsum .........................................................................................................................

77

Au-del de la responsabilit ordinaire ..........................................................................

77

pattern Livre Page V Vendredi, 9. octobre 2009 10:31 10

Table des matires

Chapitre 8. SINGLETON ...............................................................................................

79

Le mcanisme de SINGLETON .....................................................................................


Singletons et threads .....................................................................................................
Identification de singletons ...........................................................................................
Rsum .........................................................................................................................

79
81
82
84

Chapitre 9. OBSERVER .................................................................................................

85

Un exemple classique : OBSERVER dans les interfaces utilisateurs ...............................


Modle-Vue-Contrleur ................................................................................................
Maintenance dun objet Observable .........................................................................
Rsum .........................................................................................................................

85
90
96
99

Chapitre 10. MEDIATOR ...............................................................................................

101

Un exemple classique : mdiateur de GUI ...................................................................


Mdiateur dintgrit relationnelle ...............................................................................
Rsum .........................................................................................................................

101
106
112

Chapitre 11. PROXY .......................................................................................................

115

Un exemple classique : proxy dimage .........................................................................


Reconsidration des proxies dimage ...........................................................................
Proxy distant .................................................................................................................
Proxy dynamique ..........................................................................................................
Rsum .........................................................................................................................

115
120
122
128
133

Chapitre 12. CHAIN OF RESPONSABILITY .............................................................

135

Une chane de responsabilits ordinaire .......................................................................


Refactorisation pour appliquer CHAIN OF RESPONSABILITY ...................................
Ancrage dune chane de responsabilits ......................................................................
CHAIN OF RESPONSABILITY sans COMPOSITE .........................................................
Rsum .........................................................................................................................

135
137
140
142
142

Chapitre 13. FLYWEIGHT ............................................................................................

143

Immuabilit ...................................................................................................................
Extraction de la partie immuable dun flyweight .........................................................
Partage des objets flyweight .........................................................................................
Rsum .........................................................................................................................

143
144
146
149

pattern Livre Page VI Vendredi, 9. octobre 2009 10:31 10

VI

Table des matires

Partie III
Patterns de construction
Chapitre 14. Introduction la construction ..................................................................

153

Quelques dfis de construction .....................................................................................


Rsum .........................................................................................................................
Au-del de la construction ordinaire .............................................................................

153
155
155

Chapitre 15. BUILDER ...................................................................................................

157

Un objet constructeur ordinaire ....................................................................................


Construction avec des contraintes .................................................................................
Un builder tolrant ........................................................................................................
Rsum .........................................................................................................................

157
160
163
164

Chapitre 16. FACTORY METHOD ...............................................................................

165

Un exemple classique : des itrateurs ...........................................................................


Identification de FACTORY METHOD .............................................................................
Garder le contrle sur le choix de la classe instancier ...............................................
Application de FACTORY METHOD dans une hirarchie parallle .................................
Rsum .........................................................................................................................

165
166
167
169
171

Chapitre 17. ABSTRACT FACTORY ...........................................................................

173

Un exemple classique : le kit de GUI ...........................................................................


Classe FACTORY abstraite et pattern FACTORY METHOD ..............................................
Packages et classes factory abstraites ...........................................................................
Rsum .........................................................................................................................

173
178
182
182

Chapitre 18. PROTOTYPE ............................................................................................

183

Des prototypes en tant quobjets factory ......................................................................


Prototypage avec des clones .........................................................................................
Rsum .........................................................................................................................

183
185
187

Chapitre 19. MEMENTO ...............................................................................................

189

Un exemple classique : dfaire une opration ..............................................................


Dure de vie des mmentos ..........................................................................................

189
196

pattern Livre Page VII Vendredi, 9. octobre 2009 10:31 10

Table des matires

VII

Persistance des mmentos entre les sessions ................................................................


Rsum .........................................................................................................................

197
200

Partie IV
Patterns dopration
Chapitre 20. Introduction aux oprations .....................................................................

203

Oprations et mthodes .................................................................................................


Signatures .....................................................................................................................
Exceptions .....................................................................................................................
Algorithmes et polymorphisme ....................................................................................
Rsum .........................................................................................................................
Au-del des oprations ordinaires ................................................................................

203
205
205
206
208
209

Chapitre 21. TEMPLATE METHOD ...........................................................................

211

Un exemple classique : algorithme de tri ......................................................................


Compltion dun algorithme .........................................................................................
Hooks ............................................................................................................................
Refactorisation pour appliquer TEMPLATE METHOD ....................................................
Rsum .........................................................................................................................

211
215
218
219
221

Chapitre 22. STATE ........................................................................................................

223

Modlisation dtats ......................................................................................................


Refactorisation pour appliquer STATE .........................................................................
Etats constants ..............................................................................................................
Rsum .........................................................................................................................

223
227
231
233

Chapitre 23. STRATEGY ...............................................................................................

235

Modlisation de stratgies ............................................................................................


Refactorisation pour appliquer STRATEGY ...................................................................
Comparaison de STRATEGY et STATE ..........................................................................
Comparaison de STRATEGY et TEMPLATE METHOD .....................................................
Rsum .........................................................................................................................

236
238
242
243
243

pattern Livre Page VIII Vendredi, 9. octobre 2009 10:31 10

VIII

Table des matires

Chapitre 24. COMMAND ...............................................................................................


Un exemple classique : commandes de menus .............................................................
Emploi de COMMAND pour fournir un service ................................................................
Hooks ............................................................................................................................
COMMAND en relation avec dautres patterns ..................................................................
Rsum .........................................................................................................................

245
245
248
249
251
252

Chapitre 25. INTERPRETER ........................................................................................


Un exemple de INTERPRETER .....................................................................................
Interprteurs, langages et analyseurs syntaxiques ........................................................
Rsum .........................................................................................................................

253
254
265
266

Partie V
Patterns dextension
Chapitre 26. Introduction aux extensions .....................................................................
Principes de la conception oriente objet .....................................................................
Le principe de substitution de Liskov ...........................................................................
La loi de Demeter .........................................................................................................
Elimination des erreurs potentielles ..............................................................................
Au-del des extensions ordinaires ................................................................................
Rsum .........................................................................................................................

269
269
270
271
273
273
274

Chapitre 27. DECORATOR ...........................................................................................


Un exemple classique : flux dE/S et objets Writer ...................................................
Enveloppeurs de fonctions ............................................................................................
DECORATOR en relation avec dautres patterns ..............................................................
Rsum .........................................................................................................................

277
277
285
292
293

Chapitre 28. ITERATOR ................................................................................................


Itration ordinaire .........................................................................................................
Itration avec scurit inter-threads ..............................................................................
Itration sur un objet composite ...................................................................................
Ajout dun niveau de profondeur un numrateur ...............................................
Enumration des feuilles .........................................................................................
Rsum .........................................................................................................................

295
295
297
303
310
311
313

pattern Livre Page IX Vendredi, 9. octobre 2009 10:31 10

Table des matires

IX

Chapitre 29. VISITOR ....................................................................................................

315

Application de VISITOR ..............................................................................................


Un VISITOR ordinaire ..................................................................................................
Cycles et VISITOR .......................................................................................................
Risques de VISITOR ....................................................................................................
Rsum .........................................................................................................................

315
318
323
328
330

Partie VI
Annexes
Annexe A. Recommandations ........................................................................................

333

Tirer le meilleur parti du livre .......................................................................................


Connatre ses classiques ...............................................................................................
Appliquer les patterns ...................................................................................................
Continuer dapprendre ..................................................................................................

333
334
334
336

Annexe B. Solutions .........................................................................................................

337

Introduction aux interfaces ...........................................................................................


Solution 2.1 .............................................................................................................
Solution 2.2 .............................................................................................................
Solution 2.3 .............................................................................................................
ADAPTER ....................................................................................................................
Solution 3.1 .............................................................................................................
Solution 3.2 .............................................................................................................
Solution 3.3 .............................................................................................................
Solution 3.4 .............................................................................................................
Solution 3.5 .............................................................................................................
Solution 3.6 .............................................................................................................
FACADE .......................................................................................................................
Solution 4.1 .............................................................................................................
Solution 4.2 .............................................................................................................
Solution 4.3 .............................................................................................................
Solution 4.4 .............................................................................................................

337
337
338
338
338
338
339
340
341
341
342
342
342
343
343
344

pattern Livre Page X Vendredi, 9. octobre 2009 10:31 10

Table des matires

COMPOSITE ................................................................................................................
Solution 5.1 .............................................................................................................
Solution 5.2 .............................................................................................................
Solution 5.3 .............................................................................................................
Solution 5.4 .............................................................................................................
Solution 5.5 .............................................................................................................
Solution 5.6 .............................................................................................................
BRIDGE .......................................................................................................................
Solution 6.1 .............................................................................................................
Solution 6.2 .............................................................................................................
Solution 6.3 .............................................................................................................
Solution 6.4 .............................................................................................................
Solution 6.5 .............................................................................................................
Introduction la responsabilit .....................................................................................
Solution 7.1 .............................................................................................................
Solution 7.2 .............................................................................................................
Solution 7.3 .............................................................................................................
Solution 7.4 .............................................................................................................
SINGLETON ................................................................................................................
Solution 8.1 .............................................................................................................
Solution 8.2 .............................................................................................................
Solution 8.3 .............................................................................................................
Solution 8.4 ............................................................................................................
OBSERVER ..................................................................................................................
Solution 9.1 .............................................................................................................
Solution 9.2 .............................................................................................................
Solution 9.3 .............................................................................................................
Solution 9.4 .............................................................................................................
Solution 9.5 .............................................................................................................
Solution 9.6 .............................................................................................................
Solution 9.7 .............................................................................................................
MEDIATOR ..................................................................................................................
Solution 10.1 ...........................................................................................................
Solution 10.2 ...........................................................................................................
Solution 10.3 ...........................................................................................................
Solution 10.4 ...........................................................................................................
Solution 10.5 ...........................................................................................................

345
345
346
346
347
347
348
348
348
348
349
349
350
350
350
351
352
353
353
353
353
353
354
354
354
355
356
356
357
357
358
359
359
360
361
361
362

pattern Livre Page XI Vendredi, 9. octobre 2009 10:31 10

Table des matires

XI

PROXY .........................................................................................................................
Solution 11.1 ...........................................................................................................
Solution 11.2 ...........................................................................................................
Solution 11.3 ...........................................................................................................
Solution 11.4 ...........................................................................................................
Solution 11.5 ...........................................................................................................
CHAIN OF RESPONSABILITY .................................................................................
Solution 12.1 ...........................................................................................................
Solution 12.2 ...........................................................................................................
Solution 12.3 ...........................................................................................................
Solution 12.4 ...........................................................................................................
Solution 12.5 ...........................................................................................................
FLYWEIGHT ...............................................................................................................
Solution 13.1 ...........................................................................................................
Solution 13.2 ...........................................................................................................
Solution 13.3 ...........................................................................................................
Solution 13.4 ...........................................................................................................
Introduction la construction .......................................................................................
Solution 14.1 ...........................................................................................................
Solution 14.2 ...........................................................................................................
Solution 14.3 ...........................................................................................................
BUILDER .....................................................................................................................
Solution 15.1 ...........................................................................................................
Solution 15.2 ...........................................................................................................
Solution 15.3 ...........................................................................................................
Solution 15.4 ...........................................................................................................
FACTORY METHOD ..................................................................................................
Solution 16.1 ...........................................................................................................
Solution 16.2 ...........................................................................................................
Solution 16.3 ...........................................................................................................
Solution 16.4 ...........................................................................................................
Solution 16.5 ...........................................................................................................
Solution 16.6 ...........................................................................................................
Solution 16.7 ...........................................................................................................
ABSTRACT FACTORY ...............................................................................................
Solution 17.1 ...........................................................................................................
Solution 17.2 ...........................................................................................................

362
362
363
363
363
364
364
364
365
366
366
367
368
368
369
370
370
371
371
372
372
373
373
373
374
374
375
375
376
376
376
377
378
378
379
379
380

pattern Livre Page XII Vendredi, 9. octobre 2009 10:31 10

XII

Table des matires

Solution 17.3 ...........................................................................................................


Solution 17.4 ...........................................................................................................
Solution 17.5 ...........................................................................................................
PROTOTYPE ................................................................................................................
Solution 18.1 ...........................................................................................................
Solution 18.2 ...........................................................................................................
Solution 18.3 ...........................................................................................................
Solution 18.4 ...........................................................................................................
MEMENTO ..................................................................................................................
Solution 19.1 ...........................................................................................................
Solution 19.2 ...........................................................................................................
Solution 19.3 ...........................................................................................................
Solution 19.4 ...........................................................................................................
Solution 19.5 ...........................................................................................................
Introduction aux oprations ..........................................................................................
Solution 20.1 ...........................................................................................................
Solution 20.2 ...........................................................................................................
Solution 20.3 ...........................................................................................................
Solution 20.4 ...........................................................................................................
Solution 20.5 ...........................................................................................................
TEMPLATE METHOD ................................................................................................
Solution 21.1 ...........................................................................................................
Solution 21.2 ...........................................................................................................
Solution 21.3 ...........................................................................................................
Solution 21.4 ...........................................................................................................
STATE ...........................................................................................................................
Solution 22.1 ...........................................................................................................
Solution 22.2 ...........................................................................................................
Solution 22.3 ...........................................................................................................
Solution 22.4 ...........................................................................................................
STRATEGY ..................................................................................................................
Solution 23.1 ...........................................................................................................
Solution 23.2 ...........................................................................................................
Solution 23.3 ...........................................................................................................
Solution 23.4 ...........................................................................................................

380
381
381
382
382
383
383
384
384
384
385
385
386
386
387
387
387
388
388
388
389
389
389
390
390
390
390
390
391
391
392
392
392
392
393

pattern Livre Page XIII Vendredi, 9. octobre 2009 10:31 10

Table des matires

XIII

COMMAND .................................................................................................................
Solution 24.1 ...........................................................................................................
Solution 24.2 ...........................................................................................................
Solution 24.3 ...........................................................................................................
Solution 24.4 ...........................................................................................................
Solution 24.5 ...........................................................................................................
Solution 24.6 ...........................................................................................................
INTERPRETER ............................................................................................................
Solution 25.1 396
Solution 25.2 ...........................................................................................................
Solution 25.3 ...........................................................................................................
Solution 25.4 ...........................................................................................................
Introduction aux extensions ..........................................................................................
Solution 26.1 398
Solution 26.2 ...........................................................................................................
Solution 26.3 ...........................................................................................................
Solution 26.4 ...........................................................................................................
DECORATOR ..............................................................................................................
Solution 27.1 399
Solution 27.2 ...........................................................................................................
Solution 27.3 ...........................................................................................................
Solution 27.4 ...........................................................................................................
ITERATOR ...................................................................................................................
Solution 28.1 401
Solution 28.2 ...........................................................................................................
Solution 28.3 ...........................................................................................................
Solution 28.4 ...........................................................................................................
VISITOR .......................................................................................................................
Solution 29.1 403
Solution 29.2 ...........................................................................................................
Solution 29.3 ...........................................................................................................
Solution 29.4 ...........................................................................................................
Solution 29.5 ...........................................................................................................

393
393
393
395
395
396
396
396

Annexe C. Code source dOozinoz ...............................................................................


Obtention et utilisation du code source ........................................................................
Construction du code dOozinoz ..................................................................................

405
405
406

397
397
397
398
398
398
399
399
400
401
401
401
402
402
402
403
403
403
404
404

pattern Livre Page XIV Vendredi, 9. octobre 2009 10:31 10

XIV

Table des matires

Test du code avec JUnit ................................................................................................


Localiser les fichiers .....................................................................................................
Rsum .........................................................................................................................

406
406
407

Annexe D. Introduction UML .....................................................................................


Classes ..........................................................................................................................
Relations entre classes ..................................................................................................
Interfaces .......................................................................................................................
Objets ............................................................................................................................
Etats ..............................................................................................................................

409
409
412
414
414
416

Glossaire ............................................................................................................................

417

Bibliographie .....................................................................................................................

425

Index ..................................................................................................................................

427

pattern Livre Page 1 Vendredi, 9. octobre 2009 10:31 10

Prface
Les patterns de conception sont des solutions de niveaux classe et mthode des
problmes courants dans le dveloppement orient objet. Si vous tes dj un
programmeur Java intermdiaire et souhaitez devenir avanc, ou bien si vous tes
avanc mais navez pas encore tudi les patterns de conception, ce livre est pour
vous.
Il adopte une approche de cahier dexercices, chaque chapitre tant consacr un
pattern particulier. En plus dexpliquer le pattern en question, chaque chapitre
inclut un certain nombre dexercices vous demandant dexpliquer quelque chose ou
de dvelopper du code pour rsoudre un problme.
Nous vous recommandons vivement de prendre le temps deffectuer chaque exercice lorsque vous tombez dessus plutt que de lire le livre dune traite. En mettant
en pratique vos connaissances au fur et mesure de leur acquisition, vous apprendrez
mieux, mme si vous ne faites pas plus dun ou deux chapitres par semaine.

Conventions de codage
Le code des exemples prsents dans ce livre est disponible en ligne. Voyez
lAnnexe C pour savoir comment lobtenir.
Nous avons utilis le plus souvent un style cohrent avec les conventions de codage
de Sun. Les accolades ont t omises lorsque ctait possible. Nous avons d faire
quelques compromis pour nous adapter au format du livre. Pour respecter les colonnes
troites, les noms de variables sont parfois plus courts que ceux que nous
employons habituellement. Et pour viter les complications du contrle de code
source, nous avons distingu les multiples versions dun mme fichier en accolant
un chiffre son nom (par exemple, ShowBallistics2). Vous devriez normalement
utiliser le contrle de code source et travailler seulement avec la dernire version
dune classe.

pattern Livre Page 2 Vendredi, 9. octobre 2009 10:31 10

Prface

Remerciements
Nous tenons remercier le dfunt John Vlissides pour ses encouragements et ses
recommandations concernant ce livre et dautres. John, diteur de la collection
Software Patterns Series et coauteur de louvrage original Design Patterns, tait
pour nous un ami et une inspiration.
En plus de nous appuyer largement sur Design Patterns, nous nous sommes aussi
inspirs de nombreux autres livres. Voyez pour cela la bibliographie en fin
douvrage. En particulier, The Unified Modeling Language User Guide (le Guide de
lutilisateur UML) [Booch, Rambaugh, et Jacobsen 1999] donne une explication
claire dUML, et JavaTM in a Nutshell (Java en concentr : Manuel de rfrence
pour Java) [Flanagan 2005] constitue une aide concise et prcise sur Java. The
Chemistry of Fireworks [Russell 2000] nous a servi de source dinformations pour
laborer nos exemples pyrotechniques ralistes.
Enfin, nous sommes reconnaissants toute lquipe de production pour son travail
acharn et son dvouement.
Steve Metsker (Steve.Metsker@acm.org)
Bill Wake (William.Wake@acm.org)

pattern Livre Page 3 Vendredi, 9. octobre 2009 10:31 10

1
Introduction
Ce livre couvre le mme ensemble de techniques que louvrage de rfrence Design
Patterns, dErich Gamma, Richard Helm, Ralph Johnson et John Vlissides [Gamma
et al. 1995], et propose des exemples en Java. Il inclut de nombreux exercices
conus pour vous aider dvelopper votre aptitude appliquer les patterns de
conception dans vos programmes.
Il sadresse aux dveloppeurs qui connaissent Java et souhaitent amliorer
leurs comptences en tant que concepteurs.

Quest-ce quun pattern ?


Un pattern, ou modle, est un moyen daccomplir quelque chose, un moyen
datteindre un objectif, une technique. Le principe est de compiler les mthodes
prouves qui sappliquent de nombreux types defforts, tels que la fabrication
daliments, dartifices, de logiciels, ou autres. Dans nimporte quel art ou mtier
nouveau en voie de maturation, ses pratiquants commencent, un moment donn,
laborer des mthodes communes efficaces pour parvenir leurs buts et rsoudre
des problmes dans diffrents contextes. Cette communaut invente aussi gnralement un jargon pour pouvoir discuter de son savoir-faire. Une partie de cette
terminologie a trait aux modles, ou techniques tablies, permettant dobtenir
certains rsultats. A mesure que cet art se dveloppe et que son jargon stoffe, les
auteurs commencent jouer un rle important. En documentant les modles de cet
art, ils contribuent standardiser son jargon et faire connatre ses techniques.

pattern Livre Page 4 Vendredi, 9. octobre 2009 10:31 10

Les Design Patterns en Java

Les 23 modles de conception fondamentaux

Christopher Alexander a t un des premiers auteurs compiler les meilleures pratiques dun mtier en documentant ses modles. Son travail concerne larchitecture,
celle des immeubles et non des logiciels. Dans A Pattern Language: Towns, Buildings
Construction (Alexander, Ishikouwa, et Silverstein 1977), il dcrit des modles
permettant de btir avec succs des immeubles et des villes. Cet ouvrage est puissant et a influenc la communaut logicielle notamment en raison du sens quil
donne au terme objectif (intent).
Vous pourriez penser que les modles architecturaux servent principalement
concevoir des immeubles. En fait, Alexander tablit clairement que leur objectif est
de servir et dinspirer les gens qui occuperont les immeubles et les villes conus
daprs ces modles. Son travail a montr que les modles sont un excellent moyen
de saisir et de transmettre le savoir-faire et la sagesse dun art. Il prcise galement
que comprendre et documenter correctement cet objectif est essentiel, philosophique
et difficile.
La communaut informatique a fait sienne cette approche en crant de nombreux
ouvrages qui documentent des modles de dveloppement logiciel. Ces livres
consignent les meilleures pratiques en matire de processus logiciels, danalyse
logicielle, darchitecture de haut niveau, et de conception de niveau classe. Il en
apparat de nouveaux chaque anne. Lisez les critiques et les commentaires de
lecteurs pour faire un choix judicieux.

Quest-ce quun pattern de conception ?


Un pattern de conception (design pattern) est un modle qui utilise des classes et
leurs mthodes dans un langage orient objet. Les dveloppeurs commencent
souvent sintresser la conception seulement lorsquils matrisent un langage de
programmation et crivent du code depuis longtemps. Il vous est probablement dj
arriv de remarquer que du code crit par quelquun dautre semblait plus simple et
plus efficace que le vtre, auquel cas vous avez d vous demander comment son
dveloppeur tait parvenu une telle simplicit. Les patterns de conception interviennent un niveau au-dessus du code et indiquent typiquement comment atteindre
un but en nutilisant que quelques classes. Un pattern reprsente une ide, et non
une implmentation particulire.
Dautres dveloppeurs ont dcouvert avant vous comment programmer efficacement dans les langages orients objet. Si vous souhaitez devenir un programmeur

pattern Livre Page 5 Vendredi, 9. octobre 2009 10:31 10

Chapitre 1

Introduction

Java avanc, vous devriez tudier les patterns de conception, surtout ceux de ce
livre les mmes que ceux expliqus dans Design Patterns.
Louvrage Design Patterns dcrit vingt-trois patterns de conception (pour plus de
dtails, voir section suivante). De nombreux autres livres ont suivi sur le sujet, aussi
dnombre-t-on au moins cent patterns qui valent la peine dtre connus. Les vingttrois patterns recenss par Gamma, Helm, Johnson et Vlissides ne sont pas forcment les plus importants, mais ils sont nanmoins proches du haut de la liste. Ces
auteurs ont donc bien choisi et les patterns quils documentent valent certainement
la peine que vous les appreniez. Ils vous serviront de rfrence lorsque commencerez
tudier les patterns exposs par dautres sources.
Liste des patterns dcrits dans louvrage

Patterns dinterface

ADAPTER (17) fournit linterface quun client attend en utilisant les services dune
classe dont linterface est diffrente.
FACADE (33) fournit une interface simplifiant lemploi dun sous-systme.
COMPOSITE (47) permet aux clients de traiter de faon uniforme des objets individuels et des compositions dobjets.
BRIDGE (63) dcouple une classe qui sappuie sur des oprations abstraites de
limplmentation de ces oprations, permettant ainsi la classe et son implmentation de varier indpendamment.
Patterns de responsabilit

SINGLETON (81) garantit quune classe ne possde quune seule instance, et fournit
un point daccs global celle-ci.
OBSERVER (87) dfinit une dpendance du type un--plusieurs (1,n) entre des objets
de manire ce que lorsquun objet change dtat, tous les objets dpendants en
soient notifis et soient actualiss afin de pouvoir ragir conformment.
MEDIATOR (103) dfinit un objet qui encapsule la faon dont un ensemble dobjets
interagissent. Cela promeut un couplage lche, vitant aux objets davoir se
rfrer explicitement les uns aux autres, et permet de varier leur interaction indpendamment.

pattern Livre Page 6 Vendredi, 9. octobre 2009 10:31 10

Les Design Patterns en Java

Les 23 modles de conception fondamentaux

PROXY (117) contrle laccs un objet en fournissant un intermdiaire pour cet


objet.
CHAIN OF RESPONSABILITY (137) vite de coupler lmetteur dune requte son
rcepteur en permettant plus dun objet dy rpondre.
FLYWEIGHT (145) utilise le partage pour supporter efficacement un grand nombre
dobjets forte granularit.
Patterns de construction

BUILDER (159) dplace la logique de construction dun objet en-dehors de la classe


instancier, typiquement pour permettre une construction partielle ou pour simplifier
lobjet.
FACTORY METHOD (167) laisse un autre dveloppeur dfinir linterface permettant de
crer un objet, tout en gardant un contrle sur le choix de la classe instancier.
ABSTRACT FACTORY (175) permet la cration de familles dobjets ayant un lien ou
interdpendants.
PROTOTYPE (187) fournit de nouveaux objets par la copie dun exemple.
MEMENTO (193) permet le stockage et la restauration de ltat dun objet.
Patterns dopration

TEMPLATE METHOD (217) implmente un algorithme dans une mthode, laissant


dautres classes le soin de dfinir certaines tapes de lalgorithme.
STATE (229) distribue la logique dpendant de ltat dun objet travers plusieurs
classes qui reprsentent chacune un tat diffrent.
STRATEGY (241) encapsule des approches, ou stratgies, alternatives dans des classes
distinctes qui implmentent chacune une opration commune.

COMMAND (251) encapsule une requte en tant quobjet, de manire pouvoir paramtrer des clients au moyen de divers types de requtes (de file dattente, de temps
ou de journalisation) et de permettre un client de prparer un contexte spcial dans
lequel mettre la requte.
INTERPRETER (261) permet de composer des objets excutables daprs un ensemble
de rgles de composition que vous dfinissez.

pattern Livre Page 7 Vendredi, 9. octobre 2009 10:31 10

Chapitre 1

Introduction

Patterns dextension

DECORATOR (287) permet de composer dynamiquement le comportement dun


objet.
ITERATOR (305) fournit un moyen daccder de faon squentielle aux lments
dune collection.
VISITOR (325) permet de dfinir une nouvelle opration pour une hirarchie sans
changer ses classes.

Java
Les exemples de ce livre utilisent Java, le langage orient objet (OO) dvelopp par
Sun. Ce langage, ses bibliothques et ses outils associs forment une suite de
produits pour le dveloppement et la gestion de systmes aux architectures multiniveaux et orientes objet.
Limportance de Java tient en partie au fait quil sagit dun langage de consolidation, cest--dire conu pour intgrer les points forts des langages prcdents.
Cette consolidation est la cause de son succs et garantit que les langages futurs
tendront sinscrire dans sa continuit au lieu de sen loigner radicalement. Votre
investissement dans Java ne perdra assurment pas de sa valeur, quel que soit le
langage qui lui succde.
Les patterns de Design Patterns sappliquent Java, car, comme Smalltalk, C++ et
C#, ils se fondent sur un paradigme classe/instance. Java ressemble beaucoup plus
Smalltalk et C++ qu Prolog ou Self par exemple. Mme sil ne faut pas ngliger limportance de paradigmes concurrents, le paradigme classe/instance constitue
une avance concrte en informatique applique. Le prsent livre emploie Java en
raison de sa popularit et parce que son volution suit le chemin des langages que
nous utiliserons dans les annes venir.

UML
Lorsque les solutions des exercices contiennent du code, ce livre utilise Java. Mais
nombre dexercices vous demandent de dessiner un diagramme illustrant les relations entre des classes, des packages et dautres lments. Vous pouvez choisir la
notation que vous prfrez, mais sachez que ce livre utilise la notation UML
(Unified Modeling Language). Mme si vous la connaissez dj, il peut tre utile

pattern Livre Page 8 Vendredi, 9. octobre 2009 10:31 10

Les Design Patterns en Java

Les 23 modles de conception fondamentaux

davoir une rfrence porte de main. Vous pouvez consulter deux ouvrages de
qualit : The Unified Modeling Language User Guide (le Guide de lutilisateur UML)
[Booch, Rumbaugh, et Jacobsen 1999] et UML Distilled [Fowler et Scott 2003].
Les connaissances minimales dont vous avez besoin pour ce livre sont donnes dans
lAnnexe D consacre UML.

Exercices
Mme si vous lisez de nombreux ouvrages sur un sujet, vous naurez le sentiment
de le matriser vraiment quen le pratiquant. Tant que vous nappliquerez pas
concrtement les connaissances acquises, certaines subtilits et approches alternatives vous chapperont. Le seul moyen de gagner de lassurance avec les patterns de
conception est de les appliquer dans le cadre dexercices pratiques.
Le problme lorsque lon apprend en faisant est que lon peut causer des dgts.
Vous ne pouvez pas appliquer les patterns de conception dans du code en production si vous ne les matrisez pas. Mais il faut bien que vous commenciez les appliquer
pour acqurir ce savoir-faire. La solution est de vous familiariser avec les patterns
au travers dexemples de problmes, o vos erreurs seront sans consquence mais
instructives.
Chaque chapitre de ce livre dbute par une courte introduction puis prsente
progressivement une srie dexercices. Lorsque vous avez trouv une solution, vous
pouvez la comparer aux rponses proposes dans lAnnexe B. Il se peut que la solution du livre adopte une approche diffrente de la vtre, vous faisant voir les choses
sous une autre perspective.
Vous ne pouvez probablement pas prvoir le temps quil vous faudra pour trouver
les rponses aux exercices. Si vous consultez dautres livres, travaillez avec un
collgue et crivez des chantillons de code pour vrifier votre solution, cest
parfait ! Vous ne regretterez pas lnergie et le temps investis.
Un avertissement : si vous vous contentez de lire les solutions immdiatement aprs
avoir lu un exercice, vous ne tirerez pas un grand enseignement de ce livre. Ces
solutions ne vous seront daucune utilit si vous nlaborez pas dabord les vtres
pour pouvoir ensuite les leur comparer et tirer les leons de vos erreurs.

pattern Livre Page 9 Vendredi, 9. octobre 2009 10:31 10

Chapitre 1

Introduction

Organisation du livre
Il existe de nombreuses faons dorganiser et de classer les patterns de conception.
Vous pourriez les organiser en fonction de leurs similitudes sur le plan structurel,
ou bien suivre lordre de Design Patterns. Mais laspect le plus important dun
pattern est son objectif, cest--dire la valeur potentielle lie son application. Le
prsent livre organise les vingt-trois patterns de Design Patterns en fonction de leur
objectif.
Reste ensuite dterminer comment catgoriser ces objectifs. Nous sommes partis
du principe que lobjectif dun pattern de conception peut gnralement tre
exprim comme tant le besoin daller plus loin que les fonctionnalits ordinaires
intgres Java. Par exemple, Java offre un large support pour la dfinition des
interfaces implmentes par les classes. Mais si vous disposez dj dune classe
dont vous aimeriez modifier linterface pour quelle corresponde aux exigences
dun client, vous pourriez dcider dappliquer le pattern ADAPTER. Lobjectif de ce
pattern est de vous aider complmenter les fonctionnalits dinterface intgres
Java.
Ce livre regroupe les patterns de conception en cinq catgories que voici :
1. Interfaces.
2. Responsabilit.
3. Construction.
4. Oprations.
5. Extensions.
Ces cinq catgories correspondent aux cinq parties du livre. Chaque partie dbute
par un chapitre qui prsente et remet en question les fonctionnalits Java lies au
type dobjectif dont il est question. Par exemple, le premier chapitre de la Partie I
traite des interfaces Java ordinaires. Il vous amne rflchir sur la structure des
interfaces Java, notamment en les comparant aux classes abstraites. Les autres
chapitres de cette partie dcrivent les patterns qui ont pour principal objectif de
dfinir une interface, cest--dire lensemble des mthodes quun client peut appeler partir dun fournisseur de services. Chacun deux rpond un besoin que les
interfaces Java ne peuvent satisfaire elles seules.

pattern Livre Page 10 Vendredi, 9. octobre 2009 10:31 10

10

Les Design Patterns en Java

Les 23 modles de conception fondamentaux

Ce classement des patterns par objectifs ne signifie pas que chaque pattern supporte
seulement un type dobjectif. Lorsquil en supporte plusieurs, il fait lobjet dun
chapitre entier dans la premire partie laquelle il sapplique puis il est mentionn
brivement dans les autres parties concernes. Le Tableau 1.1 illustre la catgorisation sous-jacente lorganisation du livre.
Tableau 1.1 : Une catgorisation des patterns par objectifs

Objectif

Patterns

Interfaces

ADAPTER, FACADE, COMPOSITE, BRIDGE

Responsabilit

SINGLETON, OBSERVER, MEDIATOR, PROXY, CHAIN OF RESPONSIBILITY,


FLYWEIGHT

Construction

BUILDER, FACTORY METHOD, ABSTRACT FACTORY, PROTOTYPE, MEMENTO

Oprations

TEMPLATE METHOD, STATE, STRATEGY, COMMAND, INTERPRETER

Extensions

DECORATOR, ITERATOR, VISITOR

Nous esprons que ce classement vous amnera vous interroger. Pensez-vous


aussi que SINGLETON a trait la responsabilit, et non la construction ? COMPOSITE est-il rellement un pattern dinterface ? Toute catgorisation est subjective.
Mais vous conviendrez certainement que le fait de rflchir lobjectif des patterns
et la faon de les appliquer est un exercice trs utile.

Oozinoz
Les exercices de ce livre citent tous des exemples dOozinoz Fireworks, une entreprise fictive qui fabrique et vend des pices pour feux dartifice et organise des
vnements pyrotechniques. Vous pouvez vous procurer le code de ces exemples
ladresse www.oozinoz.com. Pour en savoir plus sur la compilation et le test du
code, voyez lAnnexe C.

pattern Livre Page 11 Vendredi, 9. octobre 2009 10:31 10

Chapitre 1

Introduction

11

Rsum
Les patterns de conception distillent une sagesse vieille de quelques dizaines
dannes qui tablit un jargon standard, permettant aux dveloppeurs de nommer
les concepts quils appliquent. Ceux abords dans louvrage de rfrence Design
Patterns font partie des patterns de niveau classe les plus utiles et mritent que vous
les appreniez. Le prsent livre reprend ces patterns mais utilise Java et ses bibliothques pour ses exemples et exercices. En ralisant les exercices proposs, vous
apprendrez reconnatre et appliquer une part importante de la sagesse de la
communaut logicielle.

pattern Livre Page 12 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 13 Vendredi, 9. octobre 2009 10:31 10

I
Patterns dinterface

pattern Livre Page 14 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 15 Vendredi, 9. octobre 2009 10:31 10

2
Introduction aux interfaces
Pour parler de manire abstraite, linterface dune classe est lensemble des mthodes
et champs de la classe auxquels des objets dautres classes sont autoriss accder.
Elle constitue gnralement un engagement que les mthodes accompliront
lopration signifie par leur nom et tel que spcifie par les commentaires, les tests
et autres documentations du code. Limplmentation dune classe est le code
contenu dans ses mthodes.
Java fait du concept dinterface une structure distincte, sparant expressment
linterface ce quun objet doit faire de limplmentation comment un
objet remplit cet engagement. Les interfaces Java permettent plusieurs classes
doffrir la mme fonctionnalit et une mme classe dimplmenter plusieurs interfaces.
Plusieurs patterns de conception emploient les fonctionnalits intgres Java. Par
exemple, vous pourriez utiliser une interface pour adapter linterface dune classe
afin de rpondre aux besoins dun client en appliquant le pattern ADAPTER. Mais
avant daborder certaines notions avances, il peut tre utile de sassurer que vous
matrisez les fonctionnalits de base, commencer par les interfaces.

pattern Livre Page 16 Vendredi, 9. octobre 2009 10:31 10

16

Partie I

Patterns dinterface

Interfaces et classes abstraites


Le livre original Design Patterns [Gamma et al. 1995] mentionne frquemment
lemploi de classes abstraites mais pas du tout lemploi dinterfaces. La raison en
est que les langages C++ et Smalltalk, sur lesquels il sappuie pour ses exemples, ne
possdent pas une telle structure. Cela ne remet toutefois pas en cause lutilit de ce
livre pour les dveloppeurs Java, tant donn que les interfaces Java sont assez
semblables aux classes abstraites.
Exercice 2.1
Enumrez trois diffrences entre les classes abstraites et les interfaces Java.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.

Si les interfaces nexistaient pas, vous pourriez utiliser la place des classes abstraites,
comme dans C++. Les interfaces jouent toutefois un rle essentiel dans le dveloppement dapplications multiniveaux, ce qui justifie certainement leur statut
particulier de structure distincte.
Considrez la dfinition dune interface que les classes de simulation de fuse
doivent implmenter. Les ingnieurs conoivent toutes sortes de fuses, quelles
soient combustible solide ou liquide, avec des caractristiques balistiques trs
diverses. Indpendamment de sa composition, la simulation dune fuse doit fournir
des chiffres pour la pousse (thrust) et la masse (mass). Voici le code quutilise
Oozinoz pour dfinir linterface de simulation de fuse :
package com.oozinoz.simulation;
public interface RocketSim {
abstract double getMass();
public double getThrust();
void setSimTime(double t);
}

pattern Livre Page 17 Vendredi, 9. octobre 2009 10:31 10

Chapitre 2

Introduction aux interfaces

17

Exercice 2.2
Parmi les affirmations suivantes, lesquelles sont vraies ?
A. Les mthodes de linterface RocketSim sont toutes trois abstraites, mme
si seulement getMass() dclare cela explicitement.
B. Les trois mthodes de linterface sont publiques, mme si seulement
getThrust() dclare cela explicitement.
C. Linterface est dclare public interface, mais elle serait publique
mme si le mot cl public tait omis.
D. Il est possible de crer une autre interface, par exemple RocketSimSolid,
qui tende RocketSim.
E. Toute interface doit comporter au moins une mthode.
F. Une interface peut dclarer des champs dinstance quune classe dimplmentation doit galement dclarer.
G. Bien quil ne soit pas possible dinstancier une interface, une interface
peut dclarer des mthodes constructeurs dont la signature sera donne
par une classe dimplmentation.

Interfaces et obligations
Un avantage important des interfaces Java est quelles limitent linteraction entre
les objets. Cette limitation savre tre un soulagement. En effet, une classe qui
implmente une interface peut subir des changements considrables dans sa faon
de remplir le contrat dfini par linterface sans que cela affecte aucunement ses
clients.
Un dveloppeur qui cre une classe implmentant RocketSim a pour tche dcrire
les mthodes getMass() et getThrust() qui retournent les mesures de performance dune fuse. Autrement dit, il doit remplir le contrat de ces mthodes.
Parfois, les mthodes dsignes par une interface nont aucune obligation de fournir
un service lappelant. Dans certains cas, la classe dimplmentation peut mme
ignorer lappel, implmentant une mthode avec un corps vide.

pattern Livre Page 18 Vendredi, 9. octobre 2009 10:31 10

18

Partie I

Patterns dinterface

Exercice 2.3
Donnez un exemple dinterface avec des mthodes nimpliquant aucune responsabilit pour la classe dimplmentation de retourner une valeur ou daccomplir
une quelconque action pour le compte de lappelant.
Si vous crez une interface qui spcifie un ensemble de mthodes de notification,
vous pourriez envisager dutiliser une classe stub, cest--dire une classe qui implmente linterface avec des mthodes ne faisant rien. Les dveloppeurs peuvent driver des sous-classes de la classe stub, en redfinissant uniquement les mthodes de
linterface qui sont importantes pour leur application. La classe WindowAdapter
dans java.awt.event est un exemple dune telle classe, comme illustr Figure 2.1
(pour une introduction rapide UML, voyez lAnnexe D). Cette classe implmente
toutes les mthodes de linterface WindowListener mais les implmentations sont
vides ; ces mthodes ne contiennent aucune instruction.

interface

WindowAdapter

WindowListener

windowActivated()

windowActivated()

windowClosed()

windowClosed()

windowClosing()

windowClosing()

windowDeactivated()

windowDeactivated()

windowDeiconified()

windowDeiconified()

windowIconified()

windowIconified()

windowOpened()
windowStateChanged()
windowGainedFocus()
windowLostFocus()

windowOpened()
windowStateChanged()
windowGainedFocus()
windowLostFocus()

Figure 2.1
La classe WindowAdapter facilite lenregistrement de listeners pour les vnements de fentre
en vous permettant dignorer ceux qui ne vous intressent pas.

pattern Livre Page 19 Vendredi, 9. octobre 2009 10:31 10

Chapitre 2

Introduction aux interfaces

19

En plus de dclarer des mthodes, une interface peut dclarer des constantes. Dans
lexemple suivant, ClassificationConstants dclare deux constantes auxquelles
les classes implmentant cette interface auront accs :
public interface ClassificationConstants {
static final int CONSUMER = 1;
static final int DISPLAY = 2;
}

Une autre diffrence notable existe entre les interfaces et les classes abstraites. Tout
en dclarant quelle tend (extends) une autre classe, une classe peut aussi dclarer
quelle implmente (implements) une ou plusieurs interfaces.

Rsum
La puissance des interfaces rside dans le fait quelles stipulent ce qui est attendu et
ce qui ne lest pas en matire de collaboration entre classes. Elles sont semblables
aux classes purement abstraites en ce quelles dfinissent des attentes mais ne les
implmentent pas.
Matriser la fois les concepts et les dtails de lapplication des interfaces Java
demande du temps, mais le sacrifice en vaut la peine. Cette structure puissante est
au cur de nombreuses conceptions robustes et de plusieurs patterns de conception.

Au-del des interfaces ordinaires


Vous pouvez simplifier et renforcer vos conceptions grce une application approprie des interfaces Java. Parfois, cependant, la conception dune interface doit
dpasser sa dfinition et son utilisation ordinaires.
Si vous envisagez de

Appliquez le pattern

Adapter linterface dune classe pour quelle corresponde linterface attendue par un client

ADAPTER

Fournir une interface simple pour un ensemble de classes

FACADE

Dfinir une interface qui sapplique la fois des objets individuels


et des groupes dobjets

COMPOSITE

Dcoupler une abstraction de son implmentation de sorte que les


deux puissent varier indpendamment

BRIDGE

pattern Livre Page 20 Vendredi, 9. octobre 2009 10:31 10

20

Partie I

Patterns dinterface

Lobjectif de chaque pattern de conception est de rsoudre un problme dans un


certain contexte. Les patterns dinterface conviennent dans des contextes o vous
avez besoin de dfinir ou de redfinir laccs aux mthodes dune classe ou dun groupe
de classes. Par exemple, lorsque vous disposez dune classe qui accomplit un
service ncessaire, mais dont les noms de mthodes ne correspondent pas aux attentes
dun client, vous pouvez appliquer le pattern ADAPTER.

ADAPTER:
une classe qui accomplit un service ncessaire, mais les
noms des mthodes ne correspondent pas aux attentes
du client

pattern Livre Page 21 Vendredi, 9. octobre 2009 10:31 10

3
ADAPTER
Un objet est un client lorsquil a besoin dappeler votre code. Dans certains cas,
votre code existe dj et le dveloppeur peut crer le client de manire ce quil
utilise les interfaces de vos objets. Dans dautres, le client peut tre dvelopp indpendamment de votre code. Par exemple, un programme de simulation de fuse
pourrait tre conu pour utiliser les informations techniques que vous fournissez,
mais une telle simulation aurait sa propre dfinition du comportement que doit avoir une
fuse. Si une classe existante est en mesure dassurer les services requis par un client
mais que ses noms de mthodes diffrent, vous pouvez appliquer le pattern ADAPTER.
Lobjectif du pattern ADAPTER est de fournir linterface quun client attend en
utilisant les services dune classe dont linterface est diffrente.

Adaptation une interface


Le dveloppeur dun client peut avoir prvu les situations o vous aurez besoin
dadapter votre code au sien. Cela est vident sil a fourni une interface qui dfinit
les services dont le code client a besoin, comme dans lexemple de la Figure 3.1.
Une classe cliente invoque une mthode mthodeRequise() dclare dans une
interface. Supposez que vous avez trouv une classe existante avec une mthode
nomme par exemple mthodeUtile() capable de rpondre aux besoins du client.
Vous pouvez alors adapter cette classe au client en crivant une classe qui tend
ClasseExistante, implmente InterfaceRequise et redfinit mthodeRequise() de sorte quelle dlgue ses demandes mthodeUtile().
La classe NouvelleClasse est un exemple de ADAPTER. Une instance de cette
classe est une instance de InterfaceRequise. En dautres termes, NouvelleClasse
rpond aux besoins du client.

pattern Livre Page 22 Vendredi, 9. octobre 2009 10:31 10

22

Partie I

Patterns dinterface

Figure 3.1
Lorsque le dveloppeur du code
client dfinit prcisment les besoins
du client, vous pouvez
remplir le contrat
dfini par linterface
en adaptant le code
existant.

Client

interface
InterfaceRequise

mthodeRequise()

ClasseExistante

mthodeUtile()

NouvelleClasse

mthodeRequise()

Pour prendre un exemple plus concret, imaginez que vous travailliez avec un
package qui simule le vol et le minutage de fuses comme celles fabriques par
Oozinoz. Ce package inclut un simulateur dvnements qui couvre les effets du
lancement de plusieurs fuses, ainsi quune interface qui spcifie le comportement
dune fuse. La Figure 3.2 illustre ce package.
Vous disposez dune classe PhysicalRocket que vous voulez inclure dans la simulation. Cette classe possde des mthodes qui correspondent approximativement au
comportement requis par le simulateur. Vous pouvez donc appliquer ADAPTER en
drivant de PhysicalRocket une sous-classe qui implmente linterface RocketSim. La Figure 3.3 illustre partiellement cette conception.
La classe PhysicalRocket contient les informations dont le simulateur a besoin,
mais ses mthodes ne correspondent pas exactement celles que le programme de
simulation dclare dans linterface RocketSim. Cette diffrence tient au fait que le simulateur possde une horloge interne et actualise occasionnellement les objets simuls
en invoquant une mthode setSimTime(). Pour adapter la classe PhysicalRocket aux
exigences du simulateur, un objet OozinozRocket pourrait utiliser une variable
dinstance time et la passer aux mthodes de la classe PhysicalRocket lorsque
ncessaire.

pattern Livre Page 23 Vendredi, 9. octobre 2009 10:31 10

Chapitre 3

ADAPTER

com.oozinoz.simulation

EventSim

interface
RocketSim

getMass():double

Figure 3.2
Le package Simulation dfinit clairement ses exigences pour simuler le vol dune fuse.

PhysicalRocket
interface
RocketSim

PhysicalRocket(
burnArea:double,
burnRate:double,
fuelMass:double,
totalMass:double)
getBurnTime():double

getMass():double
getThrust():double
setSimTime(t:double)

getMass(t:double):double
getThrust(t:double):double

OozinozRocket

Figure 3.3
Une fois complt, ce diagramme reprsentera la conception dune classe qui adapte
la classe PhysicalRocket pour rpondre aux exigences de linterface RocketSim.

23

pattern Livre Page 24 Vendredi, 9. octobre 2009 10:31 10

24

Partie I

Patterns dinterface

Exercice 3.1
Compltez le diagramme de la Figure 3.3 en faisant en sorte que la classe OozinozRocket permette un objet PhysicalRocket de prendre part une simulation
en tant quobjet RocketSim. Partez du principe que vous ne pouvez modifier ni
RocketSim ni PhysicalRocket.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.


Le code de PhysicalRocket est un peu complexe car il runit toutes les caractristiques physiques dont se sert Oozinoz pour modliser une fuse. Mais cest exactement la logique que nous voulons rutiliser. La classe adaptateur OozinozRocket
traduit simplement les appels pour utiliser les mthodes de sa super-classe. Le code
de cette nouvelle sous-classe pourrait ressembler ce qui suit :
package com.oozinoz.firework;
import com.oozinoz.simulation.*;
public class OozinozRocket
extends PhysicalRocket implements RocketSim {
private double time;
public OozinozRocket(
double burnArea, double burnRate,
double fuelMass, double totalMass) {
super(burnArea, burnRate, fuelMass, totalMass);
}
public double getMass() {
// Exercice !
}
public double getThrust() {
// Exercice !
}
public void setSimTime(double time) {
this.time = time;
}
}

Exercice 3.2
Compltez le code de la classe OozinozRocket en dfinissant les mthodes
getMass() et getThrust().

pattern Livre Page 25 Vendredi, 9. octobre 2009 10:31 10

Chapitre 3

ADAPTER

25

Lorsquun client dfinit ses attentes dans une interface, vous pouvez appliquer
ADAPTER en fournissant une classe qui implmente cette interface et tend une classe
existante. Il se peut aussi que vous puissiez appliquer ce pattern mme en labsence
dune telle interface, auquel cas il convient dutiliser un adaptateur dobjet.

Adaptateurs de classe et dobjet


Les conceptions des Figures 3.1 et 3.3 sont des adaptateurs de classe, cest--dire que
ladaptation procde de la drivation de sous-classes. Dans une telle conception, la
nouvelle classe adaptateur implmente linterface dsire et tend une classe existante. Cette approche ne fonctionne pas toujours, notamment lorsque lensemble de
mthodes que vous voulez adapter nest pas spcifi dans une interface. Dans ce
cas, vous pouvez crer un adaptateur dobjet, cest--dire un adaptateur qui utilise
la dlgation plutt que la drivation de sous-classes. La Figure 3.4 illustre cette
conception (comparez-la aux diagrammes prcdents).

Client

ClasseExistante

ClasseRequise

mthodeUtile()

mthodeRequise()

NouvelleClasse

mthodeRequise()

Figure 3.4
Vous pouvez crer un adaptateur dobjet en drivant la sous-classe dont vous avez besoin
et en remplissant les contrats des mthodes en vous appuyant sur un objet dune classe existante.

pattern Livre Page 26 Vendredi, 9. octobre 2009 10:31 10

26

Partie I

Patterns dinterface

La classe NouvelleClasse est un exemple de ADAPTER. Une instance de cette


classe est une instance de ClasseRequise. En dautres termes, NouvelleClasse
rpond aux besoins du client. Elle peut adapter la classe ClasseExistante pour
satisfaire le client en utilisant une instance de cette classe.
Pour prendre un exemple plus concret, imaginez que le package de simulation fonctionne directement avec une classe Skyrocket, sans spcifier dinterface dfinissant les comportements ncessaires pour la simulation. La Figure 3.5 illustre
cette classe.
Figure 3.5
Dans cette conception-ci, le
package com.oozinoz.simulation ne spcifie pas linterface dont il a besoin pour
modliser une fuse.

com.oozinoz.simulation

EventSim

Skyrocket

Skyrocket(
mass:double,
thrust:double
burnTime:double)
getMass():double
getThrust():double
setSimTime(t:double)

La classe Skyrocket utilise un modle physique assez rudimentaire. Par exemple,


elle part du principe que la fuse se consume entirement mesure que son carburant brle. Supposez que vous vouliez appliquer le modle plus sophistiqu offert
par la classe PhysicalRocket dOozinoz. Pour adapter la logique de cette classe
la simulation, vous pourriez crer une classe OozinozSkyrocket en tant quadaptateur dobjet qui tend Skyrocket et utilise un objet PhysicalRocket, comme le
montre la Figure 3.6.

pattern Livre Page 27 Vendredi, 9. octobre 2009 10:31 10

Chapitre 3

ADAPTER

Skyrocket
#simTime:double
...
Skyrocket(
mass:double,
thrust:double
burnTime:double)
getMass():double
getThrust():double

27

PhysicalRocket

PhysicalRocket(
burnArea:double,
burnRate:double,
fuelMass:double,
totalMass:double)
getBurnTime():double
getMass(t:double):double
getThrust(t:double):double

setSimTime(t:double)

OozinozSkyrocket

Figure 3.6
Une fois complt, ce diagramme reprsentera la conception dun adaptateur dobjet
qui sappuie sur les informations dune classe existante pour satisfaire le besoin dun client
dutiliser un objet Skyrocket.

En tant quadaptateur dobjet, la classe OozinozSkyrocket tend Skyrocket, et


non PhysicalRocket. Cela permet un objet OozinozSkyrocket de servir de
substitut chaque fois que le client requiert un objet Skyrocket. La classe Skyrocket
supporte la drivation de sous-classes en dfinissant sa variable simTime comme
tant protected.
Exercice 3.3
Compltez le diagramme de la Figure 3.6 en faisant en sorte que des objets
OozinozSkyrocket puissent servir dobjets Skyrocket.

pattern Livre Page 28 Vendredi, 9. octobre 2009 10:31 10

28

Partie I

Patterns dinterface

Le code de la classe OozinozSkyrocket pourrait ressembler ce qui suit :


package com.oozinoz.firework;
import com.oozinoz.simulation.*;
public class OozinozSkyrocket extends Skyrocket {
private PhysicalRocket rocket;
public OozinozSkyrocket(PhysicalRocket r) {
super(
r.getMass(0),
r.getThrust(0),
r.getBurnTime());
rocket = r;
}
public double getMass() {
return rocket.getMass(simTime);
}
public double getThrust() {
return rocket.getThrust(simTime);
}
}

La classe OozinozSkyrocket vous permet de fournir un objet OozinozSkyrocket


chaque fois que le package requiert un objet Skyrocket. En gnral, les adaptateurs
dobjet rsolvent, partiellement du moins, le problme pos par ladaptation dun
objet une interface qui na pas t expressment dfinie.
Exercice 3.4
Citez une raison pour laquelle la conception dadaptateur dobjet utilise pa r
la classe OozinozSkyrocket est plus fragile que lapproche avec adaptateur de
classe.

Ladaptateur dobjet pour la classe Skyrocket est une conception plus risque que
ladaptateur de classe qui implmente linterface RocketSim. Mais il ne faut pas
trop se plaindre. Au moins, aucune mthode na t dfinie comme tant final, ce
qui nous aurait empchs de la redfinir.

pattern Livre Page 29 Vendredi, 9. octobre 2009 10:31 10

Chapitre 3

ADAPTER

29

Adaptation de donnes pour un widget JTable


Laffichage de donnes sous forme de table donne lieu un exemple courant
dadaptateur dobjet. Swing fournit le widget JTable pour afficher des tables. Les
concepteurs de ce widget ne savaient naturellement pas quelles donnes il servirait
afficher. Aussi, plutt que de coder en dur certaines structures de donnes, ils ont
prvu une interface appele TableModel (voir Figure 3.7) dont dpend le fonctionnement de JTable. Il vous revient ensuite de crer un adaptateur pour que vos
donnes soient conformes TableModel.
Figure 3.7
La classe JTable
est un composant
Swing qui affiche
dans une table de GUI
les donnes dune
implmentation
de TableModel.

JTable

<<interface>>
TableModel
addTableModelListener()
getColumnClass()
getColumnCount()
getColumnName()
getRowCount()
getValueAt()
isCellEditable()
removeTableModelListener()
setValueAt()

Nombre des mthodes de TableModel suggrent la possibilit dune implmentation par dfaut. Heureusement, le JDK (Java Development Kit) inclut une classe
abstraite qui fournit des implmentations par dfaut pour toutes les mthodes
de cette interface lexception de celles qui sont trs spcifiques un domaine.
La Figure 3.8 illustre cette classe.
Imaginez que vous souhaitiez lister quelques fuses dans une table en utilisant une
interface utilisateur Swing. Comme le montre la Figure 3.9, vous pourriez crer
une classe RocketTableModel qui adapte un tableau de fuses linterface attendue par TableModel.
La classe RocketTableModel doit tendre AbstractTableModel puisque cette
dernire est une classe et non une interface. Lorsque linterface cible de ladaptation est supporte par une classe abstraite que vous souhaitez utiliser, vous devez

pattern Livre Page 30 Vendredi, 9. octobre 2009 10:31 10

30

Partie I

Patterns dinterface

Figure 3.8
La classe AbstractTableModel prvoit
des implmentations
par dfaut pour
presque toutes
les mthodes de
TableModel.

javax.swing.table

<<interface>>
TableModel

AbstractTableModel

getColumnCount()
getRowCount()
getValueAt()

<<interface>>
TableModel
Rocket

AbstractTableModel

getName():String
getPrice():Dollars
getApogee():double

RocketTableModel
#rockets[]:Rocket
#columnNames[]:String
RocketTableModel(rockets[]:Rocket)
getColumnCount()
getColumnName(i:int)
getRowCount()
getValueAt(row:int,col:int)

Figure 3.9
La classe RocketTableModel adapte linterface TableModel la classe Rocket
du domaine Oozinoz.

pattern Livre Page 31 Vendredi, 9. octobre 2009 10:31 10

Chapitre 3

ADAPTER

31

crer un adaptateur dobjet. Dans notre exemple, une autre raison qui justifie de ne
pas recourir un adaptateur de classe est que RocketTableModel nest ni un type ni
un sous-type de Rocket. Lorsquune classe adaptateur doit tirer ses informations de
plusieurs objets, elle est habituellement implmente en tant quadaptateur dobjet.
Retenez la diffrence : un adaptateur de classe tend une classe existante et implmente une interface cible tandis quun adaptateur dobjet tend une classe cible et
dlgue une classe existante.
Une fois la classe RocketTableModel cre, vous pouvez facilement afficher des informations sur les fuses dans un objet Swing JTable, comme illustr Figure 3.10.
Figure 3.10
Une instance
de JTable contenant
des donnes sur
les fuses.

package app.adapter;
import javax.swing.table.*;
import com.oozinoz.firework.Rocket;
public class RocketTableModel extends AbstractTableModel {
protected Rocket[] rockets;
protected String[] columnNames =
new String[] { "Name", "Price", "Apogee" };
public RocketTableModel(Rocket[] rockets) {
this.rockets = rockets;
}
public int getColumnCount() {
// Exercice !
}
public String getColumnName(int i) {
// Exercice !
}
public int getRowCount() {
// Exercice !
}
public Object getValueAt(int row, int col) {
// Exercice !
}
}

pattern Livre Page 32 Vendredi, 9. octobre 2009 10:31 10

32

Partie I

Patterns dinterface

Exercice 3.5
Compltez le code des mthodes de RocketTableModel qui adaptent un tableau
dobjets Rocket pour quil serve dinterface TableModel.
Pour obtenir le rsultat de la Figure 3.10, vous pouvez crer deux objets fuse, les
placer dans un tableau, crer une instance de RocketTableModel partir du tableau,
et utiliser des classes Swing pour afficher ce dernier. La classe ShowRocketTable
en donne un exemple :
package app.adapter;
import java.awt.Component;
import java.awt.Font;
import javax.swing.*;
import com.oozinoz.firework.Rocket;
import com.oozinoz.utility.Dollars;
public class ShowRocketTable {
public static void main(String[] args) {
setFonts();
JTable table = new JTable(getRocketTable());
table.setRowHeight(36);
JScrollPane pane = new JScrollPane(table);
pane.setPreferredSize(
new java.awt.Dimension(300, 100));
display(pane, " Rockets");
}
public static void display(Component c, String title) {
JFrame frame = new JFrame(title);
frame.getContentPane().add(c);
frame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private static RocketTableModel getRocketTable() {
Rocket r1 = new Rocket(
"Shooter", 1.0, new Dollars(3.95), 50.0, 4.5);
Rocket r2 = new Rocket(
"Orbit", 2.0, new Dollars(29.03), 5000, 3.2);
return new RocketTableModel(new Rocket[] { r1, r2 });
}

pattern Livre Page 33 Vendredi, 9. octobre 2009 10:31 10

Chapitre 3

ADAPTER

33

private static void setFonts() {


Font font = new Font("Dialog", Font.PLAIN, 18);
UIManager.put("Table.font", font);
UIManager.put("TableHeader.font", font);
}
}

La classe ShowRocketTable, constitue elle-mme de moins de vingt instructions,


figure au-dessus de milliers dautres instructions qui collaborent pour produire un
composant table au sein dun environnement GUI (Graphical User Interface).
La classe JTable peut grer pratiquement tous les aspects de laffichage dune table
mais ne peut savoir lavance quelles donnes vous voudrez prsenter. Pour vous
permettre de fournir les donnes dont elle a besoin, elle vous donne la possibilit
dappliquer le pattern ADAPTER. Pour utiliser JTable, vous implmentez linterface
TableModel quelle attend, ainsi quune classe fournissant les donnes afficher.

Identification dadaptateurs
Le Chapitre 2 a voqu lintrt que prsente la classe WindowAdapter. La classe
MouseAdapter illustre Figure 3.11 est un autre exemple de classe stub (cest-dire qui ne dfinit pas les mthodes requises par linterface quelle implmente).
Figure 3.11
La classe MouseAdapter implmente
la classe MouseListener en laissant
vide le corps de ses
mthodes.

<<interface>>
MouseListener
mouseClicked()
mouseEntered()
mouseExited()
mousePressed()
mouseReleased()

MouseAdapter

mouseClicked()
mouseEntered()
mouseExited()
mousePressed()
mouseReleased()

Exercice 3.6
Pouvez-vous considrer que vous appliquez le pattern ADAPTER lorsque vous
utilisez la classe MouseAdapter? Expliquez votre rponse.

pattern Livre Page 34 Vendredi, 9. octobre 2009 10:31 10

34

Partie I

Patterns dinterface

Rsum
Le pattern ADAPTER vous permet dutiliser une classe existante pour rpondre aux
exigences dune classe cliente. Lorsquun client spcifie ses exigences dans une
interface, vous pouvez gnralement crer une nouvelle classe qui implmente
linterface et tend la classe existante. Cette approche produit un adaptateur de
classe qui traduit les appels du client en appels des mthodes de la classe existante.
Lorsque le client ne spcifie pas linterface dont il a besoin, vous pouvez quand
mme appliquer ADAPTER en crant une sous-classe cliente qui utilise une instance
de la classe existante. Cette approche produit un adaptateur dobjet qui transmet les
appels du client cette instance. Elle nest pas dnue de risques, surtout si vous
omettez (ou tes dans limpossibilit) de redfinir toutes les mthodes que le client
pourrait appeler.
Le composant JTable dans Swing est un bon exemple de classe laquelle ses
concepteurs ont appliqu le pattern ADAPTER. Il se prsente en tant que client ayant
besoin des informations de table telles que dfinies par linterface TableModel.
Il vous est ainsi plus facile dcrire un adaptateur qui alimente la table en donnes
partir dobjets du domaine, tels que des instances de la classe Rocket.
Pour utiliser JTable, on cre souvent un adaptateur dobjet qui dlgue les appels
aux instances dune classe existante. Deux aspects de JTable font quil est peu
probable quun adaptateur de classe soit utilis. Premirement, ladaptateur est
habituellement cr en tendant AbstractTableModel, auquel cas il nest pas
possible dtendre galement la classe existante. Deuximement, la classe JTable
requiert un ensemble dobjets, et un adaptateur dobjet convient mieux pour adapter
des informations tires de plusieurs objets.
Lorsque vous concevez vos systmes, considrez la puissance et la souplesse offertes
par une architecture qui tire parti de ADAPTER.

pattern Livre Page 35 Vendredi, 9. octobre 2009 10:31 10

4
FACADE
Un gros avantage de la POO est quelle permet dviter le dveloppement de programmes monolithiques au code irrmdiablement enchevtr. Dans un systme OO,
une application est, idalement, une classe minimale qui unit les comportements
dautres classes groupes en kits doutils rutilisables. Un dveloppeur de kits
doutils ou de sous-systmes cre souvent des packages de classes bien conues
sans fournir dapplications les liant. Les packages dans les bibliothques de classes
Java se prsentent gnralement ainsi. Ce sont des kits doutils partir desquels
vous pouvez tisser une varit infinie dapplications spcifiques.
La rutilisabilit des kits doutils saccompagne dun inconvnient : lapplicabilit
diverse des classes dans un sous-systme OO met la disposition du dveloppeur
une quantit tellement impressionnante doptions quil lui est parfois difficile de
savoir par o commencer. Un environnement de dveloppement intgr, ou IDE
(Integrated Development Environment), tel quEclipse, peut affranchir le dveloppeur dune certaine part de la complexit du kit, mais il ajoute en revanche une
grande quantit de code que le dveloppeur ne souhaitera pas forcment maintenir.
Une autre approche pour simplifier lemploi dun kit doutils est de fournir une
faade une petite quantit de code qui permet un usage typique peu de frais des
classes de la bibliothque. Une faade est elle-mme une classe avec un niveau de
fonctionnalits situ entre le kit doutils et une application complte, proposant un
emploi simplifi des classes dun package ou dun sous-systme.
Lobjectif du pattern FACADE est de fournir une interface simplifiant lemploi
dun sous-systme.

pattern Livre Page 36 Vendredi, 9. octobre 2009 10:31 10

36

Partie I

Patterns dinterface

Faades, utilitaires et dmos


Une classe de faade peut ne contenir que des mthodes statiques, auquel cas elle
est appele un utilitaire dans UML (Guide de lutilisateur UML) [Booch,
Rumbaugh, et Jacobsen 1999]. Nous introduirons par la suite une classe UI (User
Interface), qui aurait pu recevoir seulement des mthodes statiques, bien que
procder ainsi aurait empch par la suite la redfinition des mthodes dans les
sous-classes.
Une dmo est un exemple qui montre comment employer une classe ou un soussystme. A cet gard, la valeur des dmos peut tre vue comme tant gale celle
des faades.
Exercice 4.1
Indiquez deux diffrences entre une dmo et une faade.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.

Le package javax.swing contient JOptionPane, une classe qui permet dafficher


facilement une bote de dialogue standard. Par exemple, le code suivant affiche et
raffiche une bote de dialogue jusqu ce que lutilisateur clique sur le bouton Yes,
comme illustr Figure 4.1.
package app.facade;
import javax.swing.*;
import java.awt.Font;
public class ShowOptionPane {
public static void main(String[] args) {
Font font = new Font("Dialog", Font.PLAIN, 18);
UIManager.put("Button.font", font);
UIManager.put("Label.font", font);
int option;
do {
option = JOptionPane.showConfirmDialog(
null,
"Had enough?",

pattern Livre Page 37 Vendredi, 9. octobre 2009 10:31 10

Chapitre 4

FACADE

37

"A Stubborn Dialog",


JOptionPane.YES_NO_OPTION);
} while (option == JOptionPane.NO_OPTION);
}
}

Figure 4.1
La classe JOptionPane
facilite laffichage de
botes de dialogue.

Exercice 4.2
La classe JOptionPane facilite laffichage dune bote de dialogue. Indiquez si
cette classe est une faade, un utilitaire ou une dmo. Justifiez votre rponse.
Exercice 4.3
Peu de faades apparaissent dans les bibliothques de classes Java. Pour quelle
raison ?

Refactorisation pour appliquer FACADE


Les faades sont souvent introduites hors de la phase de dveloppement normal
dune application. Lors de la tche de sparation des problmes dans votre code en
diverses classes, vous pouvez refactoriser, ou restructurer, le systme en extrayant
une classe dont la tche principale est de fournir un accs simplifi un soussystme. Considrez un exemple remontant aux premiers jours dOozinoz, o
aucun standard de dveloppement de GUI navait encore t adopt. Supposez que
vous vous retrouviez examiner une application quun dveloppeur a cre pour
afficher la trajectoire dune bombe arienne nayant pas explos. La Figure 4.2
illustre cette classe.
Les bombes sont prvues pour exploser trs haut dans le ciel en produisant des
effets spectaculaires. Parfois, une bombe nexplose pas du tout. Dans ce cas,
son retour sur terre devient intressant. A la diffrence dune fuse, une bombe
nest pas auto-propulse. Aussi, si vous ignorez les effets dus au vent et la rsistance de lair, la trajectoire dune bombe ayant un rat est une simple parabole.

pattern Livre Page 38 Vendredi, 9. octobre 2009 10:31 10

38

Partie I

Patterns dinterface

Figure 4.2
La classe ShowFlight
affiche la trajectoire
dune bombe arienne
ayant un rat.

JPanel

ShowFlight

ShowFlight()
main()
createTitledBorder(:String)
createTitledPanel(title:String,p:JPanel):JPanel
getStandardFont():Font
paintComponent(:Graphics)

La Figure 4.3 illustre une capture dcran de la fentre qui apparat lorsque vous
excutez ShowFlight.main().
Figure 4.3
Lapplication ShowFlight montre
lendroit o une
bombe qui na pas
explos retombe.

La classe ShowFlight prsente un problme : elle mle trois objectifs. Son objectif
principal est dagir en tant que panneau daffichage dune trajectoire. Un deuxime
objectif de cette classe est dagir en tant quapplication complte, incorporant et
affichant le panneau de trajectoire dans un cadre compos dun titre. Enfin, son
dernier objectif est de calculer la trajectoire parabolique que suit la bombe
dfaillante, le calcul tant ralis dans paintComponent():
protected void paintComponent(Graphics g) {
super.paintComponent(g); // dessine larrire-plan

pattern Livre Page 39 Vendredi, 9. octobre 2009 10:31 10

Chapitre 4

FACADE

39

int nPoint = 101;


double w = getWidth() - 1;
double h = getHeight() - 1;
int[] x = new int[nPoint];
int[] y = new int[nPoint];
for (int i = 0; i < nPoint; i++) {
// t va de 0 1
double t = ((double) i) / (nPoint - 1);
// x va de 0 w
x[i] = (int) (t * w);
// y est h pour t = 0 et t = 1, et 0 pour t = 0,5
y[i] = (int) (4 * h * (t - .5) * (t - .5));
}
g.drawPolyline(x, y, nPoint);
}

Voyez lencadr intitul "Equations paramtriques" plus loin dans ce chapitre pour
une explication de la faon dont le code dfinit les valeurs x et y de la trajectoire.
Il nest pas ncessaire davoir un constructeur. Il existe des mthodes statiques
utilitaires qui permettent dincorporer un titre dans un cadre et de dfinir une police
standard.
public static TitledBorder createTitledBorder(String title){
TitledBorder tb = BorderFactory.createTitledBorder(
BorderFactory.createBevelBorder(BevelBorder.RAISED),
title,
TitledBorder.LEFT,
TitledBorder.TOP);
tb.setTitleColor(Color.black);
tb.setTitleFont(getStandardFont());
return tb;
}
public static JPanel createTitledPanel(
String title, JPanel in) {
JPanel out = new JPanel();
out.add(in);
out.setBorder(createTitledBorder(title));
return out;
}
public static Font getStandardFont() {
return new Font("Dialog", Font.PLAIN, 18);
}

Notez que la mthode createTitledPanel() place le composant reu lintrieur


dune bordure en relief pour produire un lger espace de remplissage, empchant la
courbe de la trajectoire de toucher les bords du panneau. La mthode main() ajoute

pattern Livre Page 40 Vendredi, 9. octobre 2009 10:31 10

40

Partie I

Patterns dinterface

aussi lobjet de formulaire un espace de remplissage quil utilise pour contenir les
composants de lapplication :
public static void main(String[] args) {
ShowFlight flight = new ShowFlight();
flight.setPreferredSize(new Dimension(300, 200));
JPanel panel = createTitledPanel("Flight Path", flight);
JFrame frame = new JFrame("Flight Path for Shell Duds");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}

Lexcution de ce programme produit la fentre illustre Figure 4.3.


Equations paramtriques
Lorsque vous devez dessiner une courbe, il peut tre difficile de dcrire des valeurs y en
tant que fonctions de valeurs x. Les quations paramtriques permettent de dfinir ces
deux types de valeurs en fonction dun troisime paramtre. Plus spcifiquement, vous
pouvez dfinir un temps t allant de 0 1 alors que la courbe est dessine, et dfinir x et y
en tant que fonctions du paramtre t.
Par exemple, supposez que le trac de la trajectoire parabolique doive stendre sur la
largeur w dun objet Graphics. Une quation paramtrique pour x est simple :
x=w*t

Notez que pendant que t passe de 0 1, x va de 0 w.


Les valeurs y dune parabole doivent varier avec le carr de la valeur de t, et les valeurs de y
doivent augmenter en allant vers le bas de lcran. Pour une trajectoire parabolique, la
valeur y devrait tre gale 0 au temps t=0,5. Aussi pouvons-nous crire lquation
initiale comme suit :
y=k*(t0,5)*(t0,5)

Ici, k reprsente une constante que nous devons encore dterminer. Lquation prvoit y 0
lorsque t=0,5, et avec une valeur identique pour t=0 et t=1. A ces deux instants t, y
devrait tre gale h, la hauteur de la zone daffichage. Avec un peu de manipulation algbrique, vous pouvez trouver lquation complte pour y:
y=4*h*(t0,5)*(t0,5)

La Figure 4.3 illustre le rsultat des quations en action.


Un autre avantage des quations paramtriques est quelles ne posent pas de problme
pour dessiner des courbes qui possdent plus dune valeur y pour une valeur x. Considrez
le dessin dun cercle. Lquation dun cercle avec un rayon de 1 est pose comme suit :
x2+y2=r2

pattern Livre Page 41 Vendredi, 9. octobre 2009 10:31 10

Chapitre 4

FACADE

41

ou :
y=+sqrt(r 2x2)

Devoir grer le fait que deux valeurs y sont produites pour chaque valeur x est compliqu.
Il est aussi difficile dajuster ces valeurs pour dessiner correctement la courbe lintrieur
des dimensions h (hauteur) et w (largeur) dun objet Graphics. Les coordonnes polaires
simplifient la fonction pour un cercle :
x=r*cos(theta)
y=r*sin(theta)

Ces formules sont des quations paramtriques qui dfinissent x et y en tant que fonctions
dun nouveau paramtre theta. La variable theta reprsente la courbure dun arc qui varie
de 0 2*pi alors que le cercle est dessin. Vous pouvez dfinir le rayon dun cercle de
manire quil sinscrive lintrieur des dimensions dun objet Graphics. Quelques quations paramtriques suffisent pour dessiner un cercle dans les limites dun tel objet, comme
le montre lexemple suivant :
theta=2*pi*t
r=min(w,h)/2
x=w/2+r*cos(theta)
y=h/2-r*sin(theta)

La transposition de ces quations dans le code produit le cercle illustr Figure 4.4 le code
qui produit cet affichage se trouve dans lapplication ShowCircle sur le site oozinoz.com.
Le code dessinant un cercle est une transposition relativement directe des formules mathmatiques. Il y a toutefois une subtilit dans ce sens que le code rduit la hauteur et la
largeur de lobjet Graphics car les pixels sont numrots de 0 h1 et de 0 w1.
package app.facade;
import javax.swing.*;
import java.awt.*;
import com.oozinoz.ui.SwingFacade;
public class ShowCircle extends JPanel {
public static void main(String[] args) {
ShowCircle sc = new ShowCircle();
sc.setPreferredSize(new Dimension(300, 300));
SwingFacade.launch(sc, "Circle");
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int nPoint = 101;
double w = getWidth() - 1;
double h = getHeight() - 1;
double r = Math.min(w, h) / 2.0;
int[] x = new int[nPoint];
int[] y = new int[nPoint];
for (int i = 0; i < nPoint; i++) {
double t = ((double) i) / (nPoint - 1);
double theta = Math.PI * 2.0 * t;
x[i] = (int) (w / 2 + r * Math.cos(theta));
y[i] = (int) (h / 2 - r * Math.sin(theta));

pattern Livre Page 42 Vendredi, 9. octobre 2009 10:31 10

42

Partie I

Patterns dinterface

}
g.drawPolyline(x, y, nPoint);
}
}

Exprimer les fonctions x et y par rapport t vous permet de diviser les tches de dtermination des valeurs x et y. Cest souvent plus simple que de devoir dfinir y en fonction de x et
cela facilite souvent la transposition de x et de y en coordonnes dun objet Graphics. Les
quations paramtriques simplifient galement le dessin de courbes o y nest pas une
fonction monovalue de x.
Figure 4.4
Les quations paramtriques
simplifient la modlisation
de courbes lorsque y nest pas
une fonction monovalue
de x.

Le code de la classe ShowFlight fonctionne, mais vous pouvez le rendre plus facile
maintenir et plus rutilisable en le retravaillant pour crer des classes se concentrant sur des problmes distincts. Supposez quaprs une rvision du code, vous
dcidiez :
m

Dintroduire une classe Function avec une mthode f() qui accepte un type
double (une valeur de temps) et retourne un double (la valeur de la fonction).
De dplacer le code dessinant la courbe de la classe ShowFlight vers une classe
PlotPanel, mais de le modifier pour quil utilise des objets Function pour les
valeurs x et y. Dfinissez le constructeur PlotPanel de manire quil accepte
deux instances de Function ainsi que le nombre de points dessiner.
De dplacer la mthode createTitledPanel() vers la classe utilitaire UI pour
construire un panneau avec un titre, comme le fait dj la classe ShowFlight.

pattern Livre Page 43 Vendredi, 9. octobre 2009 10:31 10

Chapitre 4

FACADE

43

Exercice 4.4
Compltez le diagramme de la Figure 4.5 pour prsenter le code de ShowFlight
rparti en trois types : une classe Function, une classe PlotPanel qui dessine
deux fonctions paramtriques, et une classe de faade UI. Dans votre nouvelle
conception, faites en sorte que ShowFlight2 cre un objet Function pour les
valeurs y et incorpore une mthode main() qui lance lapplication.

Figure 4.5
JPanel

Lapplication de dessin
dune trajectoire parabolique restructure en
trois classes sacquittant
chacune dune tche.
ShowFlight2

PlotPanel

UI

Function

Aprs ces changements, la classe Function dfinit lapparence des quations paramtriques. Supposez que vous criez un package com.oozinoz.function pour
contenir la classe Function et dautres types. Le cur de Function.java pourrait
tre :
public abstract double f(double t);

pattern Livre Page 44 Vendredi, 9. octobre 2009 10:31 10

44

Partie I

Patterns dinterface

La classe PlotPanel rsultant de la restructuration du code na quun travail raliser :


afficher une paire dquations paramtriques :
package com.oozinoz.ui;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
import com.oozinoz.function.Function;
public class PlotPanel extends JPanel {
private int points;
private int[] xPoints;
private int[] yPoints;
private Function xFunction;
private Function yFunction;
public PlotPanel(
int nPoint, Function xFunc, Function yFunc) {
points = nPoint;
xPoints = new int[points];
yPoints = new int[points];
xFunction = xFunc;
yFunction = yFunc;
setBackground(Color.WHITE);
}
protected void paintComponent(Graphics graphics) {
double w = getWidth() - 1;
double h = getHeight() - 1;
for (int i = 0; i < points; i++) {
double t = ((double) i) / (points - 1);
xPoints[i] = (int) (xFunction.f(t) * w);
yPoints[i] = (int) (h * (1 - yFunction.f(t)));
}
graphics.drawPolyline(xPoints, yPoints, points);
}
}

Notez que la classe PlotPanel fait maintenant partie du package com.oozinoz.ui,


o rside aussi la classe UI. Aprs restructuration de la classe ShowFlight, la classe
UI inclut aussi les mthodes createTitledPanel() et createTitledBorder().
La classe UI se transforme en faade qui facilite lemploi de composants graphiques
Java.

pattern Livre Page 45 Vendredi, 9. octobre 2009 10:31 10

Chapitre 4

FACADE

45

Une application qui utiliserait ces composants pourrait tre une petite classe ayant
pour seule tche de les mettre en place et de les afficher. Par exemple, le code de la
classe ShowFlight2 se prsente comme suit :
package app.facade;
import
import
import
import
import
import

java.awt.Dimension;
javax.swing.JFrame;
com.oozinoz.function.Function;
com.oozinoz.function.T;
com.oozinoz.ui.PlotPanel;
com.oozinoz.ui.UI;

public class ShowFlight2 {


public static void main(String[] args) {
PlotPanel p = new PlotPanel(
101,
new T(),
new ShowFlight2().new YFunction());
p.setPreferredSize(new Dimension(300, 200));
JFrame frame = new JFrame(
"Flight Path for Shell Duds");
frame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(
UI.NORMAL.createTitledPanel("Flight Path", p));
frame.pack();
frame.setVisible(true);
}
private class YFunction extends Function {
public YFunction() {
super(new Function[] {});
}
public double f(double t) {
// y est 0 pour t = 0 et 1 ; y est 1 pour t = 0,5
return 4 * t * (1 - t);
}
}
}

La classe ShowFlight2 fournit la classe YFunction pour la trajectoire. La mthode


main() met en place linterface utilisateur et laffiche. Lexcution de cette classe
produit les mmes rsultats que la classe ShowFlight originale. La diffrence est
que vous disposez maintenant dune faade rutilisable qui simplifie la cration
dune interface utilisateur graphique dans des applications Java.

pattern Livre Page 46 Vendredi, 9. octobre 2009 10:31 10

46

Partie I

Patterns dinterface

Rsum
Dordinaire, vous devriez refactoriser les classes dun sous-systme jusqu ce que
chaque classe ait un objectif spcifique bien dfini. Cette approche permet dobtenir
un code plus facile maintenir. Il est toutefois possible quun utilisateur de votre
sous-systme puisse prouver des difficults pour trouver par o commencer. Pour
pallier cet inconvnient et aider le dveloppeur exploitant votre code, vous pouvez
fournir des dmos ou des faades avec votre sous-systme. Une dmo est gnralement autonome, cest une application non rutilisable qui montre une faon
dappliquer un sous-systme. Une faade est une classe configurable et rutilisable,
avec une interface de plus haut niveau qui simplifie lemploi du sous-systme.

pattern Livre Page 47 Vendredi, 9. octobre 2009 10:31 10

5
COMPOSITE
Un COMPOSITE est un groupe dobjets contenant aussi bien des lments individuels
que des lments contenant dautres objets. Certains objets contenus reprsentent
donc eux-mmes des groupes et dautres sont des objets individuels appels des
feuilles (leaf). Lorsque vous modlisez un objet composite, deux concepts efficaces
mergent. Une premire ide importante est de concevoir des groupes de manire
englober des lments individuels ou dautres groupes une erreur frquente
est de dfinir des groupes ne contenant que des feuilles. Un autre concept puissant est
la dfinition de comportements communs aux deux types dobjets, individuels et
composites. Vous pouvez unir ces deux ides en dfinissant un type commun aux
groupes et aux feuilles, et en modlisant des groupes de faon quils contiennent un
ensemble dobjets de ce type.
Lobjectif du pattern COMPOSITE est de permettre aux clients de traiter de
faon uniforme des objets individuels et des compositions dobjets.

Un composite ordinaire
La Figure 5.1 illustre une structure composite ordinaire. Les classes Leaf et Composite partagent une interface commune, Component. Un objet Composite sous-tend
dautres objets Composite et Leaf.
Notez que, dans la Figure 5.1, Component est une classe abstraite sans oprations
concrtes. Vous pouvez donc la dfinir en tant quinterface implmente par Leaf et
Composite.

pattern Livre Page 48 Vendredi, 9. octobre 2009 10:31 10

48

Partie I

Patterns dinterface

Figure 5.1
Les concepts essentiels
vhiculs par le pattern
COMPOSITE sont quun
objet composite peut
aussi contenir, outre des
feuilles, dautres objets
composites, et que les
nuds composites et
feuilles partagent une
interface commune.

Component

operation()

Leaf

operation()

Composite

operation()
other()

Exercice 5.1
Pourquoi la classe Composite dans la Figure 5.1 sous-tend-elle un ensemble
dobjets Component et pas simplement un ensemble de feuilles ?

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.

Comportement rcursif dans les objets composites


Les ingnieurs dOozinoz ont peru une composition naturelle dans les machines
quils utilisent pour la production de pices dartifice. Une unit de production se
compose de traves, chaque trave contient une ou plusieurs lignes de montage, et
chaque ligne comprend un ensemble de machines qui collaborent pour produire
des pices et respecter un calendrier. Les dveloppeurs ont modlis ce domaine
en traitant units de production, traves et lignes de montage comme des "machines"
composites, en utilisant le diagramme de classes prsent la Figure 5.2.
Comme le montre la figure, un comportement qui sapplique la fois aux machines
individuelles et aux groupes de machines est getMachineCount(), qui retourne le
nombre de machines pour un composant donn.

pattern Livre Page 49 Vendredi, 9. octobre 2009 10:31 10

Chapitre 5

COMPOSITE

49

Exercice 5.2
Ecrivez le code des mthodes getMachineCount() implmentes respectivement
par Machine et MachineComposite.
Figure 5.2
MachineComponent

La mthode get-

MachineCount() est
un comportement
appropri aussi bien
pour les machines
individuelles que
pour les machines
composites.

getMachineCount()

Machine

MachineComposite
components:List

getMachineCount()
getMachineCount()

Supposez que nous envisagions lajout des mthodes suivantes dans MachineComponent:
Mthode

Comportement

isCompletelyUp()

Indique si toutes les machines dun composant se trouvent dans un tat


actif

stopAll()

Ordonne toutes les machines dun composant darrter leur travail

getOwners()

Retourne un ensemble dingnieurs des mthodes responsables des


machines dun composant

getMaterial()

Retourne tous les produits en cours de traitement dans un composantmachine

Le fonctionnement de chaque mthode dans MachineComponent est rcursif. Par


exemple, le compte de machines dans un objet composite est le total des comptes de
machines de ses composants.

pattern Livre Page 50 Vendredi, 9. octobre 2009 10:31 10

50

Partie I

Patterns dinterface

Exercice 5.3
Pour chaque mthode dclare par MachineComponent, donnez une dfinition
rcursive pour MachineComposite et non rcursive pour Machine.
Mthode

Classe

Dfinition

getMachineCount()

MachineComposite

Retourne la somme des comptes pour


chaque composant de Component

Machine

Retourne 1

MachineComposite

??

Machine

??

MachineComposite

??

Machine

??

MachineComposite

??

Machine

??

MachineComposite

??

Machine

??

isCompletelyUp()

stopAll()

getOwners()

getMaterial()

Objets composites, arbres et cycles


Dans une structure composite, nous pouvons dire quun nud est un arbre sil
contient des rfrences dautres nuds. Cette dfinition est cependant trop vague.
Pour tre plus prcis, nous pouvons appliquer quelques termes de la thorie des
graphes la modlisation dobjets. Nous pouvons commencer par dessiner un
modle objet sous forme dun graphe un ensemble de nuds et dartes avec
des objets en tant que nuds et des rfrences dobjet en tant quartes.
Considrez la modlisation dune analyse (assay) dune prparation (batch) chimique. La classe Assay possde un attribut batch de type Batch, et la classe Batch
comprend un attribut chemical de type Chemical. Supposez quil y ait un certain
objet Assay dont lattribut batch se rfre un objet b de type Batch, et aussi
que lattribut chemical de lobjet b se rfre un objet c de type Chemical.

pattern Livre Page 51 Vendredi, 9. octobre 2009 10:31 10

Chapitre 5

COMPOSITE

51

La Figure 5.3 illustre deux options de diagrammes possibles pour ce modle.


Pour plus dinformations sur lillustration de modles objet avec UML, voyez
lAnnexe D.
Figure 5.3
Deux options de reprsentation possible des
mmes informations :
lobjet a rfrence
lobjet b, et lobjet b
rfrence lobjet c.

a:Assay

b:Batch

c:Chemical

or:

Il y a un chemin, une srie de rfrences dobjets, de a c, car a rfrence b et b


rfrence c. Un cycle est un chemin le long duquel un certain nud apparat deux
fois. Il y aurait un cycle de rfrences dans ce modle si lobjet Chemical c rfrenait en retour lobjet Assay a. Les modles objet sont des graphes orients car
chaque rfrence dobjet possde une direction. La thorie des graphes applique
gnralement le terme arbre pour dsigner certains graphes non orients. Un
graphe orient peut toutefois tre appel un arbre si :
m

Il possde un nud racine qui nest pas rfrenc.

Chaque autre nud na quun parent, le nud qui le rfrence.

Pourquoi se proccuper de cette notion darbre pour un graphe ? Parce que le


pattern COMPOSITE convient particulirement bien aux structures qui suivent cette
forme comme nous le verrons, vous pouvez nanmoins faire fonctionner un
COMPOSITE avec un graphe orient acyclique ou mme un graphe cyclique, mais
cela demande du travail et une attention supplmentaires.
Le modle objet illustr la Figure 5.3 est un simple arbre. Lorsque les modles
sont de plus grande taille, il peut tre difficile de savoir sil sagit dun arbre. La
Figure 5.4 prsente le modle objet dune usine, appel plant, cest--dire un objet
MachineComposite. Cette usine comprend une trave compose de trois machines : un mixeur (mixer), une presse (press) et un assembleur (assembler). Le
modle montre aussi que la liste de composants-machines de lobjet plant contient
une rfrence directe au mixeur.

pattern Livre Page 52 Vendredi, 9. octobre 2009 10:31 10

52

Partie I

Patterns dinterface

:List

plant:MachineComposite

bay:MachineComposite

:List

mixer:Machine

press

assembler

Figure 5.4
Un modle objet formant un graphe qui nest ni cyclique, ni un arbre.

Le graphe dobjets de la Figure 5.4 ne comprend pas de cycle, mais ce nest pas un
arbre car deux objets rfrencent le mme objet mixer. Si nous supprimons ou ne
tenons pas compte de lobjet plant et de sa liste, lobjet bay est la racine de larbre.
Les mthodes qui sappliquent des composites peuvent avoir des dfauts si elles
supposent que tous les composites sont des arbres mais que le systme accepte des
composites qui nen sont pas. LExercice 5.2 demandait la dfinition dune opration getMachineCount(). Limplmentation de cette opration dans la classe
Machine, telle que donne dans la solution de lexercice, est correcte :
public int getMachineCount() {
return 1;
}

La classe MachineComposite implmente aussi correctement getMachineCount(), retournant la somme des comptes de chaque composant dun composite :
public int getMachineCount() {
int count = 0;
Iterator i = components.iterator();
while (i.hasNext()) {
MachineComponent mc = (MachineComponent) i.next();
count += mc.getMachineCount();
}
return count;
}

pattern Livre Page 53 Vendredi, 9. octobre 2009 10:31 10

Chapitre 5

COMPOSITE

53

Ces mthodes sont correctes tant que les objets MachineComponent sont des arbres.
Il peut toutefois arriver quun composite que vous supposiez tre un arbre ne le soit
soudain plus. Cela se produirait vraisemblablement si les utilisateurs pouvaient
modifier la composition. Considrez un exemple susceptible de se produire chez
Oozinoz.
Les ingnieurs dOozinoz utilisent une application avec GUI pour enregistrer et
actualiser la composition du matriel dans lusine. Un jour, ils signalent un dfaut
concernant le nombre de machines rapport existant dans lusine. Vous pouvez
reproduire leur modle objet avec la mthode plant() de la classe OozinozFactory:
public static MachineComposite plant() {
MachineComposite plant = new MachineComposite(100);
MachineComposite bay = new MachineComposite(101);
Machine mixer = new Mixer(102);
Machine press = new StarPress(103);
Machine assembler = new ShellAssembler(104);
bay.add(mixer);
bay.add(press);
bay.add(assembler);
plant.add(mixer);
plant.add(bay);
return plant;
}

Ce code produit lobjet plant vu plus haut dans la Figure 5.4.


Exercice 5.4
Que renvoie en sortie le programme suivant ?
package app.composite;
import com.oozinoz.machine.*;
public class ShowPlant {
public static void main(String[] args) {
MachineComponent c = OozinozFactory.plant();
System.out.println(
"Nombre de machines : " + c.getMachineCount());
}
}

pattern Livre Page 54 Vendredi, 9. octobre 2009 10:31 10

54

Partie I

Patterns dinterface

Lapplication avec GUI utilise chez Oozinoz pour construire les modles objet de
lquipement dune usine devrait vrifier si un nud existe dj dans un arbre
de composant avant de lajouter une seconde fois. Un moyen daccomplir cela est de
conserver un ensemble des nuds existants. Il peut toutefois arriver que vous
nayez pas le contrle sur la formation dun composite. Dans ce cas, vous pouvez
crire une mthode isTree() pour vrifier si un composite est un arbre.
Nous considrerons un modle objet comme tant un arbre si un algorithme peut
parcourir ses rfrences sans traverser deux fois le mme nud. Vous pouvez
implmenter une mthode isTree() sur la classe abstraite MachineComponent afin
de dlguer lappel une mthode isTree() conservant un ensemble des nuds
parcourus. La classe MachineComponent peut laisser abstraite limplmentation de
la mthode isTree(set:Set) paramtre. La Figure 5.5 illustre le placement des
mthodes isTree().
Le code de MachineComponent dlgue un appel isTree() sa mthode abstraite
isTree(s:Set):
public boolean isTree() {
return isTree(new HashSet());
}
protected abstract boolean isTree(Set s);

Ces mthodes emploient la classe Set de la bibliothque de classes Java.


Figure 5.5
Une mthode
isTree() peut
dtecter si un composite est en ralit un
arbre.

MachineComponent

MachineComponent(id:int)
id:int
isTree():boolean
isTree(set:Set):boolean

Machine

MachineComposite

Machine(id:int)

MachineComposite(id:int)

isTree(set:Set)

isTree(set:Set)

pattern Livre Page 55 Vendredi, 9. octobre 2009 10:31 10

Chapitre 5

COMPOSITE

55

Les classes Machine et MachineComposite doivent implmenter la mthode


abstraite isTree(s:Set). Limplmentation de isTree() pour Machine est
simple, refltant le fait que des machines individuelles sont toujours des arbres :
protected boolean isTree(Set visited) {
visited.add(this);
return true;
}

Limplmentation dans MachineComposite de isTree() doit ajouter lobjet rcepteur la collection visited puis parcourir tous les composants du composite. La
mthode peut retourner false si un composant a dj t parcouru ou nest pas un
arbre. Sinon, elle retourne true.
Exercice 5.5
Ecrivez le code pour MachineComposite.isTree(Set visited).

En procdant avec soin, vous pouvez garantir quun modle objet reste un arbre en
refusant tout changement qui ferait retourner false par isTree(). Dun autre ct,
vous pouvez dcider dautoriser lexistence de composites qui ne sont pas des arbres,
surtout lorsque le domaine de problmes que vous modlisez contient des cycles.

Des composites avec des cycles


Le composite non-arbre auquel se rfrait lExercice 5.4 tait un accident d au fait
quun utilisateur avait marqu une machine comme faisant partie la fois dune
usine (plant) et dune trave (bay). Pour les objets physiques, vous pouvez prfrer
interdire le concept dobjet contenu par plus dun autre objet. Toutefois, un
domaine de problmes peut comprendre des lments non physiques pour lesquels
des cycles de confinement sont justifis. Cela se produit frquemment lors de la
modlisation de flux de processus.
Considrez la construction de bombes ariennes telles que celle illustre
Figure 5.6. Une bombe est lance au moyen dun mortier, ou tube, par la mise
feu de la charge de propulsion (contenant de la poudre noire) loge sous la charge
centrale. Le deuxime dispositif dallumage brle alors que la bombe est en lair
pour finalement atteindre la charge centrale lorsque la bombe est son apoge.

pattern Livre Page 56 Vendredi, 9. octobre 2009 10:31 10

56

Partie I

Patterns dinterface

Lorsque celle-ci explose, les toiles mises feu produisent les effets visuels des
feux dartifice.
Figure 5.6
Une bombe arienne
utilise deux charges :
lune pour la propulsion initiale et lautre
pour faire clater le
cur contenant les
toiles lorsque la
bombe atteint son
apoge.

Cur
Coque interne
Coque externe
Etoiles
Charge de propulsion
Dispositif d'allumage

Le flux des processus de construction dune bombe arienne commence par la


fabrication dune coque interne, suivie dune vrification, puis dune amlioration
ou de son assemblage final.
Pour fabriquer la coque interne, un oprateur utilise un assembleur de coques qui
place les toiles dans un compartiment hmisphrique, insre une charge centrale
de poudre noire, ajoute davantage dtoiles au-dessus de la charge, puis ferme le
tout au moyen dun autre compartiment hmisphrique.
Un inspecteur vrifie que la coque interne rpond aux standards de scurit et de
qualit. Si ce nest pas le cas, loprateur la dsassemble et recommence. Si elle
passe linspection, loprateur ajoute un dispositif dallumage pour joindre une
charge de propulsion la coque interne, puis termine en ajoutant une enveloppe.
Comme pour les composites de machines, les ingnieurs dOozinoz disposent
dune application avec GUI leur permettant de dcrire la composition dun processus.
La Figure 5.7 montre la structure des classes qui grent la modlisation du processus.
La Figure 5.8 prsente les objets qui reprsentent le flux des processus participant
la fabrication dune bombe arienne. Le processus make est une squence compose
de ltape buildInner suivie de ltape inspect et du sous-processus reworkOrFinish. Ce sous-processus prend lune des deux voies possibles. Il peut requrir
une tape de dsassemblage suivie du processus make, ou seulement dune tape
finish.

pattern Livre Page 57 Vendredi, 9. octobre 2009 10:31 10

Chapitre 5

COMPOSITE

57

Figure 5.7
Le processus de
construction de pices
dartifice inclut
des tapes qui sont
des alternances ou
des squences dautres
tapes.

ProcessComponent

ProcessComponent(name:String)
getStepCount()
getStepCount(s:Set)
name:String
toString()

ProcessStep

ProcessComposite
subprocesses:List

getStepCount(s:Set)
add(c:ProcessComponent)
getStepCount(s:sET)

ProcessAlternation

ProcessSequence

Exercice 5.6
La Figure 5.8 illustre les objets du modle du processus dassemblage dune
bombe. Un diagramme objet complet montrerait les relations entre tous les objets
se rfrenant. Par exemple, le diagramme montre les rfrences que lobjet
make entretient. Votre travail est de complter les relations manquantes dans le
diagramme.
Lopration getStepCount() dans la hirarchie ProcessComponent compte le
nombre dtapes individuelles dans le flux de processus. Notez que ce compte nest
pas la longueur du processus mais le nombre dtapes de traitement de nuds
feuilles du graphe du processus. La mthode getStepCount() doit prendre soin de
compter une fois chaque tape et de ne pas entrer dans une boucle infinie lorsquun
processus comprend un cycle. La classe ProcessComponent implmente la

pattern Livre Page 58 Vendredi, 9. octobre 2009 10:31 10

58

Partie I

Patterns dinterface

Figure 5.8
Une fois termin,
ce diagramme reprsentera un modle
objet du processus
de fabrication de
bombes ariennes
Oozinoz.

buildInnerShell:
ProcessStep

inspect:
ProcessStep

make:
ProcessSequence

reworkOrFinish:
ProcessAlternation

:ProcessSequence

disassemble:
ProcessStep

finish:
ProcessStep

mthode getStepCount() de sorte quelle sappuie sur une mthode compagnon qui
transmet un ensemble de nuds parcourus :
public int getStepCount() {
return getStepCount(new HashSet());
}
public abstract int getStepCount(Set visited);

La classe ProcessComposite veille dans son implmentation ce que la mthode


getStepCount() ne parcoure pas un nud dj visit :
public int getStepCount(Set visited) {
visited.add(getName());
int count = 0;
for (int i = 0; i < subprocesses.size(); i++) {
ProcessComponent pc =
(ProcessComponent) subprocesses.get(i);
if (!visited.contains(pc.getName()))
count += pc.getStepCount(visited);
}
return count;
}

pattern Livre Page 59 Vendredi, 9. octobre 2009 10:31 10

Chapitre 5

COMPOSITE

59

Limplmentation de getStepCount() de la classe ProcessStep est simple :


public int getStepCount(Set visited) {
visited.add(name);
return 1;
}

Le package com.oozinoz.process dOozinoz contient une classe ShellProcess


qui inclut une mthode make() qui retourne lobjet make illustr Figure 5.8. Le
package com.oozinoz.testing comprend une classe ProcessTest qui fournit des
tests automatiss de divers types de graphes de processus. Par exemple, la classe
ProcessTest inclut une mthode qui vrifie que lopration getStepCount()
compte correctement le nombre dtapes dans le processus make cyclique :
public void testShell() {
assertEquals(4, ShellProcess.make().getStepCount());
}

Ce test sexcute au sein du framework JUnit. Voir www.junit.org pour plus


dinformations sur JUnit.

Consquences des cycles


Beaucoup doprations sur un composite, telles que le calcul de son nombre de
nuds feuilles, sont justifies mme si le composite nest pas un arbre. Gnralement, la seule diffrence que les composites non-arbre introduisent est que vous
devez tre attentif ne pas oprer une deuxime fois sur un mme nud. Toutefois,
certaines oprations deviennent inutiles si le composite contient un cycle. Par
exemple, nous ne pouvons pas dterminer par voie algorithmique le nombre maximal dtapes requises pour fabriquer une bombe arienne chez Oozinoz car le
nombre de fois o ltape damlioration doit tre recommence ne peut tre connu.
Toute opration dpendant de la longueur dun chemin dans un composite ne serait
pas logique si le composite comprend un cycle. Aussi, bien que nous puissions
parler de la hauteur dun arbre le chemin le plus long de la racine une feuille ,
il ny a pas de longueur de chemin maximale dans un graphe cyclique.
Une autre consquence de permettre lintroduction de composites non-arbre est que
vous perdez la capacit de supposer que chaque nud na quun parent. Si un
composite nest pas un arbre, un nud peut avoir plus dun parent. Par exemple, le
processus modlis dans la Figure 5.8 pourrait avoir plusieurs tapes composites utilisant ltape inspect, donnant ainsi lobjet inspect plusieurs parents.

pattern Livre Page 60 Vendredi, 9. octobre 2009 10:31 10

60

Partie I

Patterns dinterface

Il ny a pas de problme inhrent au fait davoir un nud avec plusieurs parents,


mais votre modle et votre code doivent en tenir compte.

Rsum
Le pattern COMPOSITE comprend deux concepts puissants associs. Le premier est
quun groupe dobjets peut contenir des lments individuels mais aussi dautres
groupes. Lautre ide est que ces lments individuels et composites partagent une
interface commune. Ces concepts sont unis dans la modlisation objet, lorsque vous
crez une classe abstraite ou une interface Java qui dfinit des comportements
communs des objets composites et individuels.
La modlisation de composites conduit souvent une dfinition rcursive des
mthodes sur les nuds composites. Lorsquil y a une rcursivit, il y a le risque
dcrire du code produisant une boucle infinie. Pour viter ce problme, vous
pouvez prendre des mesures pour garantir que vos composites soient toujours des
arbres. Une autre possibilit est dautoriser lintervention de cycles dans un composite, mais il vous faut modifier vos algorithmes pour viter toute rcursivit infinie.

pattern Livre Page 61 Vendredi, 9. octobre 2009 10:31 10

6
BRIDGE
Le pattern BRIDGE, ou Driver, vise implmenter une abstraction. Le terme abstraction se rfre une classe qui sappuie sur un ensemble doprations abstraites,
lesquelles peuvent avoir plusieurs implmentations.
La faon habituelle dimplmenter une abstraction est de crer une hirarchie de
classes, avec une classe abstraite au sommet qui dfinit les oprations abstraites.
Chaque sous-classe de la hirarchie apporte une implmentation diffrente de
lensemble doprations. Cette approche devient insuffisante lorsquil vous faut
driver une sous-classe de la hirarchie pour une quelconque autre raison.
Vous pouvez crer un BRIDGE (pont) en dplaant lensemble doprations abstraites
vers une interface de sorte quune abstraction dpendra dune implmentation de
linterface.
Lobjectif du pattern BRIDGE est de dcoupler une abstraction de limplmentation de ses oprations abstraites, permettant ainsi labstraction et son
implmentation de varier indpendamment.

Une abstraction ordinaire


Presque chaque classe est une abstraction dans ce sens quelle constitue une
approximation, une idalisation, ou une simplification de la catgorie dobjets rels
quelle modlise. Toutefois, dans le cas du BRIDGE, nous utilisons spcifiquement le
terme abstraction pour signifier une classe sappuyant sur un ensemble doprations
abstraites.

pattern Livre Page 62 Vendredi, 9. octobre 2009 10:31 10

62

Partie I

Patterns dinterface

Supposez que vous ayez des classes de contrle qui interagissent avec certaines
machines produisant les pices dartifice chez Oozinoz. Ces classes refltent les
diffrences dans la faon dont les machines oprent. Vous pourriez toutefois dsirer
crer certaines oprations abstraites qui produiraient les mmes rsultats sur
nimporte quelle machine. La Figure 6.1 montre des classes de contrle provenant
du package com.oozinoz.controller.
Figure 6.1
Ces deux classes
ont des mthodes
semblables que vous
pouvez placer dans
un modle commun
pour piloter des
machines.

StarPressController

FuserController

start()

startMachine()

stop()

stopMachine()

startProcess()

begin()

endProcess()

end()

index()

conveyIn()

discharge()

conveyOut()
switchSpool()

Les deux classes de la Figure 6.1 possdent des mthodes semblables pour dmarrer et arrter les machines quelles contrlent : presse toiles (star press) ou
assembleuse de dispositif dallumage (fuser). Elles sont toutefois nommes diffremment : start() et stop() dans la classe StarPressController, et startMachine() et stopMachine() dans FuserController. Ces classes de contrle, ou
contrleurs, possdent galement des mthodes pour amener une caisse dans la
zone de traitement (index() et conveyIn()), pour dbuter et terminer le traitement
dune caisse (startProcess() et endProcess(), et begin() et end()), et pour
retirer une caisse (discharge() et conveyOut()). La classe FuserController
possde galement une mthode switchSpool() qui permet de changer la bobine
de mche dallumage (fuse spool).
Supposez maintenant que vous souhaitiez crer une mthode shutdown() qui
assure un arrt en bon ordre, effectuant les mmes tapes sur les deux machines. Pour
en simplifier lcriture, vous pouvez standardiser les noms des oprations courantes,
comme startMachine(), stopMachine(), startProcess(), stopProcess(),
conveyIn(), et conveyOut(). Il se trouve toutefois que vous ne pouvez pas changer
les classes de contrle car lune delles provient du fournisseur de la machine.

pattern Livre Page 63 Vendredi, 9. octobre 2009 10:31 10

Chapitre 6

BRIDGE

63

Exercice 6.1
Indiquez de quelle manire vous pourriez appliquer un pattern de conception
pour permettre le contrle de diverses machines avec une interface commune.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.


La Figure 6.2 illustre lintroduction dune classe abstraite MachineManager avec
des sous-classes qui retransmettent les appels de contrle en les adaptant au sein de
mthodes supportes par FuserController et StarPressController.

MachineManager

startMachine()
stopMachine()

FuserController

startProcess()
stopProcess()
conveyIn()

StarPressController

conveyOut()
shutdown()

FuserManager

StarPressManager

Figure 6.2
Les classes FuserManager et StarPressManager implmentent les mthodes abstraites
de MachineManager en transmettant les appels aux mthodes correspondantes des objets
FuserController et StarPressController.

Il nest pas problmatique quun contrleur incorpore des oprations qui soient
uniques pour le type de machine concern. Par exemple, bien que la Figure 6.2
ne le montre pas, la classe FuserManager possde galement une mthode

pattern Livre Page 64 Vendredi, 9. octobre 2009 10:31 10

64

Partie I

Patterns dinterface

switchSpool() qui transmet les appels la mthode switchSpool() dun objet


FuserController.
Exercice 6.2
Ecrivez une mthode shutdown() qui terminera le traitement pour la classe
MachineManager, dchargera la caisse en cours de traitement et arrtera la
machine.

La mthode shutdown() de la classe MachineManager nest pas abstraite mais


concrte. Toutefois, nous pouvons dire que cest une abstraction car la mthode
universalise, ou abstrait, la dfinition des tapes raliser pour arrter une machine.

De labstraction au pattern BRIDGE


La hirarchie MachineManager tant code par rapport au type dquipement,
chaque type de machine ncessite une sous-classe diffrente de MachineManager.
Que se passerait-il si vous deviez organiser la hirarchie selon un autre critre ?
Par exemple, supposez que vous travailliez directement sur les machines et que
celles-ci fournissent un acquittement des tapes quelles accomplissent. Conformment cela, vous voulez crer une sous-classe MachineManager de mise en route,
ou handshaking, avec des mthodes permettant de paramtrer linteraction avec
la machine, telle que la dfinition dune valeur de temporisation. Vous avez
toutefois besoin de diffrents gestionnaires de machine pour les presses toiles et
les assembleuses de dispositif dallumage. Si vous ne rorganisiez pas dabord la
hirarchie MachineManager, votre nouvelle hirarchie risquerait de ressembler au
modle de la Figure 6.3.
La hirarchie illustre la Figure 6.3 conoit les classes suivant deux critres :
selon le type de machine et selon que la machine gre ou non le protocole de mise
en route. Ce principe dual de codage prsente un problme. Plus particulirement,
une mthode telle que setTimeout() peut contenir un code identique deux
endroits, mais nous ne pouvons pas le coder dans la hirarchie car les super-classes
ne grent pas lide du handshaking.
En gnral, les classes de handshaking ne disposent daucun moyen pour partager
le code car il ny a pas de super-classe de handshaking. Et mesure que nous ajoutons

pattern Livre Page 65 Vendredi, 9. octobre 2009 10:31 10

Chapitre 6

BRIDGE

65

Figure 6.3
Les sous-classes de
mise en route (Hsk)
ajoutent un paramtrage pour le temps
dattente dun
acquittement de la
part dune machine.

MachineManager

FuserManager

StarPressManager

HskFuserManager

HskStarPressManager

setTimeout(:double)

setTimeout(:double)

davantage de classes dans la hirarchie, le problme empire. Si nous disposons au


final de contrleurs pour cinq machines et que la mthode setTimeout() doive tre
change, nous devons modifier le code cinq endroits.
Dans une telle situation, nous pouvons appliquer le pattern BRIDGE. Nous pouvons
dissocier labstraction MachineManager de limplmentation de ses oprations
abstraites en plaant les mthodes abstraites dans une hirarchie distincte. La classe
MachineManager demeure une abstraction et le rsultat produit par lappel de ses
mthodes sera diffrent selon quil sagira dune presse ou dune assembleuse.
Sparer labstraction de limplmentation de ses mthodes permet aux deux hi rarchies de varier de manire indpendante. Nous pouvons ajouter un support pour
de nouvelles machines sans influer sur la hirarchie MachineManager. Nous pouvons
galement tendre la hirarchie MachineManager sans changer aucun des contrleurs
de machine. La Figure 6.4 prsente la sparation souhaite.
Lobjectif de la nouvelle conception est de sparer la hirarchie MachineManager
de limplmentation des oprations abstraites de la hirarchie.
Exercice 6.3
La Figure 6.4 illustre la hirarchie MachineManager restructure en BRIDGE.
Ajoutez les mentions manquantes.

pattern Livre Page 66 Vendredi, 9. octobre 2009 10:31 10

66

Partie I

Patterns dinterface

Notez que dans la Figure 6.4 la classe MachineManager2 devient concrte bien
quelle soit toujours une abstraction. Les mthodes abstraites dont dpend maintenant MachineManager rsident dans linterface MachineDriver. Le nom de cette
interface suggre que les classes qui adaptent les requtes de MachineManager aux
diffrentes machines spcifiques sont devenues des drivers. Un driver est un objet
qui pilote un systme informatique ou un quipement externe selon une interface
bien spcifie. Les drivers fournissent lexemple le plus courant dapplication du
pattern BRIDGE.
interface
MachineDriver

MachineManager2
driver:??
??
??

shutdown()

??
??
??
??

??

??

??

StarPressDriver

Figure 6.4
Une fois complt, ce diagramme montrera la sparation de labstraction MachineManager
de limplmentation de ses oprations abstraites.

Des drivers en tant que BRIDGE


Les drivers sont des abstractions. Le rsultat de lexcution de lapplication dpend
du driver en place. Chaque driver est une instance du pattern ADAPTER, fournissant
linterface quun client attend en utilisant les services dune classe comportant une
interface diffrente. Une conception globale qui utilise des drivers est une instance

pattern Livre Page 67 Vendredi, 9. octobre 2009 10:31 10

Chapitre 6

BRIDGE

67

de BRIDGE. La conception spare le dveloppement dapplication de celui des


drivers qui implmentent les oprations abstraites dont dpendent les applications.
Une conception base de drivers vous force crer un modle abstrait commun de
la machine ou du systme piloter. Cela prsente lavantage de permettre au code du
ct abstraction de sappliquer nimporte lequel des drivers au travers desquels il
pourrait sexcuter. La dfinition dun ensemble commun de mthodes pour les
drivers peut toutefois prsenter linconvnient dliminer le comportement quun
quipement pilot pourrait supporter. Rappelez-vous de la Figure 6.1 quun contrleur dassembleuse de dispositif dallumage possde une mthode switchSpool().
O cette mthode est-elle passe dans la conception rvise de la Figure 6.4 (ou
Figure B5 de lAnnexe B) ? La rponse est que nous lavons limine par abstraction.
Vous pouvez linclure dans la nouvelle classe FuserDriver. Toutefois, ceci peut
donner lieu du code ct abstraction devant procder une vrification pour
savoir si son driver est une instance de FuserDriver.
Pour viter de perdre la mthode switchSpool(), nous pourrions faire en sorte que
chaque driver limplmente, sachant que certains dentre eux ignoreront simplement lappel. Lorsque vous devez choisir un modle abstrait des oprations quun
driver doit grer, vous tes souvent confront ce genre de dcision. Vous pouvez
inclure des mthodes que certains drivers ne supporteront pas, ou exclure des
mthodes pour limiter ce que les abstractions pourront faire avec un driver ou bien
les forcer inclure du code pour un cas particulier.

Drivers de base de donnes


Un exemple banal dapplication utilisant des drivers est laccs une base de
donnes. La connectivit base de donnes dans Java sappuie sur JDBC. Une bonne
source de documentation expliquant comment appliquer JDBC est JDBC API
Tutorial and Reference (2/e) [White et al. 1999]. Dit succinctement, JDBC est une
API (Application Programming Interface) qui permet dexcuter des instructions
SQL (Structured Query Langage). Les classes qui implmentent linterface sont des
drivers JDBC, et les applications qui sappuient sur ces drivers sont des abstractions
qui peuvent fonctionner avec nimporte quelle base de donnes pour laquelle il
existe un driver JDBC. Larchitecture JDBC dissocie une abstraction de son implmentation pour que les deux puissent varier de manire indpendante ; cest un
excellent exemple de BRIDGE.

pattern Livre Page 68 Vendredi, 9. octobre 2009 10:31 10

68

Partie I

Patterns dinterface

Pour utiliser un driver JDBC, vous le chargez, le connectez la base de donnes et


crez un objet Statement:
Class.forName(driverName);
Connection c = DriverManager.getConnection(url, user, pwd);
Statement stmt = c.createStatement();

Une description du fonctionnement de la classe DriverManager sortirait du cadre


de la prsente description. Sachez toutefois qu ce stade stmt est un objet Statement capable dmettre des requtes SQL qui retournent des ensembles de rsultats
(result set) :
ResultSet result = stmt.executeQuery(
"SELECT name, apogee FROM firework");
while (result.next()) {
String name = result.getString("name");
int apogee = result.getInt("apogee");
System.out.println(name + ", " + apogee);
}

Exercice 6.4
La Figure 6.5 illustre un diagramme de squence UML qui dcrit le flux de
messages dans une application JDBC typique. Compltez les noms de types et le
nom de message manquants.

Figure 6.5
Ce diagramme
montre une partie
du flux de messages
typique qui intervient dans une
application JDBC.

:Client

:?
createStatement()
<<create>>
:?

??()

<<create>>
:?

next()

pattern Livre Page 69 Vendredi, 9. octobre 2009 10:31 10

Chapitre 6

BRIDGE

69

Exercice 6.5
Supposez que chez Oozinoz nous nayons que des bases de donnes SQL Server.
Donnez un argument en faveur de lemploi de lecteurs et dadaptateurs spcifiques SQL Server. Donnez un autre argument qui justifierait de ne pas le faire.
Larchitecture JDBC divise clairement les rles du dveloppeur de driver et du
dveloppeur dapplication. Dans certains cas, cette division nexistera pas
lavance, mme si vous utilisez des drivers. Vous pourrez ventuellement implmenter des drivers en tant que sous-classes dune super-classe abstraite, avec
chaque sous-classe pilotant un sous-systme diffrent. Dans une telle situation,
vous pouvez envisager limplmentation dun BRIDGE lorsquil vous faut davantage
de souplesse.

Rsum
Une abstraction est une classe qui dpend de mthodes abstraites. Lexemple le plus
simple dabstraction est une hirarchie abstraite, o des mthodes concrtes dans la
super-classe dpendent dautres mthodes abstraites. Vous pouvez tre forc de
dplacer ces dernires vers une autre hirarchie si vous voulez restructurer la hirarchie originale selon un autre critre. Il sagit alors dune application du pattern
BRIDGE, sparant une abstraction de limplmentation de ses mthodes abstraites.
Lexemple le plus courant dapplication de BRIDGE apparat dans les drivers, tels
que ceux de base de donnes. Les drivers de base de donnes sont un bon exemple
des compromis inhrents une conception avec BRIDGE. Un driver peut ncessiter des mthodes quun implmenteur ne peut grer. Dun autre ct, un driver
peut ngliger des mthodes utiles qui pourraient sappliquer une certaine base de
donnes. Cela pourrait vous inciter rcrire du code spcifique une implmentation au lieu dtre abstrait. Il nest pas toujours vident de savoir sil faut privilgier
labstraction ou la spcificit, mais il est important de prendre des dcisions mrement
rflchies.

pattern Livre Page 70 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 71 Vendredi, 9. octobre 2009 10:31 10

II
Patterns de responsabilit

pattern Livre Page 72 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 73 Vendredi, 9. octobre 2009 10:31 10

7
Introduction la responsabilit
La responsabilit dun objet est comparable celle dun reprsentant au centre de
rception des appels de la socit Oozinoz. Lorsquun client appelle chez Oozinoz,
la personne qui rpond est un intermdiaire, ou proxy pour reprendre un terme
informatique, qui reprsente la socit. Ce reprsentant effectue des tches prvisibles, gnralement en les dlguant un autre systme ou une autre personne.
Parfois, le reprsentant dlgue une requte une seule autorit centrale qui joue le
rle de mdiateur dans une situation ou transmet les problmes le long dune chane
de responsabilits.
A linstar des reprsentants, les objets ordinaires disposent des informations et des
mthodes adquates pour pouvoir oprer de manire indpendante. Cependant, il y
a des situations qui demandent de scarter de ce modle de fonctionnement indpendant et de recourir une entit centrale responsable. Il existe plusieurs patterns
qui rpondent ce type de besoin. Il y a aussi des patterns qui permettent aux objets
de relayer les requtes et qui isolent un objet des autres objets qui en dpendent. Les
patterns affrents la responsabilit fournissent des techniques pour centraliser,
transmettre et aussi limiter la responsabilit des objets.

Responsabilit ordinaire
Bien que vous ayez probablement une bonne ide de la faon dont les attributs et les
responsabilits doivent tre associs dans une classe bien conue, il pourrait vous
paratre difficile dexpliquer les raisons motivant vos choix.

pattern Livre Page 74 Vendredi, 9. octobre 2009 10:31 10

74

Partie II

Patterns de responsabilit

Exercice 7.1
La structure de la classe illustre Figure 7.1 prsente au moins dix choix dassignation de responsabilits discutables. Identifiez tous les problmes possibles et
expliquez par crit ce qui est erron pour quatre dentre eux.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.


Figure 7.1
Quest-ce qui
ne va pas dans
cette figure ?

Rocket

thrust():Rocket

LiquidRocket

isLiquid()
getLocation()

Firework

interface
Runnable

run()

CheapRockets

run()

Reservation

rez:Reservation

loc:Location

price:Dollars

city:String

getPrice()

getCity()

getReservation()

getDate()

Location

Lexamen des bizarreries de la Figure 7.1 dbridera votre rflexion pour entreprendre une modlisation dobjets approprie. Cest ltat desprit qui convient lorsque
vous dfinissez des termes tels que classe. La valeur de lexercice de dfinition de

pattern Livre Page 75 Vendredi, 9. octobre 2009 10:31 10

Chapitre 7

Introduction la responsabilit

75

termes augmente sil favorise la communication entre les personnes et diminue sil
devient un objectif en soi et une source de conflits. Dans ce mme tat desprit,
rpondez la difficile question de lExercice 7.2.
Exercice 7.2
Dfinissez les qualits dune classe efficace et utile.

Lemploi dune classe est facilit si ses mthodes ont un nom suffisamment explicite pour quon puisse savoir ce quelles ralisent. Il y a toutefois des cas o un nom
de mthode ne contient pas suffisamment dinformations pour quon puisse prdire
leffet quaura un appel de la mthode.
Exercice 7.3
Donnez un exemple de raison pour laquelle limpossibilit de prdire leffet
produit par un appel de mthode serait justifie.

Llaboration de principes relatifs lassignation de responsabilits dans un


systme orient objet est un domaine qui ncessite des progrs. Un systme dans
lequel chaque classe et mthode dfinit clairement ses responsabilits et sen
acquitte correctement est un systme puissant, suprieur la plupart des systmes
que lon rencontre aujourdhui.

Contrle de la responsabilit grce la visibilit


Il est courant de parler de classes et de mthodes assumant diverses responsabilits.
Dans la pratique, cela signifie gnralement que vous, en tant que dveloppeur,
assumez la responsabilit dune conception robuste et dun fonctionnement correct
du code. Heureusement que Java apporte quelque soulagement dans ce domaine.
Vous pouvez limiter la visibilit de vos classes, champs et mthodes, et circonscrire
ainsi la responsabilit au niveau des dveloppeurs qui emploient votre code. La visibilit peut tre un signe de la faon dont une portion dune classe doit tre expose.

pattern Livre Page 76 Vendredi, 9. octobre 2009 10:31 10

76

Partie II

Patterns de responsabilit

Le Tableau 7.1 reprend les dfinitions informelles de leffet des modificateurs daccs.
Tableau 7.1 : Dfinitions informelles de leffet des modificateurs daccs

Accs

Dfinition informelle

public

Accs non limit

(rien)

Accs limit au package

protected

Accs limit la classe contenant llment en question, ou aux types drivs


de cette classe

private

Accs limit au type contenant llment en question

Dans la pratique, certaines subtilits demandent de considrer plutt la dfinition


formelle de ces modificateurs daccs quune dfinition intuitive. Par exemple, la
question de savoir si une visibilit affecte des objets ou des classes.
Exercice 7.4
Un objet peut-il se rfrer un membre priv dune autre instance de la mme
classe ? Plus spcifiquement, le code suivant compilera-t-il ?
public class Firework {
private double weight = 0;
/// ...
private double compare(Firework f) {
return weight - f.weight;
}
}

Les modificateurs daccs vous aident limiter votre responsabilit en restreignant les
services que vous fournissez aux autres dveloppeurs. Par exemple, si vous ne
voulez pas que dautres dveloppeurs puissent manipuler un champ de lune de vos
classes, vous pouvez le dfinir private. Inversement, vous apporterez de la souplesse
pour les autres dveloppeurs en dclarant un lment protected, bien quil y ait
le risque dassocier trop troitement les sous-classes la classe parent. Prenez des
dcisions mrement rflchies et, au besoin, mettez en place une stratgie de groupe
concernant la faon dont vous voulez restreindre les accs pour limiter vos responsabilits tout en autorisant des extensions futures.

pattern Livre Page 77 Vendredi, 9. octobre 2009 10:31 10

Chapitre 7

Introduction la responsabilit

77

Rsum
En tant que dveloppeur Java, vous tes responsable de la cration des classes
formant un ensemble logique dattributs et de comportements associs. Crer une
classe efficace est un art, mais il est possible dtablir certaines caractristiques
dune classe bien conue. Une de vos responsabilits est aussi de vous assurer que
les mthodes de vos classes assurent bien les services que leur nom suggre. Vous
pouvez limiter cette responsabilit avec lemploi appropri de modificateurs de
visibilit, mais envisagez lexistence de certains compromis entre scurit et
souplesse au niveau de la visibilit de votre code.

Au-del de la responsabilit ordinaire


Indpendamment de la faon dont une classe restreint laccs ses membres, le
dveloppement OO rpartit normalement les responsabilits entre objets individuels. En dautres termes, le dveloppement OO promeut lencapsulation, lide
quun objet travaille sur ses propres donnes.
La responsabilit distribue est la norme, mais plusieurs patterns de conception sy
opposent et placent la responsabilit au niveau dun objet intermdiaire ou dun
objet central. Par exemple, le pattern SINGLETON concentre la responsabilit au
niveau dun seul objet et fournit un accs global cet objet. Une faon de se souvenir de lobjectif de ce pattern, ainsi que de celui dautres patterns, est de les voir en
tant quexceptions la rgle ordinaire de la responsabilit rpartie.
Si vous envisagez de

Appliquez le pattern

Centraliser la responsabilit au niveau dune instance de classe

SINGLETON

Librer un objet de la "conscience" de connatre les objets qui en


dpendent

OBSERVER

Centraliser la responsabilit au niveau dune classe qui supervise la


faon dont les objets interagissent

MEDIATOR

Laisser un objet agir au nom dun autre objet

PROXY

Autoriser une requte tre transmise le long dune chane dobjets


jusqu celui qui la traitera

CHAIN OF
RESPONSABILITY

Centraliser la responsabilit au niveau dobjets partags de forte


granularit

FLYWEIGHT

pattern Livre Page 78 Vendredi, 9. octobre 2009 10:31 10

78

Partie II

Patterns de responsabilit

Lobjectif de chaque pattern de conception est de permettre la rsolution dun


problme dans un certain contexte. Les patterns de responsabilit conviennent dans
des contextes o vous devez vous carter de la rgle normale stipulant que la
responsabilit devrait tre distribue autant que possible.

pattern Livre Page 79 Vendredi, 9. octobre 2009 10:31 10

8
SINGLETON
Les objets peuvent gnralement agir de faon responsable en effectuant leur travail
sur leurs propres attributs, sans avoir dautre obligation que dassurer leur cohrence propre. Cependant, certains objets assument dautres responsabilits, telles
que la modlisation dentits du monde rel, la coordination de tches, ou la modlisation de ltat gnral dun systme. Lorsque, dans un systme, un certain objet
assume une responsabilit dont dpendent dautres objets, vous devez disposer
dune mthode pour localiser cet objet. Par exemple, vous pouvez avoir besoin
didentifier un objet qui reprsente une machine particulire, un objet client qui
puisse se construire lui-mme partir dinformations extraites dune base de donnes,
ou encore un objet qui initie une rcupration de la mmoire systme.
Dans certains cas, lorsque vous devez trouver un objet responsable, lobjet dont
vous avez besoin sera la seule instance de sa classe. Par exemple, la cration de
fuses peut se suffire dun seul objet Factory. Dans ce cas, vous pouvez utiliser le
pattern SINGLETON.
Lobjectif du pattern SINGLETON est de garantir quune classe ne possde
quune seule instance et de fournir un point daccs global celle-ci.

Le mcanisme de SINGLETON
Le mcanisme de SINGLETON est plus simple exposer que son objectif. En effet, il
est plus ais dexpliquer comment garantir quune classe naura quune instance
que pourquoi cette restriction est souhaitable. Vous pouvez placer SINGLETON dans
la catgorie "patterns de cration", comme le fait louvrage Design Patterns.

pattern Livre Page 80 Vendredi, 9. octobre 2009 10:31 10

80

Partie II

Patterns dinterface

Lessentiel est de voir les patterns dune faon qui permette de sen souvenir, de les
reconnatre et de les appliquer. Lintention, ou lobjectif, du pattern SINGLETON
demande quun objet spcifique assume une responsabilit dont dpendent dautres
objets.
Vous disposez de quelques options quant la faon de crer un objet qui remplit un
rle de manire unique. Indpendamment de la faon dont vous crez un singleton,
vous devez vous assurer que dautres dveloppeurs ne crent pas de nouvelles
instances de la classe que vous souhaitez restreindre.
Exercice 8.1
Comment pouvez-vous empcher dautres dveloppeurs de crer de nouvelles
instances de votre classe ?

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.

Lorsque vous concevez une classe singleton, vous devez dcider du moment de
linstanciation du seul objet qui reprsentera la classe. Une possibilit est de crer
cette instance en tant que champ statique dans la classe. Par exemple, une classe
SystemStartup pourrait inclure la ligne :
private static Factory factory = new Factory();

Cette classe pourrait rendre son unique instance disponible par lintermdiaire
dune mthode getFactory() publique et statique.
Plutt que de crer lavance une instance de singleton, vous pouvez attendre
jusquau moment o linstance est requise pour la premire fois en utilisant une
initialisation tardive, dite "paresseuse", ou lazy-initialization. Par exemple, la classe
SystemStartup peut mettre disposition son unique instance de la manire
suivante :
public static Factory getFactory() {
if (factory == null)
factory = new Factory();
// ...
return factory;
}

pattern Livre Page 81 Vendredi, 9. octobre 2009 10:31 10

Chapitre 8

SINGLETON

81

Exercice 8.2
Pour quelle raison dcideriez-vous de procder linitialisation paresseuse dune
instance de singleton plutt que de linitialiser dans la dclaration de son
champ ?
Quoi quil en soit, le pattern SINGLETON suggre de fournir une mthode publique
et statique qui donne accs lobjet SINGLETON. Si cette mthode cre lobjet, elle
doit aussi sassurer que seule une instance pourra tre cre.

Singletons et threads
Si vous voulez effectuer linitialisation paresseuse dun singleton dans un environnement multithread, vous devez prendre soin dempcher plusieurs threads dinitialiser le singleton. Dans une telle situation, il ny a aucune garantie quune mthode
se termine avant quune autre mthode dans un autre thread commence son excution.
Il se pourrait, par exemple, que deux threads tentent dinitialiser un singleton pratiquement en mme temps. Supposez quune mthode dtecte le singleton comme
tant null. Si un autre thread dbute ce moment-l, il trouvera galement le
singleton null. Les deux mthodes voudront alors linitialiser. Pour empcher ce
type de contention, vous devez recourir un mcanisme de verrouillage pour coordonner les mthodes sexcutant dans diffrents threads.
Java inclut des fonctionnalits pour supporter le dveloppement multithread, et,
plus spcifiquement, il fournit chaque objet un verrou (lock), une ressource exclusive qui indique que lobjet appartient un thread. Pour garantir quun seul objet
initialise un singleton, vous pouvez synchroniser linitialisation par rapport au
verrou dun objet appropri. Dautres mthodes ncessitant laccs exclusif au
singleton peuvent tre synchronises par rapport au mme verrou. Pour plus
dinformations sur la POO avec concurrence, reportez-vous lexcellent ouvrage
Concurrent Programming in Java [Lea 2000]. Ce livre suggre une synchronisation sur le verrou qui appartient la classe elle-mme, comme dans le code suivant :
package com.oozinoz.businessCore;
import java.util.*;
public class Factory {
private static Factory factory;

pattern Livre Page 82 Vendredi, 9. octobre 2009 10:31 10

82

Partie II

Patterns dinterface

private static Object classLock = Factory.class;


private long wipMoves;
private Factory() {
wipMoves = 0;
}
public static Factory getFactory() {
synchronized (classLock) {
if (factory == null)
factory = new Factory();
return factory;
}
}
public void recordWipMove() {
// Exercice !
}
}

Le code de getFactory() sassure que si un second thread tente une initialisation


paresseuse du singleton aprs quun autre thread a commenc la mme initialisation, le second thread devra attendre pour obtenir le verrou dobjet classLock.
Une fois quil obtiendra le verrou, il ne dtectera pas de singleton null (tant donn
quil ne peut y avoir quune seule instance de la classe, nous pouvons utiliser le seul
verrou statique).
La variable wipMoves enregistre le nombre de fois que le travail en cours (wip,
work in process), progresse. Chaque fois quune caisse arrive sur une autre
machine, le sous-systme qui provoque ou enregistre lavancement doit appeler la
mthode recordWipMove() du singleton factory.
Exercice 8.3
Ecrivez le code de la mthode recordWipMove() de la classe Factory.

Identification de singletons
Les objets uniques ne sont pas chose inhabituelle. En fait, la plupart des objets dans
une application assument une responsabilit unique. Pourquoi crer deux objets
avec des responsabilits identiques ? De mme, pratiquement chaque classe
assure un rle unique. Pourquoi dvelopper deux fois la mme classe ? Dun

pattern Livre Page 83 Vendredi, 9. octobre 2009 10:31 10

Chapitre 8

SINGLETON

83

autre ct, une classe singleton une classe qui nautorise quune instance
est relativement rare. Le fait quun objet ou une classe soit unique ne signifie pas
ncessairement que le pattern SINGLETON est appliqu. Considrez les classes de la
Figure 8.1.
Figure 8.1
Quelles classes
semblent appliquer
SINGLETON?

OurBiggestRocket

java.lang.math

TopSalesAssociate

java.lang.System
+out:PrintStream

-Math()
+pow(a:double,b:double):double

PrintStream
PrintSpooler
PrinterManager

Exercice 8.4
Pour chaque classe de la Figure 8.1, indiquez sil sagit dune classe singleton et
justifiez votre rponse.

SINGLETON est probablement le pattern le plus connu, mais il faut y recourir avec
prudence car il est facile de mal lutiliser. Ne laissez pas cette technique devenir un
moyen pratique de crer des variables globales. Le couplage introduit nest pas
beaucoup mieux simplement parce que vous avez utilis un pattern. Minimisez le
nombre de classes qui savent quelles travaillent avec un SINGLETON. Il est prfrable pour une classe de simplement savoir quelle a un objet avec lequel travailler
et non de connatre les restrictions relatives sa cration. Soyez attentif lemploi

pattern Livre Page 84 Vendredi, 9. octobre 2009 10:31 10

84

Partie II

Patterns dinterface

que vous souhaitez en faire. Par exemple, si vous avez besoin de sous-classes ou de
diffrentes versions pour effectuer des tests, SINGLETON ne sera probablement pas
appropri car il ny aura pas exactement une seule instance.

Rsum
Le code qui supporte SINGLETON sassure quune classe ne possde quune instance
et fournit un point daccs global linstance. Une faon de limplmenter est de
procder par initialisation paresseuse dun objet singleton, en linstanciant seulement lorsquil est requis. Dans un environnement multithread, vous devez veiller
grer la collaboration des threads qui peuvent accder simultanment aux mthodes
et aux donnes dun singleton.
Le fait quun objet soit unique nindique pas forcment que le pattern SINGLETON a
t utilis. Celui-ci centralise lautorit au niveau dune seule instance de classe en
dissimulant le constructeur et en offrant un seul point daccs la mthode de cration
de lobjet.

pattern Livre Page 85 Vendredi, 9. octobre 2009 10:31 10

9
OBSERVER
Dordinaire, les clients collectent des informations provenant dun objet en appelant ses mthodes. Toutefois, lorsque lobjet change, un problme survient :
comment les clients qui dpendent des informations de lobjet peuvent-ils dterminer
que celles-ci ont chang ?
Certaines conceptions imputent lobjet la responsabilit dinformer les clients
lorsquun aspect intressant de lobjet change. Cette dmarche pose un problme,
car cest le client qui sait quels sont les attributs qui lintressent. Lobjet intressant
ne doit pas accepter la responsabilit dactualiser le client. Une solution possible est
de sarranger pour que le client soit inform lorsque lobjet change et de laisser au
client la libert de senqurir ou non du nouvel tat de lobjet.
Lobjectif du pattern OBSERVER est de dfinir une dpendance du type un-plusieurs (1,n) entre des objets de manire que, lorsquun objet change dtat,
tous les objets dpendants en soient notifis afin de pouvoir ragir conformment.

Un exemple classique : OBSERVER dans les interfaces utilisateurs


Le pattern OBSERVER permet un objet de demander dtre notifi lorsquun autre
objet change. Lexemple le plus courant dapplication de ce pattern intervient dans
les interfaces graphiques utilisateurs, ou GUI. Lorsquun utilisateur clique sur un
bouton ou agit sur un curseur, de nombreux objets de lapplication doivent ragir au
changement. Les concepteurs de Java ont anticip lintrt de savoir quand un utilisateur provoque un changement au niveau dun composant de linterface graphique.

pattern Livre Page 86 Vendredi, 9. octobre 2009 10:31 10

86

Partie II

Patterns de responsabilit

La forte prsence de OBSERVER dans Swing nous le prouve. Swing se rfre aux
clients en tant que listeners et vous pouvez enregistrer autant de listeners que vous
le souhaitez pour recevoir les vnements dun objet.
Considrez une application typique Oozinoz avec GUI, telle celle illustre
Figure 9.1. Cette application permet un ingnieur de tester visuellement les paramtres dterminant la relation entre la pousse (thrust), le taux de combustion (burn
rate) et la surface de combustion (burn surface).

Figure 9.1
Les courbes montrent le changement en temps rel lorsque lutilisateur ajuste
la variable tPeak avec le curseur.

Lors de la mise feu dune fuse, la portion de combustible qui est expose lair
brle et provoque une pousse. De la mise feu au taux de combustion maximal, la
surface de combustion, partie de la zone initiale dallumage, augmente pour atteindre la surface totale du combustible. Ce taux maximal se produit au moment t peak.
La surface de combustion diminue ensuite mesure que le combustible est
consomm. Lapplication de calculs balistiques normalise le temps pour quil soit
0 lors de lallumage et 1 lorsque la combustion sarrte. Aussi, tpeak reprsente
un nombre entre 0 et 1.
Oozinoz utilise un jeu dquations de calcul du taux de combustion et de la pousse :
taux = 2 5

( t t peak )

taux 1 0,3
pousse = 1,7 ----------
0,6

pattern Livre Page 87 Vendredi, 9. octobre 2009 10:31 10

Chapitre 9

OBSERVER

87

Lapplication reprsente Figure 9.1 montre comment t peak influe sur le taux de
combustion et la pousse dune fuse. A mesure que lutilisateur dplace le curseur,
la valeur de tpeak change et les courbes suivent une autre forme. La Figure 9.2 illustre
les classes principales constituant lapplication.
Figure 9.2
Lapplication
de calculs balistiques
senregistre ellemme pour recevoir
les vnements de
curseur.

interface
ChangeListener

stateChanged(
e:ChangeEvent)

JPanel

2
ShowBallistics

BallisticsPanel

burnPanel():
BallisticsPanel

BallisticsPanel(
:BallisticsFunction)

slider():JSlider

setTPeak(tPeak:double)

thrustPanel():
BallisticsPanel
valueLabel():JLabel
stateChanged(
e:ChangeEvent)

interface
BallisticsFunction

Les classes ShowBallistics et BallisticsPanel sont des membres du package


app.observer.ballistics. Linterface BallisticsFunction est un membre du
package com.oozinoz.ballistics. Ce package contient aussi une classe utilitaire
Ballistics fournissant les instances de BallisticsFunction qui dfinissent les
courbes pour le taux de combustion et la pousse.
Lorsque lapplication initialise le curseur, elle senregistre elle-mme pour en recevoir les vnements. Lorsque la valeur du curseur change, lapplication actualise les
panneaux daffichage (Panel) des courbes ainsi que ltiquette (Label) affichant la
valeur de tpeak.

pattern Livre Page 88 Vendredi, 9. octobre 2009 10:31 10

88

Partie II

Patterns de responsabilit

Exercice 9.1
Compltez les mthodes slider() et stateChanged() pour ShowBallistics
de manire que les panneaux daffichage et la valeur de tpeak refltent la position du
curseur.
public JSlider slider() {
if (slider == null) {
slider = new JSlider();
sliderMax = slider.getMaximum();
sliderMin = slider.getMinimum();
slider.addChangeListener( ?? );
slider.setValue(slider.getMinimum());
}
return slider;
}
public void stateChanged(ChangeEvent e) {
double val = slider.getValue();
double tp = (val - sliderMin) / (sliderMax - sliderMin);
burnPanel(). ?? ( ?? );
thrustPanel(). ?? ( ?? );
valueLabel(). ?? ( ?? );
}

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.


La classe ShowBallistics actualise les objets pour les deux panneaux daffichage
et la valeur de tpeak, qui dpend de la valeur du curseur. Cest une pratique
frquente et pas ncessairement mauvaise, mais notez quelle dfait totalement
lobjectif de OBSERVER. Swing applique OBSERVER pour que le curseur nait pas
savoir quels sont les clients intresss par son tat. Lapplication ShowBallistics
nous place toutefois dans la situation que voulions viter, savoir : un seul objet,
lapplication, sait quels objets actualiser et se charge dmettre les interrogations
appropries au lieu de laisser chaque objet senregistrer lui-mme de manire individuelle.
Pour crer un OBSERVER dune plus grande granularit, vous pouvez apporter quelques changements au code pour laisser chaque composant intress senregistrer
lui-mme pour recevoir les vnements de changement du curseur.
Dans cette conception, vous pouvez dplacer les appels de addChangeListener() se
trouvant dans la mthode slider() vers les constructeurs des composants dpendants :

pattern Livre Page 89 Vendredi, 9. octobre 2009 10:31 10

Chapitre 9

OBSERVER

89

public BallisticsPanel2(
BallisticsFunction func,
JSlider slider) {
this.func = func;
this.slider = slider;
slider.addChangeListener(this);
}

Exercice 9.2
Produisez un nouveau diagramme de classes pour une conception laissant
chaque objet intress senregistrer pour recevoir les vnements du curseur.
Veillez tenir compte du champ affichant la valeur du curseur.

Lorsque le curseur change, lobjet BallisticsPanel2 en est averti. Le champ


tPeak recalcule sa valeur et se redessine :
public void stateChanged(ChangeEvent e) {
double val = slider.getValue();
double max = slider.getMaximum();
double min = slider.getMinimum();
tPeak = (val - min) / (max - min);
repaint();
}

Cette nouvelle conception donne lieu un nouveau problme. Chaque objet intress senregistre et sactualise lors des changements du curseur. Cette rpartition
de la responsabilit est bonne, mais chaque composant qui est lcoute des vnements du curseur doit recalculer la valeur de tPeak. En particulier, si vous utilisez
une classe BallisticsLabel2 comme dans la solution de lExercice 9.2 , sa
mthode stateChanged() sera presque identique la mthode stateChanged()
de BallisticsPanel2. Pour rduire ce code dupliqu, nous pouvons extraire un
objet de domaine sous-jacent partir de la prsente conception.
Nous pouvons simplifier le systme en introduisant une classe Tpeak qui contiendra
la valeur de temps critique. Lapplication restera alors lcoute des vnements du
curseur et actualisera un objet Tpeak auquel seront attentifs les autres composants
intresss. Cette approche devient une conception MVC (Modle-Vue-Contrleur)
(voir [Buschmann et al. 1996] pour un traitement en dtail de lapproche MVC).

pattern Livre Page 90 Vendredi, 9. octobre 2009 10:31 10

90

Partie II

Patterns de responsabilit

Modle-Vue-Contrleur
A mesure que les applications et les systmes augmentent de taille, il est important
de rpartir toujours davantage les responsabilits pour que les classes et les packages restent dune taille suffisamment petite pour faciliter la maintenance. La triade
Modle-Vue-Contrleur spare un objet intressant, le modle, des lments de
GUI qui le reprsentent et le manipulent, la vue et le contrleur. Java gre cette
sparation au moyen de listeners, mais comme le montre la section prcdente,
toutes les conceptions recourant des listeners ne suivent pas ncessairement une
approche MVC.
Les versions initiales de lapplication ShowBallistics combinent la logique intelligente dune interface GUI dapplication et des informations balistiques. Vous
pouvez rduire ce code par une approche MVC pour redistribuer les responsabilits
de lapplication. Dans le processus de recodage, la classe ShowBallistics rvise
garde les vues et les contrleurs dans ses lments de GUI.
Lide des crateurs de MVC tait que lapparence dun composant (la vue) pouvait
tre spare de ce qui lanimait (le contrleur). Dans la pratique, lapparence dun
composant de GUI et ses fonctionnalits supportant linteraction utilisateur sont
troitement couples, et lemploi typique de Swing ne spare pas les vues des
contrleurs si vous vous plongez davantage dans les rouages internes de ces
concepts dans Swing, vous verrez merger cette sparation. La valeur de MVC
rside dans le fait dextraire le modle dune application pour le placer dans un
domaine propre.
Le modle dans lapplication ShowBallistics est la valeur tPeak. Pour recoder
avec lapproche MVC, nous pourrions introduire une classe Tpeak qui contiendrait
cette valeur de temps crte et autoriser les listeners intresss senregistrer pour
recevoir les vnements de changement. Une telle classe pourrait ressembler
lextrait suivant :
package app.observer.ballistics3;
import java.util.Observable;
public class Tpeak extends Observable {
protected double value;
public Tpeak(double value) {
this.value = value;
}
public double getValue() {
return value;

pattern Livre Page 91 Vendredi, 9. octobre 2009 10:31 10

Chapitre 9

OBSERVER

91

}
public void setValue(double value) {
this.value = value;
setChanged();
notifyObservers();
}
}

Si vous deviez rviser ce code chez Oozinoz, un point essentiel serait soulev :
presque aucune portion de ce code ne se rapporte au moment o le taux de combustion atteint sa valeur crte. En fait, cet extrait ressemble un outil relativement
gnrique pour contenir une valeur et pour alerter des listeners lorsquelle change.
Nous pourrions modifier le code pour liminer ce caractre gnrique, mais examinons
tout dabord une conception rvise utilisant la classe Tpeak.
Nous pouvons maintenant laborer une conception dans laquelle lapplication reste
attentive au curseur et tous les autres composants restent lcoute de lobjet
Tpeak. Lorsque le curseur est dplac, lapplication change la valeur dans lobjet Tpeak.
Les panneaux daffichage et le champ de valeur sont lcoute de cet objet et
sactualisent lorsquil change. Les classes BurnRate et Thrust emploient lobjet
Tpeak pour le calcul de leurs fonctions, mais elles nont pas besoin dcouter les
vnements (cest--dire de senregistrer cet effet).
Exercice 9.3
Crez un diagramme de classes montrant lapplication dpendant du curseur
alors que les panneaux daffichage et le champ de valeur dpendent dun objet
Tpeak.

Cette conception permet de neffectuer quune seule fois le travail de traduction de


la valeur du curseur en valeur de temps crte. Lapplication actualise un seul objet
Tpeak, et tous les objets de GUI intresss par un changement peuvent interroger
lobjet pour en connatre la nouvelle valeur.
La classe Tpeak ne fait pas que conserver une valeur. Aussi essayons-nous de recoder lapplication pour crer une classe conteneur de valeur. De plus, il est possible
quun nombre observ, tel quune valeur de temps crte, ne soit pas une valeur
isole mais plutt lattribut dun objet de domaine. Par exemple, le temps crte est
un attribut dun moteur de fuse. Nous pouvons tenter damliorer notre conception

pattern Livre Page 92 Vendredi, 9. octobre 2009 10:31 10

92

Partie II

Patterns de responsabilit

pour sparer les classes, avec une classe permettant aux objets de GUI dobserver
les objets de domaine.
Lorsque vous dcidez de sparer des objets de GUI dobjets de domaine, ou
dobjets mtiers (business object), vous pouvez crer des couches de code. Une
couche est un groupe de classes ayant des responsabilits similaires, souvent
rassembles dans un seul package Java. Les couches suprieures, telles quune
couche GUI, dpendent gnralement seulement de classes situes dans des
couches de niveau gal ou infrieur. Le codage en couches demande gnralement
davoir une dfinition claire des interfaces entre les couches, telles quentre une
GUI et les objets mtiers quelle reprsente. Vous pouvez rorganiser les responsabilits du code de ShowBallistics pour obtenir un systme en couches, comme le
montre la Figure 9.3.
Figure 9.3

Observer

Observer

En crant une classe


Tpeak observable,
vous pouvez sparer
la logique mtier et
la couche GUI.

BallisticsPanel

BallisticsLabel

Couche GUI
Couche mtier

Observable

Tpeak

addObserver(o:Observer)

getValue()

notifyObservers()

setValue(value:double)

setChanged()

La conception illustre Figure 9.3 cre une classe Tpeak pour modliser la valeur
tpeak critique pour les rsultats des quations balistiques affichs par lapplication.
Les classes BallisticsPanel et BallisticsLabel dpendent de Tpeak. Plutt
que de laisser lobjet Tpeak la responsabilit dactualiser les lments de GUI,
la conception applique le pattern OBSERVER pour que les objets intresss puissent
senregistrer pour tre notifis de tout changement de Tpeak. Les bibliothques
de classes Java offrent un support en fournissant une classe Observable et une

pattern Livre Page 93 Vendredi, 9. octobre 2009 10:31 10

Chapitre 9

OBSERVER

93

interface Observer contenues dans le package java.util package. La classe


Tpeak peut driver une sous-classe Observable et actualiser ses objets "observateurs"
lorsque sa valeur change :
public void setValue(double value) {
this.value = value;
setChanged();
notifyObservers();
}

Notez que vous devez appeler setChanged() de faon que la mthode notifyObservers(), hrite dObservable, signale le changement.
La mthode notifyObservers() invoque la mthode update() de chaque observateur enregistr. La mthode update() est une exigence pour les classes implmentant linterface Observer, comme illustr Figure 9.4.
Figure 9.4
Un objet BallisticsLabel est un Observer.
Il peut senregistrer
auprs dun objet
Observable comme
objet intress pour
que la mthode
update() de lobjet
label soit appele
lorsque lobjet Observable change.

interface

BallisticsLabel

Observer

update(
o:Observable,
arg:Object)

BallisticsLabel(
tPeak:Tpeak)
update(
o:Observable,
arg:Object)

Un objet BallisticsLabel na pas besoin de conserver une rfrence vers lobjet


Tpeak quil observe. Au lieu de cela, le constructeur de BallisticsLabel peut
senregistrer pour obtenir les mises jour lorsque lobjet Tpeak change. La
mthode update() de lobjet BallisticsLabel recevra lobjet Tpeak en tant
quargument Observable. La mthode peut transtyper (cast) largument en Tpeak,
extraire la nouvelle valeur, modifier le champ de valeur et redessiner lcran.
Exercice 9.4
Rdigez le code complet pour BallisticsLabel.java.

pattern Livre Page 94 Vendredi, 9. octobre 2009 10:31 10

94

Partie II

Patterns de responsabilit

La nouvelle conception de lapplication de calcul balistique spare lobjet mtier


des lments de GUI qui le reprsentent. Deux exigences doivent tre respectes
pour quelle fonctionne.
1. Les implmentations de Observer doivent senregistrer pour signaler leur intrt et doivent sactualiser elles-mmes de faon correcte, souvent en incluant
lactualisation de laffichage.
2. Les sous-classes de Observable doivent notifier les objets intresss observateurs
lorsque leurs valeurs changent.
Ces deux tapes dfinissent la plupart des interactions dont vous avez besoin
entres les diffrentes couches de lapplication balistique. Il vous faut aussi
prvoir un objet Tpeak qui change conformment lorsque la position du curseur
change. Vous pouvez pour cela instancier une sous-classe anonyme de ChangeListener.

Exercice 9.5
Supposez que tPeak soit une instance de Tpeak et un attribut de la classe
ShowBallistics3. Compltez le code de ShowBallistics3.slider() de
faon que le changement du curseur actualise tPeak.
public JSlider slider() {
if (slider == null) {
slider = new JSlider();
sliderMax = slider.getMaximum();
sliderMin = slider.getMinimum();
slider.addChangeListener
(
new ChangeListener()
{
// Exercice !
}
);
slider.setValue(slider.getMinimum());
}
return slider;
}

pattern Livre Page 95 Vendredi, 9. octobre 2009 10:31 10

Chapitre 9

OBSERVER

95

Lorsque vous appliquez lapproche MVC, le flux des vnements peut sembler
indirect. Un mouvement de curseur dans lapplication de calculs balistiques provoque lactualisation dun objet Tpeak par un objet ChangeListener. En retour, un
changement dans lobjet Tpeak notifie les objets daffichage (champ et panneaux)
qui actualisent leur affichage. Le changement est rpercut de la couche GUI la
couche mtier puis nouveau vers la couche GUI.

:JSlider

:ChangeListener

:BallisticsLabel

Couche GUI
Couche mtier
:Tpeak

??
??

??

Figure 9.5
Lapproche MVC demande de passer les messages de la couche GUI vers la couche mtier
puis nouveau vers la couche GUI.

Le bnfice dune telle conception en couches rside dans la valeur de linterface et


dans le niveau dindpendance que vous obtenez entre les couches. Ce codage en
couches est une distribution en couches des responsabilits, ce qui produit un code
plus simple maintenir. Par exemple, dans notre exemple dapplication de calculs
balistiques, vous pouvez ajouter une seconde GUI, peut-tre pour un quipement
portable, sans avoir changer les classes de la couche objets mtiers. De mme,
vous pourriez ajouter dans cette dernire une nouvelle source de changement qui
actualise un objet Tpeak. Dans ce cas, le mcanisme OBSERVER dj en place met
automatiquement jour les objets de la couche GUI.

pattern Livre Page 96 Vendredi, 9. octobre 2009 10:31 10

96

Partie II

Patterns de responsabilit

Cette conception en couches rend aussi possible lexcution de diffrentes couches


sur diffrents ordinateurs. Une couche ou un ensemble de couches sexcutant sur
un ordinateur constitue un niveau dans un systme multiniveau (n-tier). Une
conception multiniveau peut rduire la quantit de code devant tre excute sur
lordinateur de lutilisateur final. Elle vous permet aussi dapporter des changements dans les classes mtiers sans avoir changer le logiciel sur les machines des
utilisateurs, ce qui simplifie grandement le dploiement. Toutefois, lchange de
messages entre ordinateurs a son cot et le dploiement en environnement multiniveau doit tre fait judicieusement. Par exemple, vous ne pourrez probablement
pas vous permettre de faire attendre lutilisateur pendant que les vnements de
dfilement transitent entre son ordinateur et le serveur. Dans ce cas, vous devrez
probablement laisser le dfilement se produire sur la machine de lutilisateur, puis
concevoir sous forme dune autre action utilisateur distincte la validation dune
nouvelle valeur de temps crte. En bref, OBSERVER supporte larchitecture MVC, ce
qui promeut la conception en couches et saccompagne de nombreux avantages
pour le dveloppement et le dploiement de logiciels.

Maintenance dun objet Observable


Vous ne pourrez pas toujours crer la classe que vous voulez pour guetter les changements dune sous-classe de Observable. En particulier, votre classe peut dj
tre une sous-classe de quelque chose dautre que Object. Dans ce cas, vous
pouvez associer votre classe un objet Observable et faire en sorte quelle lui
transmette les appels de mthodes essentiels. La classe Component dans java.awt
suit cette approche mais utilise un objet PropertyChangeSupport la place dun
objet Observable.
La classe PropertyChangeSupport est semblable la classe Observable, mais
elle fait partie du package java.beans. LAPI JavaBeans permet la cration de
composants rutilisables. Elle trouve sa plus grande utilit dans le dveloppement
de composants de GUI, mais vous pouvez certainement lappliquer dautres fins.
La classe Component emploie un objet PropertyChangeSupport pour permettre
aux objets observateurs intresss de senregistrer et de recevoir une notification de
changement des proprits de champs, de panneaux, et dautres lments de GUI.
La Figure 9.6 montre la relation qui existe entre la classe Component de java.awt
et la classe PropertyChangeSupport.

pattern Livre Page 97 Vendredi, 9. octobre 2009 10:31 10

Chapitre 9

OBSERVER

97

La classe PropertyChangeSupport illustre un problme quil vous faudra rsoudre


lors de lemploi du pattern OBSERVER, savoir, le niveau de dtails fournir par la
classe observe pour indiquer ce qui a chang. Cette classe utilise une approche
push, o le modle renseigne sur ce qui sest produit dans PropertyChangeSupport, la notification indique le changement de la proprit, dune ancienne valeur
une nouvelle valeur. Une autre option est lapproche pull, o le modle signale aux
objets observateurs quil a chang, mais ceux-ci doivent interroger le modle pour
savoir de quelle manire. Les deux approches peuvent tre appropries. Lapproche
push peut signifier davantage de travail de dveloppement et associe troitement les
objets observateurs lobjet observ, mais offre la possibilit de meilleures performances.
La classe Component duplique une partie de linterface de la classe PropertyChangeSupport. Ces mthodes dans Component transmettent chacune lappel de message
vers une instance de la classe PropertyChangeSupport.

Component

PropertyChangeSupport

addPropertyChangeListener(
l:PropertyChangeListener)

addPropertyChangeListener(
l:PropertyChangeListener)

firePropertyChange(
propertyName:String,
oldValue:Object,
newValue:Object)

firePropertyChange(
propertyName:String,
oldValue:Object,
newValue:Object)

removePropertyChangeListener(
l:PropertyChangeListener)
...

removePropertyChangeListener(
l:PropertyChangeListener)

interface
PropertyChangeListener

propertyChange(
e:PropertyChangeEvent)

Figure 9.6
Un objet Component renseigne un objet PropertyChangeSupport qui renseigne
un ensemble de listeners.

pattern Livre Page 98 Vendredi, 9. octobre 2009 10:31 10

98

Partie II

Patterns de responsabilit

??

??

BallisticsLabel

BallisticsPanel

Tpeak

PropertyChangeSupport

??
??
??
??
??

Figure 9.7
Un objet mtier Tpeak peut dlguer les appels qui affectent les listeners un objet Property-

ChangeSupport.

Exercice 9.7
Compltez le diagramme de classes de la Figure 9.7 pour que Tpeak utilise un
objet PropertyChangeSupport afin de grer des listeners.
Que vous utilisiez Observer, PropertyChangeSupport ou une autre classe pour
appliquer le pattern OBSERVER, limportant est de dfinir une dpendance un-plusieurs entre des objets. Lorsque ltat dun objet change, tous les objets
dpendants en sont avertis et sont actualiss automatiquement. Cela limite la responsabilit et facilite la maintenance des objets intressants et de leurs observateurs
intresss.

pattern Livre Page 99 Vendredi, 9. octobre 2009 10:31 10

Chapitre 9

OBSERVER

99

Rsum
Le pattern OBSERVER apparat frquemment dans les applications avec GUI et
constitue un pattern fondamental dans les bibliothques de GUI Java. Avec ces
composants, vous navez jamais besoin de modifier ou de driver une sous-classe
dune classe de composant simplement pour communiquer ses vnements
dautres objets intresss. Pour de petites applications, une pratique courante
consiste nenregistrer quun seul objet, lapplication, pour recevoir les vnements dune GUI. Il ny a pas de problme inhrent cette approche, mais sachez
quelle inverse la rpartition des responsabilits que vise OBSERVER. Pour une
grande GUI, envisagez la possibilit de passer une conception MVC, en permettant chaque objet intress de grer son besoin dtre notifi au lieu dintroduire
un objet central mdiateur. Lapproche MVC vous permet aussi dassocier avec
davantage de souplesse diverses couches de lapplication, lesquelles peuvent alors
changer de faon indpendante et tre excutes sur des machines diffrentes.

pattern Livre Page 100 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 101 Vendredi, 9. octobre 2009 10:31 10

10
MEDIATOR
Le dveloppement orient objet ordinaire distribue la responsabilit aussi loin que
possible, avec chaque objet accomplissant sa tche indpendamment des autres. Par
exemple, le pattern OBSERVER supporte cette distribution en limitant la responsabilit dun objet que dautres objets trouvent intressant. Le pattern SINGLETON
rsiste la distribution de la responsabilit et vous permet de la centraliser au
niveau de certains objets que les clients localisent et rutilisent. A linstar de
SINGLETON, le pattern MEDIATOR centralise la responsabilit mais pour un ensemble
spcifique dobjets plutt que pour tous les clients dans un systme.
Lorsque les interactions entre les objets reposent sur une condition complexe impliquant que chaque objet dun groupe connaisse tous les autres, il est utile dtablir
une autorit centrale. La centralisation de la responsabilit est galement utile lorsque la logique entourant les interactions des objets en relation est indpendante de
lautre comportement des objets.
Lobjectif du pattern MEDIATOR est de dfinir un objet qui encapsule la faon
dont un ensemble dobjets interagissent. Cela promeut un couplage lche,
vitant aux objets davoir se rfrer explicitement les uns aux autres, et
permet de varier leur interaction indpendamment.

Un exemple classique : mdiateur de GUI


Cest probablement lors du dveloppement dune application GUI que vous
rencontrerez le plus le pattern MEDIATOR. Le code dune telle application tend
devenir volumineux, demandant tre refactoris en dautres classes. La classe
ShowFlight dans le Chapitre 4, consacr au pattern FACADE, remplissait initialement

pattern Livre Page 102 Vendredi, 9. octobre 2009 10:31 10

102

Partie II

Patterns de responsabilit

trois rles. Avant quelle ne soit refactorise, elle servait de panneau daffichage,
dapplication GUI complte et de calculateur de trajectoire de vol. La refactorisation a permis de simplifier lapplication invoquant laffichage du panneau de
trajectoire, la ramenant seulement quelques lignes de code. Toutefois, les grosses
applications peuvent conserver leur complexit aprs ce type de refactorisation,
mme si elles nincluent que la logique qui cre les composants et dfinit leur interaction. Considrez lapplication de la Figure 10.1.

Figure 10.1
Cette application laisse lutilisateur actualiser manuellement lemplacement dun bac
de produit chimique.

Oozinoz stocke les produits chimiques dans des bacs (tub) en plastique. Les machines lisent les codes-barres sur les bacs pour garder trace de leur emplacement dans
lusine. Parfois, une correction manuelle est ncessaire, notamment lorsquun
employ dplace un bac au lieu dattendre quun robot le transfre. La Figure 10.1
prsente une nouvelle application partiellement dveloppe qui permet lutilisateur
de spcifier la machine au niveau de laquelle un bac se situe.
Dans lapplication MoveATub, du package app.mediator.moveATub, lorsque
lutilisateur slectionne une des machines dans la liste de gauche, la liste de bacs
change pour afficher ceux prsents sur la machine en question. Il peut ensuite slectionner un des bacs, choisir une machine cible, et cliquer sur le bouton Do it! pour
actualiser lemplacement du bac. La Figure 10.2 prsente un extrait de la classe de
lapplication.
Le dveloppeur de cette application la cre initialement laide dun assistant et
a commenc la refactoriser. Environ la moiti des mthodes de MoveATub existent

pattern Livre Page 103 Vendredi, 9. octobre 2009 10:31 10

Chapitre 10

MEDIATOR

103

MoveATub

MoveATub()
assignButton():JButton

interface
ListSelectionListener

actionPerformed()
boxes():List
boxList():JList
machineList():JList

interface
ActionListener

tubList():ListView
valueChanged()
labeledPanel():JPanel
tubList():ListView
updateTubList(:String)
main()

Figure 10.2
La classe MoveATub combine des mthodes de cration de composants, de gestion dvnements,
et de base de donnes fictive.

pour procder une initialisation paresseuse des variables contenant les composants GUI de lapplication. La mthode assignButton() est un exemple typique :
private JButton assignButton() {
if (assignButton == null) {
assignButton = new JButton("Do it!");
assignButton.setEnabled(false);
assignButton.addActionListener(this);
}
return assignButton;
}

Le programmeur a dj limin les valeurs codes en dur gnres par lassistant


pour spcifier lemplacement et la taille du bouton. Mais un problme plus immdiat est que la classe MoveATub comporte un grand nombre de mthodes ayant des
utilits diffrentes.
La plupart des mthodes statiques fournissent une base de donnes fictive de noms
de bacs et de noms de machines. Le dveloppeur envisage de remplacer lapproche
consistant utiliser uniquement des noms par lemploi dobjets Tub et Machine.

pattern Livre Page 104 Vendredi, 9. octobre 2009 10:31 10

104

Partie II

Patterns de responsabilit

La majorit des autres mthodes contient la logique de gestion des vnements de


lapplication. Par exemple, la mthode valueChanged() dtermine si le bouton
dassignation a t activ :
public void valueChanged(ListSelectionEvent e) {
// ...
assignButton().setEnabled(
! tubList().isSelectionEmpty()
&& ! machineList().isSelectionEmpty());
}

Le dveloppeur pourrait placer la mthode valueChanged() et les autres mthodes


de gestion dvnements dans une classe mdiateur distincte. Il convient de noter
que le pattern MEDIATOR est dj luvre dans la classe MoveATub: les composants ne sactualisent pas directement les uns les autres. Par exemple, ni la machine
ni les composants liste nactualisent directement le bouton dassignation. A la
place, lapplication MoveATub enregistre des listeners pour les vnements de slection puis actualise le bouton, selon les lments slectionns dans les deux listes.
Dans cette application, un objet MoveATub agit en tant que mdiateur, recevant les
vnements et dispatchant les actions correspondantes.
Les bibliothques de classes Java, ou JCL (Java Class Libraries), vous incitent
utiliser un mdiateur mais nimposent aucunement que lapplication soit son propre
mdiateur. Au lieu de mlanger dans une mme classe des mthodes de cration de
composants, des mthodes de gestion dvnements et des mthodes de base de
donnes fictive, il serait prfrable de les placer dans des classes avec des spcialisations distinctes.
La refactorisation donne au mdiateur une classe propre, vous permettant de le
dvelopper et de vous concentrer dessus sparment. Lorsque lapplication refactorise sexcute, les composants passent les vnements un objet MoveATubMediator. Le mdiateur peut avoir une action sur des objets non-GUI, par exemple
pour actualiser la base de donnes lorsquune assignation a lieu. Il peut aussi
rappeler des composants GUI, par exemple pour dsactiver le bouton lissue de
lassignation.
Les composants GUI pourraient appliquer le pattern MEDIATOR automatiquement,
signalant un mdiateur lorsque des vnements surviennent plutt que de prendre
la responsabilit dactualiser directement dautres composants. Les applications
GUI donnent probablement lieu lexemple le plus courant de MEDIATOR, mais il
existe dautres situations o vous pourriez vouloir introduire un mdiateur.

pattern Livre Page 105 Vendredi, 9. octobre 2009 10:31 10

Chapitre 10

MEDIATOR

MoveATub2

105

MoveATubMediator

NameBase

Figure 10.3
Sparation des mthodes de cration de composants, des mthodes de gestion dvnements
et des mthodes de base de donnes fictive de lapplication.

Exercice 10.2
Dessinez un diagramme illustrant ce qui se produit lorsque lutilisateur clique sur
le bouton Do it!. Montrez quels objets sont daprs vous les plus importants ainsi
que les messages changs entre ces objets.
Lorsque linteraction dun ensemble dobjets est complexe, vous pouvez centraliser
la responsabilit de cette interaction dans un objet mdiateur qui reste extrieur au
groupe. Cela promeut le couplage lche (loose coupling), cest--dire une rduction de la responsabilit que chaque objet entretient vis--vis de chaque autre. Grer
cette interaction dans une classe indpendante prsente aussi lavantage de simplifier et de standardiser les rgles dinteraction. La valeur dun mdiateur apparat de
manire vidente lorsque vous avez besoin de grer lintgrit relationnelle.

pattern Livre Page 106 Vendredi, 9. octobre 2009 10:31 10

106

Partie II

Patterns de responsabilit

Mdiateur dintgrit relationnelle


Le paradigme orient objet doit sa puissance en partie au fait quil permet de reprsenter aisment au moyen dobjets Java les relations entre des objets du monde rel.
Toutefois, la capacit dun modle objet Java reflter le monde rel se heurte
deux limitations. Premirement, les objets rels varient avec le temps et Java noffre
aucun support intgr pour cela. Par exemple, les instructions dassignation liminent toute valeur prcdente au lieu de la mmoriser, comme un tre humain le
ferait. Deuximement, dans le monde rel, les relations sont aussi importantes que
les objets, alors quelles ne bnficient que dun faible support dans les langages
orients objet actuels, Java y compris. Par exemple, il ny a pas de support intgr
pour le fait que si la machine Star Press 2402 se trouve dans la trave 1, la trave 1
doit contenir la machine Star Press 2402. En fait, de telles relations risquent dtre
ignores, do lintrt dappliquer le pattern MEDIATOR.
Considrez les bacs (tub) en plastique dOozinoz. Ces bacs sont toujours assigns
une certaine machine. Vous pouvez modliser cette relation au moyen dune table,
comme lillustre le Tableau 10.1.
Tableau 10.1 : Enregistrer les informations relationnelles dans une table
prserve lintgrit relationnelle

Bac

Machine

T305

StarPress-2402

T308

StarPress-2402

T377

ShellAssembler-2301

T379

ShellAssembler-2301

T389

ShellAssembler-2301

T001

Fuser-2101

T002

Fuser-2101

Le Tableau 10.1 illustre la relation entre les bacs et les machines, cest--dire leur
positionnement rciproque. Mathmatiquement, une relation est un sous-ensemble
de toutes les paires ordonnes dobjets, tel quil y a une relation des bacs vers les
machines et une relation des machines vers les bacs. Lunicit des valeurs de la

pattern Livre Page 107 Vendredi, 9. octobre 2009 10:31 10

Chapitre 10

MEDIATOR

107

colonne Bac garantit quaucun bac ne peut apparatre sur deux machines la fois.
Voyez lencadr suivant pour une dfinition plus stricte de la cohrence relationnelle dans un modle objet.
Intgrit relationnelle
Un modle objet prsente une cohrence relationnelle si chaque fois que lobjet a pointe
vers lobjet b, lobjet b pointe vers lobjet a.
Pour une dfinition plus rigoureuse, considrez deux classes, Alpha et Beta. A reprsente
lensemble des objets qui sont des instances de la classe Alpha, et B reprsente lensemble
des objets qui sont des instances de la classe Beta. a et b sont donc des membres respectivement de A et de B, et la paire ordonne (a, b) indique que lobjet a A possde une rfrence vers lobjet b B. Cette rfrence peut soit tre directe soit faire partie dun
ensemble de rfrences, comme lorsque lobjet a possde un objet List qui inclut b.
Le produit cartsien A B est lensemble de toutes les paires ordonnes possibles (a, b) avec
a A et b B. Les ensembles A et B autorisent les deux produits cartsiens A B et B A.

Une relation de modle objet sur A et B est le sous-ensemble de A B qui existe dans un
modle objet. AB reprsente ce sous-ensemble, et BA reprsente le sous-ensemble B A qui
existe dans le modle.
Toute relation binaire R A B possde un inverse R1 B A dfini par :
(b, a) R1 si et seulement si (a, b) R

Linverse de AB fournit lensemble des rfrences qui doivent exister de B vers les instances
de A lorsque le modle objet est cohrent. Autrement dit, les instances des classes Alpha et
Beta sont cohrentes relationnellement si et seulement si BA est linverse de AB.

Lorsque vous enregistrez les informations relationnelles des bacs et des machines
dans une table, vous pouvez garantir que chaque bac se trouve sur une seule
machine la fois en appliquant comme restriction quil napparaisse quune seule
fois dans la colonne Bac. Une faon de procder est de dfinir cette colonne comme
cl primaire de la table dans une base de donnes relationnelle. Avec ce modle, qui
reflte la ralit, un bac ne peut pas apparatre sur deux machines en mme temps :
(b, a) R1 si et seulement si (a, b) R.
Un modle objet ne peut pas garantir lintgrit relationnelle aussi facilement quun
modle relationnel. Considrez lapplication MoveATub. Comme voqu prcdemment, son concepteur prvoit dabandonner lemploi de noms au profit dobjets Tub
et Machine. Lorsquun bac se trouve prs dune machine, lobjet qui le reprsente
possde une rfrence vers lobjet reprsentant la machine. Chaque objet Machine

pattern Livre Page 108 Vendredi, 9. octobre 2009 10:31 10

108

Partie II

Patterns de responsabilit

possde une collection dobjets Tub reprsentant les bacs situs prs de la machine.
La Figure 10.4 illustre un modle objet typique.
Figure 10.4
Un modle objet
distribue les informations sur les relations.

T305
StarPress-2402
T377

T379

T389

T308
Assembler-2301

T001
Fuser-2101
T002

Les doubles flches de cette figure mettent en vidence le fait que les bacs ont
connaissance des machines et vice versa. Les informations sur cette relation bac/
machine sont maintenant distribues travers de nombreux objets au lieu de figurer
dans une table centrale, ce qui la rend plus difficile grer et fait quelle se prte
bien lapplication du pattern MEDIATOR.
Considrez une anomalie survenue chez Oozinoz lorsquun dveloppeur a
commenc modliser une nouvelle machine incluant un lecteur de codes-barres
pour identifier les bacs. Aprs scannage dun bac t pour obtenir son identifiant,
son emplacement est dfini comme tant sur la machine m au moyen du code
suivant :
// Renseigne le bac sur la machine et inversement
t.setMachine(m);
m.addTub(t);

Le moyen le plus simple de garantir lintgrit relationnelle est de replacer les


informations relationnelles dans une seule table gre par un objet mdiateur. Au
lieu que les machines aient connaissance des bacs et inversement, il faut donner
ces objets une rfrence vers un mdiateur qui soccupe de grer la table. Cette
table peut tre une instance de la classe Map (du package java.util). La
Figure 10.6 prsente un diagramme de classe incluant un mdiateur.

pattern Livre Page 109 Vendredi, 9. octobre 2009 10:31 10

Chapitre 10

MEDIATOR

T305
StarPress-2402
T377

T379

T389

T308
ShellAssembler-2301

T001
Fuser-2101
T002

Figure 10.5
Une fois complt, ce diagramme mettra en vidence lerreur dans le code qui actualise
lemplacement du bac.

Figure 10.6
Les objets Tub et
Machine sappuient
sur un mdiateur
pour contrler la
relation entre les
bacs et les machines.

Tub

Machine

mediator:TubMediator

mediator:TubMediator

getLocation():Machine

addTub(t:Tub)

setLocation(m:Machine)

getTubs():Set

TubMediator
-tubToMachine:Map

getTubs(m:Machine):Set
getMachine(t:Tub):Machine
set(t:Tub,m:Machine)

109

pattern Livre Page 110 Vendredi, 9. octobre 2009 10:31 10

110

Partie II

Patterns de responsabilit

La classe Tub possde un attribut demplacement qui permet denregistrer la


machine proximit de laquelle se trouve un bac. Le code garantit quun bac ne
peut tre qu un seul endroit la fois, utilisant un objet TubMediator pour grer la
relation bac/machine :
package com.oozinoz.machine;
public class Tub {
private String id;
private TubMediator mediator = null;
public Tub(String id, TubMediator mediator) {
this.id = id;
this.mediator = mediator;
}

public Machine getLocation() {


return mediator.getMachine(this);
}
public void setLocation(Machine value) {
mediator.set(this, value);
}
public String toString() {
return id;
}
public int hashCode() {
// ...
}
public boolean equals(Object obj) {
// ...
}
}

La mthode setLocation() de la classe Tub utilise un mdiateur pour actualiser


lemplacement dun bac, lui dlguant la responsabilit de prserver lintgrit relationnelle. Cette classe implmente les mthodes hashCode() et equals() de sorte
que les objets Tub puissent tre correctement stocks dans une table de hachage.
Voici les dtails du code :
public int hashCode() {
return id.hashCode();
}
public boolean equals(Object obj) {

pattern Livre Page 111 Vendredi, 9. octobre 2009 10:31 10

Chapitre 10

MEDIATOR

111

if (obj == this) return true;


if (obj.getClass() != Tub.class)
return false;
Tub that = (Tub) obj;
return id.equals(that.id);
}

La classe TubMediator utilise un objet Map pour stocker la relation bac/machine.


Le mdiateur peut ainsi garantir que le modle objet nautorise jamais deux machines
possder le mme bac :
public class TubMediator {
protected Map tubToMachine = new HashMap();
public Machine getMachine(Tub t) {
// Exercice !
}
public Set getTubs(Machine m) {
Set set = new HashSet();
Iterator i = tubToMachine.entrySet().iterator();
while (i.hasNext()) {
Map.Entry e = (Map.Entry) i.next();
if (e.getValue().equals(m))
set.add(e.getKey());
}
return set;
}
public void set(Tub t, Machine m) {
// Exercice !
}
}

Exercice 10.4
Ecrivez le code des mthodes getMachine() et set() de la classe TubMediator.

Plutt que dintroduire une classe mdiateur, vous pourriez garantir quun bac ne se
trouve jamais sur deux machines en mme temps en plaant la logique directement
dans les classes Tub et Machine. Toutefois, cette logique prserve lintgrit relationnelle et na pas grand-chose voir avec le fonctionnement des bacs et des
machines. Elle peut aussi tre source derreurs. Une erreur possible serait de dplacer

pattern Livre Page 112 Vendredi, 9. octobre 2009 10:31 10

112

Partie II

Patterns de responsabilit

un bac vers une autre machine, en actualisant ces deux objets mais pas la machine
prcdente.
Lemploi dun mdiateur permet dencapsuler dans une classe indpendante la logique dfinissant la faon dont les objets interagissent. Au sein du mdiateur, il est
facile de sassurer que le fait de changer lemplacement dun objet Tub loigne
automatiquement le bac de la machine sur laquelle il se trouvait. Le code de test
JUnit suivant, du package TubTest.java, prsente ce comportement :
public void testLocationChange() {
TubMediator mediator = new TubMediator();
Tub t = new Tub("T403", mediator);
Machine m1 = new Fuser(1001, mediator);
Machine m2 = new Fuser(1002, mediator);
t.setLocation(m1);
assertTrue(m1.getTubs().contains(t));
assertTrue(!m2.getTubs().contains(t));
t.setLocation(m2);
assertFalse(m1.getTubs().contains(t));
assertTrue(m2.getTubs().contains(t));
}

Lorsque vous disposez dun modle objet qui nest pas li une base de donnes
relationnelle, vous pouvez utiliser des mdiateurs pour prserver lintgrit relationnelle de votre modle. Confier la gestion de la relation des mdiateurs permet
ces classes de se spcialiser dans cette prservation.
Exercice 10.5
Pour ce qui est dextraire une logique dune classe ou dune hirarchie existante
afin de la placer dans une nouvelle classe, MEDIATOR ressemble dautres
patterns. Citez deux autres patterns pouvant impliquer une telle refactorisation.

Rsum
Le pattern MEDIATOR promeut le couplage lche, vitant des objets en relation de
devoir se rfrer explicitement les uns aux autres. Il intervient le plus souvent dans
le dveloppement dapplications GUI, lorsque vous voulez viter davoir grer la
complexit lie lactualisation mutuelle dobjets. Larchitecture de Java vous
pousse dans cette direction, vous encourageant dfinir des objets qui enregistrent

pattern Livre Page 113 Vendredi, 9. octobre 2009 10:31 10

Chapitre 10

MEDIATOR

113

des listeners pour les vnements GUI. Si vous dveloppez des interfaces utilisateur
avec Java, vous appliquez probablement ce pattern.
Bien que ce chapitre puisse vous inciter utiliser le pattern MEDIATOR lors de la
cration dune interface GUI, sachez que Java ne vous oblige pas extraire cette
mdiation de la classe de lapplication. Mais cela peut nanmoins simplifier votre
code. Le mdiateur peut ainsi se concentrer sur linteraction entre les composants
GUI, et la classe dapplication peut se concentrer sur la construction des composants.
Dautres situations se prtent lintroduction dun objet mdiateur. Par exemple,
vous pourriez en avoir besoin pour centraliser la responsabilit de prserver lintgrit relationnelle dans un modle objet. Vous pouvez appliquer MEDIATOR chaque
fois que vous devez dfinir un objet qui encapsule la faon dont un ensemble
dobjets interagissent.

pattern Livre Page 114 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 115 Vendredi, 9. octobre 2009 10:31 10

11
PROXY
Un objet ordinaire fait sa part de travail pour supporter linterface publique quil
annonce. Il peut nanmoins arriver quun objet lgitime ne soit pas en mesure
dassumer cette responsabilit ordinaire. Cela peut se produire lorsque lobjet met
beaucoup de temps se charger, lorsquil sexcute sur un autre ordinateur, ou
lorsque vous devez intercepter des messages qui lui sont destins. Dans de telles
situations, un objet proxy peut prendre cette responsabilit vis--vis dun client et
transmettre les requtes au moment voulu lobjet cible sous-jacent.
Lobjectif du pattern PROXY est de contrler laccs un objet en fournissant
un intermdiaire pour cet objet.

Un exemple classique : proxy dimage


Un objet proxy possde gnralement une interface qui est quasiment identique
celle de lobjet auquel il sert dintermdiaire. Il accomplit sa tche en transmettant
lorsquil se doit les requtes lobjet sous-jacent auquel il contrle laccs. Un
exemple classique du pattern PROXY intervient pour rendre plus transparent le chargement dimages volumineuses en mmoire. Imaginez que les images dune application doivent apparatre dans des pages ou panneaux qui ne sont pas affichs
initialement. Pour viter de charger ces images avant quelles ne soient requises,
vous pourriez leur substituer des proxies qui soccuperaient de les charger la
demande. Cette section prsente un exemple dun tel proxy. Notez toutefois que les
conceptions qui emploient le pattern PROXY sont parfois fragiles car elles sappuient
sur la transmission dappels de mthodes des objets sous-jacents. Cette transmission
peut produire une conception fragile et coteuse en maintenance.

pattern Livre Page 116 Vendredi, 9. octobre 2009 10:31 10

116

Partie II

Patterns de responsabilit

Imaginez que vous soyez ingnieur chez Oozinoz et travailliez un proxy dimage
qui, pour des raisons de performances, affichera une petite image temporaire
pendant le chargement dune image plus volumineuse. Vous disposez dun prototype oprationnel (voir Figure 11.1). Le code de cette application est contenu dans
la classe ShowProxy du package app.proxy. Le code sous-jacent qui supporte cette
application se trouve dans le package com.oozinoz.imaging.

Figure 11.1
Les captures dcran illustrent une mini-application avant, pendant et aprs le chargement
dune image volumineuse (cette image appartient au domaine public. Library of Congress,
Prints and Photographs Division, Gottscho-Schleisner Collection [LC-G605-CT-00488]).

Linterface utilisateur affiche lune des trois images suivantes : une image indiquant
que le chargement na pas encore commenc (Absent), une image indiquant que
limage est en cours de chargement (Loading), ou limage voulue. Lorsque
lapplication dmarre, elle affiche Absent, une image JPEG cre laide dun outil
de dessin. Lorsque lutilisateur clique sur Load, une image Loading prdfinie
saffiche presque instantanment. Aprs quelques instants, limage dsire apparat.

pattern Livre Page 117 Vendredi, 9. octobre 2009 10:31 10

Chapitre 11

PROXY

117

Un moyen ais dafficher une image enregistre au format JPEG, par exemple, est
dutiliser un objet ImageIcon comme argument dun "label" qui affichera limage :
ImageIcon icon = new ImageIcon("images/fest.jpg");
JLabel label = new JLabel(icon);

Dans lapplication que vous dveloppez, vous voulez passer JLabel un proxy qui
transmettra les requtes de dessin de lcran (paint) : (1) une image Absent, (2)
une image Loading, ou (3) limage dsire. Le flux des messages est reprsent
dans le diagramme de squence de la Figure 11.2.
Figure 11.2
Un objet ImageIconProxy transmet
les requtes paint()
lobjet ImageIcon

:ImageIconProxy

:Client

:JLabel

courant.

current:ImageIcon

paint()
paint()
paint()

Lorsque lutilisateur clique sur Load, votre code fait en sorte que limage courante
de lobjet ImageIconProxy devienne Loading, et le proxy entame le chargement
de limage attendue. Une fois celle-ci compltement charge, elle devient limage
courante de ImageIconProxy.
Pour crer un proxy, vous pouvez driver une sous-classe de ImageIcon, comme le
montre la Figure 11.3. Le code de ImageIconProxy dfinit deux variables statiques
contenant les images Absent et Loading :
static final ImageIcon ABSENT = new ImageIcon(
ClassLoader.getSystemResource("images/absent.jpg"));
static final ImageIcon LOADING = new ImageIcon(
ClassLoader.getSystemResource("images/loading.jpg"));

pattern Livre Page 118 Vendredi, 9. octobre 2009 10:31 10

118

Partie II

Patterns de responsabilit

Excutable

ImageIcon
//~25 champs non inclus ici
getIconHeight():int
getIconWidth():int

ImageIconProxy
ABSENT:ImageIcon
LOADING:ImageIcon
current:ImageIcon

paintIcon(
c:Component,
g:Graphics,
x:int,
y:int)

ImageIconProxy(
filename:String)

//~50 autres mthodes

getIconHeight():int

load(callback:JFrame)
run()
getIconWidth():int
paintIcon()

Figure 11.3
Un objet ImageIconProxy peut remplacer un objet ImageIcon puisquil sagit en fait
dun objet ImageIcon.

Le constructeur de ImageIconProxy reoit le nom dun fichier dimage charger. Lorsque la mthode load() dun objet ImageIconProxy est invoque, elle dfinit limage
comme tant LOADING et lance un thread spar pour charger limage. Le fait demployer un thread spar vite lapplication de devoir patienter pendant le chargement.
La mthode load() reoit un objet JFrame qui est rappel par la mthode run()
lissue du chargement. Voici le code presque complet de ImageIconProxy.java:
package com.oozinoz.imaging;
import java.awt.*;
import javax.swing.*;
public class ImageIconProxy
extends ImageIcon implements Runnable {
static final ImageIcon ABSENT = new ImageIcon(
ClassLoader.getSystemResource("images/absent.jpg"));
static final ImageIcon LOADING = new ImageIcon(
ClassLoader.getSystemResource("images/loading.jpg"));
ImageIcon current = ABSENT;
protected String filename;
protected JFrame callbackFrame;
public ImageIconProxy(String filename) {

pattern Livre Page 119 Vendredi, 9. octobre 2009 10:31 10

Chapitre 11

PROXY

119

super(ABSENT.getImage());
this.filename = filename;
}
public void load(JFrame callbackFrame) {
this.callbackFrame = callbackFrame;
current = LOADING;
callbackFrame.repaint();
new Thread(this).start();
}
public void run() {
current = new ImageIcon(
ClassLoader.getSystemResource(filename));
callbackFrame.pack();
}
public int getIconHeight() { /* Exercice ! */ }
public int getIconWidth() { /* Exercice ! */ }
public synchronized void paintIcon(
Component c, Graphics g, int x, int y) {
// Exercice !
}
}

Exercice 11.1
Un objet ImageIconProxy accepte trois appels daffichage dimage quil doit
passer limage courante. Ecrivez le code des mthodes getIconHeight(),
getIconWidth() et paintIcon() de la classe ImageIconProxy.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.


Imaginez que vous parveniez faire fonctionner le code de cette petite application
de dmonstration. Avant de crer la vritable application, laquelle ne se limite pas
un bouton Load, vous procdez une rvision de la conception, qui rvle toute sa
fragilit.
Exercice 11.2
La classe ImageIconProxy ne constitue pas un composant rutilisable bien
conu. Citez deux problmes de cette conception.

pattern Livre Page 120 Vendredi, 9. octobre 2009 10:31 10

120

Partie II

Patterns de responsabilit

Lorsque vous rvisez la conception dun dveloppeur, vous devez la fois


comprendre cette conception et vous former une opinion son sujet. Il se peut que
le dveloppeur pense avoir utilis un pattern spcifique alors que vous doutez quil
soit prsent. Dans lexemple prcdent, le pattern PROXY apparat de manire
vidente mais ne garantit en rien que la conception soit bonne. Il existe dailleurs de
bien meilleures conceptions. Lorsque ce pattern est prsent, il doit pouvoir tre
justifi car la transmission de requtes peut entraner des problmes que dautres
conceptions permettraient dviter. La prochaine section devrait vous aider dterminer si le pattern PROXY est une option valable pour votre conception.

Reconsidration des proxies dimage


A ce stade, peut-tre vous demandez-vous si les patterns de conception vous ont t
dune quelconque aide. Vous avez implment fidlement un pattern et voil que
vous cherchez maintenant vous en dbarrasser. Il sagit en fait dune tape naturelle et mme saine, qui survient plus souvent dans des conditions relles de dveloppement que dans les livres. En effet, un auteur peut, avec laide de ses relecteurs,
repenser et remplacer une conception de qualit insuffisante avant que son ouvrage
ne soit publi. Dans la pratique, un pattern peut vous aider faire fonctionner une
application et faciliter les discussions sur sa conception. Dans lexemple de
ImageIconProxy, le pattern a servi cela, mme sil est beaucoup plus simple
dobtenir leffet dsir sans implmenter littralement un proxy.
La classe ImageIcon opre sur un objet Image. Plutt que de transmettre les requtes de dessin de lcran un objet ImageIcon spar, il est plus facile doprer sur
lobjet Image envelopp dans ImageIcon. La Figure 11.4 prsente une classe
LoadingImageIcon (tire du package com.oozinoz.imaging) qui possde seulement
deux mthodes, load() et run(), en plus de ses constructeurs.
Figure 11.4
La classe LoadingImageIcon fonctionne en changeant
lobjet Image quelle
contient.

ImageIcon
image:Image

LoadingImageIcon
ABSENT:ImageIcon
LOADING:ImageIcon

getImage():Image
setImage(i:Image)

LoadingImageIcon(
filename:String)
load(callback:JFrame)
run()

pattern Livre Page 121 Vendredi, 9. octobre 2009 10:31 10

Chapitre 11

PROXY

121

La mthode load() de cette classe rvise reoit toujours un objet JFrame rappeler aprs le chargement de limage souhaite. Lorsquelle sexcute, elle invoque
setImage() avec limage de LOADING, redessine le cadre (frame) et lance un thread
spar pour elle-mme. La mthode run(), qui sexcute dans un thread spar,
cre un nouvel objet ImageIcon pour le fichier nomm dans le constructeur, appelle
setImage() avec limage de cet objet et redessine le cadre.
Voici le code presque complet de LoadingImageIcon.java:
package com.oozinoz.imaging;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
public class LoadingImageIcon
extends ImageIcon implements Runnable {
static final ImageIcon ABSENT = new ImageIcon(
ClassLoader.getSystemResource("images/absent.jpg"));
static final ImageIcon LOADING = new ImageIcon(
ClassLoader.getSystemResource("images/loading.jpg"));
protected String filename;
protected JFrame callbackFrame;
public LoadingImageIcon(String filename) {
super(ABSENT.getImage());
this.filename = filename;
}
public void load(JFrame callbackFrame) {
// Exercice !
}
public void run() {
// Exercice !
}
}

Exercice 11.3
Ecrivez le code des mthodes load() et run() de LoadingImageIcon.

Ce code rvis est moins li la conception de ImageIcon, sappuyant principalement sur getImage() et setImage() et non sur la transmission dappels. En fait,
il ny a pas du tout de transmission. LoadingImageIcon a seulement lapparence
dun proxy, et non la structure.

pattern Livre Page 122 Vendredi, 9. octobre 2009 10:31 10

122

Partie II

Patterns de responsabilit

Le fait que le pattern PROXY ait recours la transmission peut accrotre la maintenance du code. Par exemple, si lobjet sous-jacent change, lquipe dOozinoz
devra actualiser le proxy. Pour viter cela, vous devriez lorsque vous le pouvez
renoncer ce pattern. Il existe cependant des situations o vous navez dautre
choix que de lutiliser. En particulier, lorsque lobjet pour lequel vous devez intercepter des messages sexcute sur une autre machine, ce pattern est parfois la seule
option envisageable.

Proxy distant
Lorsque vous voulez invoquer une mthode dun objet qui sexcute sur un autre
ordinateur, vous ne pouvez le faire directement et devez donc trouver un autre
moyen de communiquer avec lui. Vous pourriez ouvrir un socket sur lhte distant
et laborer un protocole pour envoyer des messages lobjet. Idalement, une telle
approche vous permettrait de lui passer des messages de la mme manire que sil
tait local. Vous devriez pouvoir appeler les mthodes dun objet proxy qui transmettrait ces requtes lobjet distant. En fait, de telles conceptions ont dj t
implmentes, notamment dans CORBA (Common Object Request Broker Architecture), dans ASP.NET (Active Server Pages for .NET), et dans Java RMI (Remote
Method Invocation).
Grce RMI, un client peut assez aisment obtenir un objet proxy qui transmette
les appels vers lobjet dsir actif sur une autre machine. Il importe de connatre
RMI puisque ce mcanisme fait partie des fondements de la spcification EJB
(Enterprise JavaBeans), un standard important de lindustrie. Indpendamment
de la faon dont les standards de lindustrie voluent, le pattern PROXY continuera
de jouer un rle important dans les environnements distribus, du moins dans un
avenir proche, et RMI reprsente un bon exemple dimplmentation de ce
pattern.
Pour vous familiariser avec RMI, vous aurez besoin dun ouvrage de rfrence sur
le sujet, tel que JavaTM Enterprise in a Nutshel (Java en concentr : Manuel de
rfrence pour Java) [Flanagan et al. 2002]. Lexemple prsent dans cette section
nest pas un tutoriel sur RMI mais permet de mettre en vidence la prsence et
limportance du pattern PROXY dans les applications RMI. Nous laisserons de ct
les difficults de conception introduites par RMI et EJB.
Supposez que vous ayez dcid dexplorer le fonctionnement de RMI en rendant les
mthodes dun objet accessibles un programme Java qui sexcute sur un autre

pattern Livre Page 123 Vendredi, 9. octobre 2009 10:31 10

Chapitre 11

PROXY

123

ordinateur. La premire tape de dveloppement consiste crer une interface pour


la classe qui doit tre accessible distance. Vous commencez par crer une interface
Rocket qui est indpendante du code existant Oozinoz :
package com.oozinoz.remote;
import java.rmi.*;
public interface Rocket extends Remote {
void boost(double factor) throws RemoteException;
double getApogee() throws RemoteException;
double getPrice() throws RemoteException;
}

Linterface Rocket tend Remote et ses mthodes dclarent toutes quelles gnrent
(throw) des exceptions distantes (RemoteException). Expliquer cet aspect de
linterface dpasse le cadre du prsent livre, mais nimporte quel ouvrage didacticiel sur RMI devrait le faire. Votre rfrence RMI devrait galement expliquer que,
pour agir en tant que serveur, limplmentation de votre interface distante peut tendre
UnicastRemoteObject, comme illustr Figure 11.5.
Figure 11.5
Pour utiliser RMI,
vous pouvez dabord
dfinir linterface
souhaite pour les
messages changs
entre les deux ordinateurs puis crer
une sous-classe
de UnicastRemoteObject qui
implmente cette
interface.

interface
Rocket
boost(factor:double)
getApogee():double
getPrice():double

UnicastRemoteObject

RocketImpl
apogee:double
price:double
RocketImpl(price:double,apogee:double)
boost(factor:double)
getApogee():double
getPrice():double

pattern Livre Page 124 Vendredi, 9. octobre 2009 10:31 10

124

Partie II

Patterns de responsabilit

Vous avez prvu que des objets RocketImpl soient actifs sur un serveur et accessibles
via un proxy qui lui est actif sur un client. Le code de la classe RocketImpl est simple :
package com.oozinoz.remote;
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
public class RocketImpl
extends UnicastRemoteObject
implements Rocket {
protected double price;
protected double apogee;
public RocketImpl(double price, double apogee)
throws RemoteException {
this.price = price;
this.apogee = apogee;
}
public void boost(double factor) {
apogee *= factor;
}
public double getApogee() {
return apogee;
}
public double getPrice() {
return price;
}
}

Une instance de RocketImpl peut tre active sur une machine et accessible un
programme Java excut sur une autre machine. Pour que cela puisse fonctionner,
un client a besoin dun proxy pour lobjet RocketImpl. Ce proxy doit implmenter
linterface Rocket et possder les fonctionnalits additionnelles requises pour
communiquer avec un objet distant. Un gros avantage de RMI est quil automatise
la construction de ce proxy. Pour gnrer le proxy, placez le fichier RocketImpl.java et le fichier dinterface Rocket.java sous le rpertoire dans lequel vous
excuterez le registre RMI :
c:\rmi>dir /b com\oozinoz\remote
RegisterRocket.class
RegisterRocket.java
Rocket.class

pattern Livre Page 125 Vendredi, 9. octobre 2009 10:31 10

Chapitre 11

PROXY

125

Rocket.java
RocketImpl.class
RocketImpl.java
ShowRocketClient.class
ShowRocketClient.java

Pour crer la classe stub RocketImpl qui facilite la communication distante, excutez
le compilateur RMI livr avec le JDK :
c:\rmi> rmic com.oozinoz.remote.RocketImpl

Notez que lexcutable rmic reoit comme argument un nom de classe, et non un
nom de fichier. Depuis la version 1.2 du JDK, le compilateur RMI cre un seul
fichier stub dont les machines client et serveur ont toutes deux besoin. Les versions
antrieures craient des fichiers spars pour une utilisation sur le client et le
serveur. La commande rmic cre une classe RocketImpl_Stub:
c:\rmi>dir /b com\oozinoz\remote
RegisterRocket.class
RegisterRocket.java
Rocket.class
Rocket.java
RocketImpl.class
RocketImpl.java
RocketImpl_Stub.class
ShowRocketClient.class
ShowRocketClient.java

Pour rendre un objet actif, il faut lenregistrer auprs dun registre RMI qui
sexcute sur le serveur. Lexcutable rmiregistry est intgr au JDK. Lorsque
vous excutez le registre, spcifiez le port sur lequel il coutera :
c:\rmi> rmiregistry 5000

Une fois le registre en cours dexcution sur le serveur, vous pouvez crer et enregistrer un objet RocketImpl:
package com.oozinoz.remote;
import java.rmi.*;
public class RegisterRocket {
public static void main(String[] args) {
try {
// Exercice !
Naming.rebind(
"rmi://localhost:5000/Biggie", biggie);
System.out.println("biggie enregistr");

pattern Livre Page 126 Vendredi, 9. octobre 2009 10:31 10

126

Partie II

Patterns de responsabilit

} catch (Exception e) {
e.printStackTrace();
}
}
}

Si vous compilez et excutez ce code, le programme affichera une confirmation de


lenregistrement de la fuse :
biggie enregistr

Vous devez remplacer la ligne // Exercice ! de la classe RegisterRocket par le


code qui cre un objet biggie modlisant une fuse. Le reste du code de la mthode
main() enregistre cet objet. Une description du fonctionnement de la classe Naming
dpasse le cadre de cette discussion. Vous devriez nanmoins disposer des informations
suffisantes pour pouvoir crer lobjet biggie que ce code enregistre.
Exercice 11.4
Remplacez la ligne // Exercice ! par une dclaration et une instanciation de
lobjet biggie. Dfinissez cet objet de sorte quil modlise une fuse dont le prix
est de 29,95 dollars et lapoge de 820 mtres.

Le fait dexcuter le programme RegisterRocket rend un objet RocketImpl, en


loccurrence biggie, disponible sur un serveur. Un client qui sexcute sur une
autre machine peut accder biggie sil dispose dun accs linterface Rocket et
la classe RocketImpl_Stub. Si vous travaillez sur une seule machine, vous
pouvez quand mme raliser ce test en accdant au serveur sur localhost plutt
que sur un autre hte :
package com.oozinoz.remote;
import java.rmi.*;
public class ShowRocketClient {
public static void main(String[] args) {
try {
Object obj = Naming.lookup(
"rmi://localhost:5000/Biggie");
Rocket biggie = (Rocket) obj;
System.out.println(
"Lapoge est " + biggie.getApogee());

pattern Livre Page 127 Vendredi, 9. octobre 2009 10:31 10

Chapitre 11

PROXY

127

} catch (Exception e) {
System.out.println(
"Exception lors de la recherche dune fuse :");
e.printStackTrace();
}
}
}

Lorsque ce programme sexcute, il recherche un objet enregistr sous le nom de


"Biggie". La classe qui fournit ce nom est RocketImpl et lobjet obj retourn par
lookup() sera une instance de la classe RocketImpl_Stub. Cette dernire implmente linterface Rocket, aussi est-il lgal de convertir (cast) lobjet obj en une
instance de Rocket. La classe RocketImpl_Stub tend en fait une classe RemoteStub qui permet lobjet de communiquer avec un serveur.
Lorsque vous excutez le programme ShowRocketClient, il affiche lapoge dune
fuse "Biggie" :
Lapoge est de 820.0

Par lintermdiaire dun proxy, lappel de getApogee() est transmis une implmentation de linterface Rocket qui est active sur un serveur.
Exercice 11.5
La Figure 11.6 illustre lappel de getApogee() qui est transmis. Lobjet le plus
droite apparat en gras pour signifier quil est actif en dehors du programme
ShowRocketClient. Compltez les noms de classes manquants.
Figure 11.6
Ce diagramme, une
fois complt, reprsentera le flux de
messages dans une
application distribue base sur RMI.

:?

ShowRocketClient

:?

getApogee()
getApogee()

pattern Livre Page 128 Vendredi, 9. octobre 2009 10:31 10

128

Partie II

Patterns de responsabilit

Lintrt de RMI est quil permet des programmes client dinteragir avec un objet
local servant de proxy pour un objet distant. Vous dfinissez linterface de lobjet que
sont censs se partager le client et le serveur. RMI fournit, lui, le mcanisme de
communication et dissimule au serveur et au client le fait que deux implmentations
de Rocket collaborent pour assurer une communication interprocessus quasiment
transparente.

Proxy dynamique
Les ingnieurs dOozinoz sont parfois confronts des problmes de performances
et aimeraient trouver un moyen dinstrumentaliser le code sans avoir apporter de
changements majeurs leur conception.
Java offre une fonctionnalit qui peut les aider dans ce sens : le proxy dynamique.
Un tel proxy permet denvelopper un objet dans un autre. Vous pouvez faire en sorte
que lobjet extrieur le proxy intercepte tous les appels destins lobjet
envelopp. Le proxy passe habituellement ces appels lobjet intrieur, mais vous
pouvez ajouter du code qui sexcute avant ou aprs les appels intercepts. Certaines limitations de cette fonctionnalit vous empchent denvelopper nimporte quel
objet. En revanche, dans des conditions adquates, vous disposez dun contrle
total sur lopration accomplie par lobjet envelopp.
Un proxy dynamique utilise les interfaces implmentes par la classe dun objet.
Les appels pouvant tre intercepts par le proxy sont dfinis dans lune de ces interfaces. Si vous disposez dune classe qui implmente une interface dont vous voulez
intercepter certaines mthodes, vous pouvez utiliser un proxy dynamique pour
envelopper une instance de cette classe.
Pour crer un proxy dynamique, vous devez avoir la liste des interfaces intercepter.
Heureusement, cette liste peut gnralement tre obtenue en interrogeant lobjet
que vous voulez envelopper au moyen dune ligne de code comme la suivante :
Class[] classes = obj.getClass().getInterfaces();

Ce code indique que les mthodes intercepter appartiennent aux interfaces implmentes par la classe dun objet. La cration dun proxy dynamique ncessite deux
autres ingrdients : un chargeur de classe (loader) et une classe contenant le
comportement que vous voulez excuter lorsque votre proxy intercepte un appel.

pattern Livre Page 129 Vendredi, 9. octobre 2009 10:31 10

Chapitre 11

PROXY

129

Comme pour la liste dinterfaces, vous pouvez obtenir un chargeur de classe appropri
en utilisant celui associ lobjet envelopper :
ClassLoader loader = obj.getClass().getClassLoader();

Le dernier ingrdient requis est lobjet proxy lui-mme. Cet objet doit tre une
instance dune classe qui implmente linterface InvocationHandler du package
java.lang.reflect. Cette interface dclare lopration suivante :
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable;

Lorsque vous enveloppez un objet dans un proxy dynamique, les appels destins
cet objet sont drouts vers cette opration invoke(), dans une classe que vous
fournissez. Le code de votre mthode invoke() devra probablement passer
lobjet envelopp chaque appel de mthode. Vous pouvez passer linvocation avec
une ligne comme celle-ci :
result = m.invoke(obj, args);

Cette ligne utilise le principe de rflexion pour passer lappel dsir lobjet envelopp. Le grand intrt des proxies dynamiques est que vous pouvez ajouter
nimporte quel comportement avant ou aprs lexcution de cette ligne.
Imaginez que vous vouliez consigner un avertissement lorsquune mthode est
longue sexcuter. Vous pourriez crer une classe ImpatientProxy avec le code
suivant :
package app.proxy.dynamic;
import java.lang.reflect.*;
public class ImpatientProxy implements InvocationHandler {
private Object obj;
private ImpatientProxy(Object obj) {
this.obj = obj;
}
public Object invoke(
Object proxy, Method m, Object[] args)
throws Throwable {
Object result;
long t1 = System.currentTimeMillis();
result = m.invoke(obj, args);

pattern Livre Page 130 Vendredi, 9. octobre 2009 10:31 10

130

Partie II

Patterns de responsabilit

long t2 = System.currentTimeMillis();
if (t2 - t1 > 10) {
System.out.println(
"> Il faut " + (t2 - t1)
+ " millisecondes pour invoquer " + m.getName()
+ "() avec");
for (int i = 0; i < args.length; i++)
System.out.println(
"> arg[" + i + "]: " + args[i]);
}
return result;
}
}

Cette classe implmente la mthode invoke() de manire quelle vrifie le temps


que prend lobjet envelopp pour accomplir lopration invoque. Si la dure
dexcution est trop longue, la classe ImpatientProxy affiche un avertissement.
Pour pouvoir utiliser un objet ImpatientProxy, vous devez employer la classe
Proxy du package java.lang.reflect. Cette classe a besoin dune liste dinterfaces et dun chargeur de classe, ainsi que dune instance de ImpatientProxy. Pour
simplifier la cration du proxy dynamique, nous pourrions ajouter la mthode
suivante la classe ImpatientProxy:
public static Object newInstance(Object obj) {
ClassLoader loader = obj.getClass().getClassLoader();
Class[] classes = obj.getClass().getInterfaces();
return Proxy.newProxyInstance(
loader, classes, new ImpatientProxy(obj));
}

La mthode statique newInstance() cre un proxy dynamique pour nous. A partir


dun objet envelopper, elle extrait la liste des interfaces et le chargeur de classe de
cet objet. Puis elle instancie la classe ImpatientProxy en lui passant lobjet envelopper. Tous ces ingrdients sont ensuite passs la mthode newProxyInstance()
de la classe Proxy.
Lobjet retourn implmente toutes les interfaces implmentes par la classe
de lobjet envelopp. Nous pouvons convertir lobjet retourn en nimporte laquelle de
ces interfaces.
Imaginez que vous travailliez avec un ensemble (set) dobjets et que certaines
oprations semblent sexcuter lentement. Pour dterminer quels objets sont

pattern Livre Page 131 Vendredi, 9. octobre 2009 10:31 10

Chapitre 11

PROXY

131

concerns, vous pouvez envelopper lensemble dans un objet ImpatientProxy,


comme illustr ci-aprs :
package app.proxy.dynamic;
import
import
import
import
import

java.util.HashSet;
java.util.Set;
com.oozinoz.firework.Firecracker;
com.oozinoz.firework.Sparkler;
com.oozinoz.utility.Dollars;

public class ShowDynamicProxy {


public static void main(String[] args) {
Set s = new HashSet();
s = (Set)ImpatientProxy.newInstance(s);
s.add(new Sparkler(
"Mr. Twinkle", new Dollars(0.05)));
s.add(new BadApple("Lemon"));
s.add(new Firecracker(
"Mr. Boomy", new Dollars(0.25)));
System.out.println(
"Lensemble contient " + s.size() + " lments.");
}
}

Ce code cre un objet Set pour contenir quelques lments. Puis il enveloppe cet
ensemble dans un objet ImpatientProxy, convertissant le rsultat de la mthode
newInstance() en un objet Set. La consquence est que lobjet s se comporte
comme un ensemble, sauf que le code de ImpatientProxy produira un avertissement si une des mthodes est trop longue sexcuter. Par exemple, lorsque le
programme invoque la mthode add() de lensemble, notre objet ImpatientProxy
intercepte lappel et le passe lensemble en minutant le rsultat de chaque appel.
Lexcution du programme ShowDynamicProxy produit le rsultat suivant :
> Il faut 1204 millisecondes pour invoquer add() avec
> arg[0]: Lemon
Lensemble contient 3 lments.

Le code de ImpatientProxy nous aide identifier lobjet qui est long ajouter
lensemble. Il sagit de linstance Lemon de la classe BadApple. Voici le code de
cette classe :
package app.proxy.dynamic;
public class BadApple {
public String name;

pattern Livre Page 132 Vendredi, 9. octobre 2009 10:31 10

132

Partie II

Patterns de responsabilit

public BadApple(String name) {


this.name = name;
}
public boolean equals(Object o) {
if (!(o instanceof BadApple))
return false;
BadApple f = (BadApple) o;
return name.equals(f.name);
}
public int hashCode() {
try {
Thread.sleep(1200);
} catch (InterruptedException ignored) {
}
return name.hashCode();
}
public String toString() {
return name;
}
}

Le code de ShowDynamicProxy utilise un objet ImpatientProxy pour surveiller les


appels destins un ensemble. Il nexiste toutefois aucun lien entre un ensemble
donn et ImpatientProxy. Aprs avoir crit une classe de proxy dynamique, vous
pouvez lutiliser pour envelopper nimporte quel objet ds lors que celui-ci est une
instance dune classe qui implmente une interface dclarant le comportement que
vous voulez intercepter.
La possibilit de crer un comportement pouvant tre excut avant ou aprs les
appels intercepts est lune des ides de base de la programmation oriente aspect
ou POA (AOP, Aspect-Oriented Programming). Un aspect combine les notions
dadvice le code que vous voulez insrer et de point-cuts la dfinition de
points dexcution o vous voulez que le code insr soit excut. Des livres entiers
sont consacrs la POA, mais vous pouvez avoir un avant-got de lapplication
de comportements rutilisables une varit dobjets en utilisant des proxies dynamiques.
Un proxy dynamique vous permet denvelopper un objet dans un proxy qui intercepte les appels destins cet objet et qui ajoute un comportement avant ou aprs le
passage de ces appels lobjet envelopp. Vous pouvez ainsi crer des comportements rutilisables applicables nimporte quel objet, comme en programmation
oriente aspect.

pattern Livre Page 133 Vendredi, 9. octobre 2009 10:31 10

Chapitre 11

PROXY

133

Rsum
Les implmentations du pattern PROXY produisent un objet intermdiaire qui gre
laccs un objet cible. Un objet proxy peut dissimuler aux clients les changements
dtat dun objet cible, comme dans le cas dune image qui ncessite un certain
temps pour se charger. Le problme est que ce pattern sappuie habituellement sur
un couplage troit entre lintermdiaire et lobjet cible. Dans certains cas, la solution consiste utiliser un proxy dynamique. Lorsque la classe dun objet implmente des interfaces pour les mthodes que vous voulez intercepter, vous pouvez
envelopper lobjet dans un proxy dynamique et faire en sorte que votre code
sexcute avant/aprs le code de lobjet envelopp ou sa place.

pattern Livre Page 134 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 135 Vendredi, 9. octobre 2009 10:31 10

12
CHAIN OF RESPONSABILITY
Les dveloppeurs sefforcent dassocier les objets de manire souple avec une
responsabilit minimale et spcifique entre objets. Cette pratique permet de procder plus facilement des changements et avec moins de risques dintroduire des
dfauts. Dans une certaine mesure, la dissociation se produit naturellement en Java.
Les clients ne voient que linterface visible dun objet et sont affranchis des dtails
de son implmentation. Cette organisation laisse toutefois en place lassociation
fondamentale pour que le client sache quel objet possde la mthode quil doit
appeler. Vous pouvez assouplir la restriction forant un client savoir quel objet
utiliser lorsque vous pouvez organiser un groupe dobjets sous forme dune sorte de
hirarchie qui permet chaque objet soit de raliser une opration, soit de passer la
requte un autre objet.
Lobjectif du pattern CHAIN OF RESPONSABILITY est dviter de coupler lmetteur dune requte son rcepteur en permettant plus dun objet dy rpondre.

Une chane de responsabilits ordinaire


Le pattern CHAIN OF RESPONSABILITY apparat souvent dans notre quotidien rel
lorsquune personne responsable dune tche sen acquitte personnellement ou la
dlgue quelquun dautre. Cette situation se produit chez Oozinoz avec des
ingnieurs responsables de la maintenance des machines de fabrication de
fuses.
Comme dcrit au Chapitre 5, Oozinoz modlise des machines, des lignes de montage,
des traves, et des units de production (ou usine) en tant que composants matriels
de fabrication (objet MachineComponent). Cette approche permet limplmentation

pattern Livre Page 136 Vendredi, 9. octobre 2009 10:31 10

136

Partie II

Patterns de responsabilit

simple et rcursive doprations telles que larrt de toutes les machines dune
trave. Elle simplifie aussi la modlisation des responsabilits de fabrication au sein
de lusine. Chez Oozinoz, il y a toujours un ingnieur responsable pour nimporte
quel composant matriel, bien que cette responsabilit puisse tre assigne diffrents
niveaux.
Par exemple, il peut y avoir un ingnieur directement assign la maintenance
dune machine complexe mais pas forcment dans le cas dune machine simple.
Dans ce dernier cas, cest lingnieur responsable de la ligne ou de la trave
laquelle participe la machine qui en assumera la responsabilit.
Nous voudrions ne pas forcer les objets clients interroger plusieurs objets
lorsquils recherchent lingnieur responsable. Nous pouvons ici appliquer le
pattern CHAIN OF RESPONSABILITY, en associant chaque composant matriel un
objet responsible. La Figure 12.1 illustre cette conception.
Figure 12.1
MachineComponent

Chaque objet

Machine MachineComposite possde


un parent et une
association de
responsabilit,
hrits de la classe

MachineComponent.

parent:MachineComponent
responsible:Engineer
getParent():MachineComponent
getResponsible():Engineer

Machine

MachineComposite

La conception illustre Figure 12.1 permet, sans quon ait le requrir, que chaque
composant matriel garde trace de son ingnieur responsable. Si une machine na
pas dingnieur ddi, elle peut passer une requte demandant son ingnieur
responsable dtre son "parent". Dans la pratique, le parent dune machine est une
ligne, celui dune ligne est une trave, et celui dune trave est une unit de production. Chez Oozinoz, il y a toujours un ingnieur responsable quelque part dans cette
chane.

pattern Livre Page 137 Vendredi, 9. octobre 2009 10:31 10

Chapitre 12

CHAIN OF RESPONSABILITY

137

Lavantage de cette conception est que les clients de composants matriels nont pas
besoin de dterminer comment les ingnieurs sont attribus. Un client peut demander nimporte quel composant son ingnieur responsable. Les composants vitent
aux clients davoir connatre la faon dont les responsabilits sont distribues.
Dun autre ct, cette conception prsente quelques ventuels inconvnients.
Exercice 12.1
Citez deux faiblesses de la conception illustre Figure 12.1.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.

Le pattern CHAIN OF RESPONSABILITY permet de simplifier le code du client


lorsquil nest pas vident de savoir quel objet dun groupe dobjets doit traiter une
requte. Si ce pattern ntait pas dj implment, vous pourriez remarquer certaines situations o il pourrait vous aider migrer votre code vers une conception plus
simple.

Refactorisation pour appliquer CHAIN OF RESPONSABILITY


Si vous remarquez quun code client effectue des appels de test avant dmettre la
requte effective, vous pourriez lamliorer au moyen dune refactorisation. Pour
appliquer le pattern CHAIN OF RESPONSABILITY, dterminez lopration que les
objets dun groupe de classes seront parfois en mesure de supporter. Par exemple, les composants matriels chez Oozinoz peuvent parfois fournir une rfrence dingnieur responsable. Ajoutez lopration souhaite chaque classe
dans le groupe, mais implmentez lopration au moyen dune stratgie de chanage pour les cas o un objet spcifique ncessiterait de laide pour rpondre la
requte.
Considrez le code Oozinoz de modlisation doutils (Tool) et de chariots doutils
(Tool Cart). Les outils ne font pas partie de la hirarchie MachineComponent mais
ils partagent quelques similitudes avec ces composants. Plus prcisment, les outils
sont toujours assigns aux chariots doutils, et ces derniers ont un ingnieur responsable. Imaginez un affichage pouvant montrer tous les outils et les machines dune
certaine trave et disposant dune aide affichant lingnieur responsable pour

pattern Livre Page 138 Vendredi, 9. octobre 2009 10:31 10

138

Partie II

Patterns de responsabilit

nimporte quel lment choisi. La Figure 12.2 illustre les classes impliques dans
lidentification de lingnieur responsable dun quipement slectionn.

interface
VisualizationItem

...

Tool

ToolCart

MachineComponent

responsible:Engineer
getParent():MachineComponent
getResponsible():Engineer

Machine

getResponsible():Engineer

MachineComposite

Figure 12.2
Les lments dune simulation comprennent des machines, des machines composites,
des outils et des chariots doutils.

Linterface VisualizationItem spcifie quelques comportements que les classes


requirent pour participer laffichage, mais ne possde pas de mthode getResponsible(). En fait, tous les lments de la visualisation nont pas une connaissance directe de leur responsable. Lorsque la visualisation doit dterminer
lingnieur responsable dun lment, la rponse dpend du type de llment slectionn. Les machines, les groupes de machines et les chariots doutils disposent
dune mthode getResponsible(), mais pas les outils. Pour ceux-ci, le code doit

pattern Livre Page 139 Vendredi, 9. octobre 2009 10:31 10

Chapitre 12

CHAIN OF RESPONSABILITY

139

identifier le chariot auquel appartient loutil et dterminer le responsable du chariot.


Pour trouver lingnieur responsable dun lment simul, un code de menu
dapplication utilise une srie dinstructions if et de tests du type dlment. Cela
est le signe quune refactorisation pourrait amliorer le code qui se prsente comme
suit :
package com.oozinoz.machine;
public class AmbitiousMenu {
public Engineer getResponsible(VisualizationItem item) {
if (item instanceof Tool) {
Tool t = (Tool) item;
return t.getToolCart().getResponsible();
}
if (item instanceof ToolCart) {
ToolCart tc = (ToolCart) item;
return tc.getResponsible();
}
if (item instanceof MachineComponent) {
MachineComponent c = (MachineComponent) item;
if (c.getResponsible() != null)
return c.getResponsible();
if (c.getParent() != null)
return c.getParent().getResponsible();
}
return null;
}
}

Lobjectif de CHAIN OF RESPONSABILITY est dexonrer le code appelant de lobligation de savoir quel objet peut traiter une requte. Dans cet exemple, lappelant est
un menu et la requte concerne lidentification dun ingnieur responsable. Dans la
conception actuelle, lappelant doit connatre les lments qui possdent une
mthode getResponsible(). Vous pouvez perfectionner ce code en appliquant
CHAIN OF RESPONSABILITY, en donnant tous les lments simuls un tiers
responsable. Ainsi, ce sont les objets simuls qui ont pour charge de connatre leur
responsable et non plus le menu.
Exercice 12.2
Redessinez le diagramme de la Figure 12.2 en dplaant la mthode getResponsible() vers VisualizationItem et en ajoutant ce comportement Tool.

pattern Livre Page 140 Vendredi, 9. octobre 2009 10:31 10

140

Partie II

Patterns de responsabilit

Le code de menu devient plus simple maintenant quil peut demander chaque
lment pouvant tre slectionn son ingnieur responsable :
package com.oozinoz.machine;
public class AmbitiousMenu2 {
public Engineer getResponsible(VisualizationItem item) {
return item.getResponsible();
}
}

Limplmentation de la mthode getResponsible() pour chaque lment est


galement simple.
Exercice 12.3
Ecrivez le code de la mthode getResponsible() pour :
A. MachineComponent
B. Tool
C. ToolCart

Ancrage dune chane de responsabilits


Lorsque vous crivez la mthode getResponsible() pour MachineComponent,
vous devez considrer le fait que le parent dun objet MachineComponent puisse
tre null. Une autre solution est dtre un peu plus strict dans votre modle objet et
dexiger que les objets MachineComponent aient un parent non null. Pour cela, vous
pouvez ajouter un argument parent au constructeur de MachineComponent.
Vous pouvez mme mettre une exception lorsque lobjet fourni est null, tant que vous
savez o cette exception est intercepte. Considrez aussi le fait quun objet
formera la racine (root) un objet particulier qui naura pas de parent. Une approche raisonnable est de crer une classe MachineRoot en tant que sous-classe de
MachineComposite (pas de MachineComponent). Vous pouvez alors garantir quun
objet MachineComponent aura toujours un ingnieur responsable si :
m
m

Le constructeur (ou les constructeurs) de MachineRoot requiert un objet Engineer.


Le constructeur (ou les constructeurs) de MachineComponent requiert un objet
parent qui soit lui-mme un MachineComponent.

pattern Livre Page 141 Vendredi, 9. octobre 2009 10:31 10

Chapitre 12

CHAIN OF RESPONSABILITY

141

Seul MachineRoot utilise null comme valeur pour son parent.

Figure 12.3
Comment les constructeurs
peuvent-ils garantir que
chaque objet MachineComponent aura un ingnieur responsable ?

MachineComponent
parent:MachineComponent
responsible:Engineer

Machine

MachineComposite

MachineRoot

Exercice 12.4
Compltez les constructeurs de la Figure 12.3 pour supporter une conception
garantissant que chaque objet MachineComponent aura un ingnieur responsable.

En ancrant une chane de responsabilits, vous renforcez le modle objet et simplifiez le code. Vous pouvez maintenant implmenter la mthode getResponsible()
de MachineComponent comme suit :
public Engineer getResponsible() {
if (responsible != null)
return responsible;
return parent.getResponsible();
}

pattern Livre Page 142 Vendredi, 9. octobre 2009 10:31 10

142

Partie II

Patterns de responsabilit

CHAIN OF RESPONSABILITY sans COMPOSITE


Le pattern CHAIN OF RESPONSABILITY requiert une stratgie pour ordonner la
recherche dun objet pouvant traiter une requte. Gnralement, lordre suivre
dpendra dun aspect sous-jacent du domaine modlis. Cela se produit souvent
lorsquil y a une sorte de composition, comme dans la hirarchie de composants
matriels dOozinoz. Ce pattern peut toutefois sappliquer dautres modles que
les modles composites.
Exercice 12.5
Donnez un exemple dans lequel le pattern CHAIN OF RESPONSABILITY peut
intervenir alors que les objets chans ne forment pas un composite.

Rsum
Lorsque vous appliquez le pattern CHAIN OF RESPONSABILITY, vous dispensez un
client de devoir savoir quel objet dun ensemble supporte un certain comportement.
En permettant laction de recherche de responsabilit de se produire le long de la
chane dobjets, vous dissociez le client de tout objet spcifique de la chane.
Ce pattern intervient occasionnellement lorsquune chane dobjets arbitraire peut
appliquer une srie de stratgies diverses pour rpondre un certain problme, tel
que lanalyse dune entre utilisateur. Plus frquemment, il intervient dans le cas
dagrgats, o une hirarchie disolement fournit un ordre naturel pour une chane
dobjets. Ce pattern rsulte en un code plus simple au niveau la fois de la hirarchie
et du client

pattern Livre Page 143 Vendredi, 9. octobre 2009 10:31 10

13
FLYWEIGHT
Le pattern FLYWEIGHT permet le partage dun objet entre plusieurs clients, crant
une responsabilit pour lobjet partag dont les objets ordinaires nont normalement
pas se soucier. La plupart du temps, un seul client la fois dtient une rfrence
vers un objet. Lorsque ltat de lobjet change, cest parce que le client la modifi
et lobjet na pas la responsabilit den informer les autres clients. Il est cependant
parfois utile de pouvoir partager laccs un objet.
Une raison de vouloir cela apparat lorsque vous devez grer des milliers ou des
dizaines de milliers de petits objets, tels que les caractres dune version en ligne
dun livre. Dans un tel cas, ce sera pour amliorer les performances afin de pouvoir
partager efficacement des objets dune grande granularit entre de nombreux
clients. Un livre na besoin que dun objet A, bien quil ncessite un moyen de
modliser les endroits o diffrents A apparaissent.
Lobjectif du pattern FLYWEIGHT est dutiliser le partage pour supporter efficacement un grand nombre dobjets forte granularit.

Immuabilit
Le pattern FLYWEIGHT laisse plusieurs clients se partager un grand nombre de petits
objets : les flyweights (poids mouche). Pour que cela fonctionne, vous devez considrer que lorsquun client change ltat dun objet, cet tat est modifi pour chaque
client ayant accs lobjet. La faon la plus simple et la plus courante dviter
quils se perturbent mutuellement est de les empcher dintroduire des changements dtat dans lobjet partag. Un moyen dy parvenir est de crer un objet qui
soit immuable pour que, une fois cr, il ne puisse tre chang. Les objets immuables

pattern Livre Page 144 Vendredi, 9. octobre 2009 10:31 10

144

Partie II

Patterns de responsabilit

les plus frquemment rencontrs dans Java sont des instances de la classe String.
Une fois que vous avez cr une chane, ni vous ni aucun client pouvant y accder
ne pourra changer ses caractres.
Exercice 13.1
Donnez une justification du choix des crateurs de Java davoir rendu les objets
String immuables, ou argumentez contre cette dcision si vous la jugez draisonnable.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.

Lorsque vous avez un grand nombre dobjets similaires, vous pouvez vouloir en
partager laccs, mais ils ne sont pas ncessairement immuables. Dans ce cas, une
tape pralable lapplication de FLYWEIGHT est dextraire la partie immuable dun
objet pour quelle puisse tre partage.

Extraction de la partie immuable dun flyweight


Chez Oozinoz, les substances chimiques sont aussi rpandues que des caractres
dans un document. Les services achat, ingnierie, fabrication et scurit sont tous
impliqus dans la gestion de la circulation de milliers de substances chimiques dans
lusine. Les prparations chimiques sont souvent modlises au moyen dinstances
de la classe Substance illustre Figure 13.1.
La classe Substance possde de meilleures mthodes pour ses attributs ainsi
quune mthode getMoles() qui retourne le nombre de moles un compte de
molcules dans une substance. Un objet Substance reprsente une certaine
quantit dune certaine molcule. Oozinoz utilise une classe Mixture pour modliser des combinaisons de substances. Par exemple, la Figure 13.2 prsente un
diagramme dune prparation de poudre noire.
Supposez que, tant donn la prolifration de substances chimiques chez Oozinoz,
vous dcidiez dappliquer le pattern FLYWEIGHT pour rduire le nombre dobjets
Substance dans les applications. Pour traiter les objets Substance en tant que
flyweights, une premire tape est de sparer les parties immuables des parties
variables. Supposez que vous dcidiez de restructurer la classe Substance en
extrayant sa partie immuable pour la placer dans une classe Chemical.

pattern Livre Page 145 Vendredi, 9. octobre 2009 10:31 10

Chapitre 13

FLYWEIGHT

145

Figure 13.1
Substance

Un objet Substance
modlise une prparation chimique.

name:String
symbol:String
atomicWeight:double
grams:double
getName():String
getSymbol():String
getAtomicWeight():double
getGrams():double
getMoles():double

Figure 13.2
Une prparation
de poudre noire
contient du salptre,
du soufre et du
charbon.

blackPowder:Mixture
:Substance
name = " Sulfur "
:Substance

symbol = S

name = " Saltpeter "

grams = 10

atomicWeight = 32
symbol = " KNO3 "
atomicWeight = 101
grams = 75

:Substance
name = " Carbon "
symbol = " C "
atomicWeight = 12
grams = 15

Exercice 13.2
Compltez le diagramme de classes de la Figure 13.3 pour prsenter une classe
Substance2 restructure et une nouvelle classe Chemical immuable.

pattern Livre Page 146 Vendredi, 9. octobre 2009 10:31 10

146

Partie II

Patterns de responsabilit

Figure 13.3
Compltez ce
diagramme pour
extraire les caractristiques immuables
de Substance2 et les
placer dans la classe
Chemical.

Substance2

Chemical

??

??

...

...

??

??

...

...

Partage des objets flyweight


Extraire la partie immuable dun objet nest quune partie du travail dans lapplication du pattern FLYWEIGHT. Vous devez encore crer une classe factory flyweight qui
instancie les flyweights, et faire en sorte que les clients se les partagent. Vous devez
aussi vous assurer que les clients utiliseront votre factory au lieu de construire euxmmes des instances de la classe flyweight.
Pour crer des flyweights, vous avez besoin dune factory, peut-tre une classe
ChemicalFactory avec une mthode statique qui retourne une substance chimique
daprs un nom donn. Vous pourriez stocker les substances dans une table de
hachage, crant des substances connues lors de linitialisation de la factory. La
Figure 13.4 illustre un exemple de conception pour ChemicalFactory.
Figure 13.4
ChemicalFactory

La classe ChemicalFactory est une


factory flyweight qui
retourne des objets
Chemical.

-chemicals:Hashtable
+getChemical(name:String):Chemical

Chemical

Le code de ChemicalFactory peut utiliser un initialisateur statique pour stocker les


objets Chemical dans une table Hasthtable:
package com.oozinoz.chemical;
import java.util.*;

pattern Livre Page 147 Vendredi, 9. octobre 2009 10:31 10

Chapitre 13

FLYWEIGHT

147

public class ChemicalFactory {


private static Map chemicals = new HashMap();
static {
chemicals.put(
"carbon", new Chemical("Carbon", "C", 12));
chemicals.put(
"sulfur", new Chemical("Sulfur", "S", 32));
chemicals.put(
"saltpeter", new Chemical("Saltpeter", "KN03", 101));
//...
}
public static Chemical getChemical(String name) {
return (Chemical) chemicals.get(name.toLowerCase());
}
}

Aprs avoir cr une factory pour les substances chimiques, vous devez maintenant
prendre des mesures pour vous assurer que dautres dveloppeurs lutiliseront et
ninstancieront pas eux-mmes la classe Chemical. Une approche simple est de
sappuyer sur laccessibilit de la classe Chemical.
Exercice 13.3
Comment pouvez-vous utiliser laccessibilit de la classe Chemical pour dcourager dautres dveloppeurs de linstancier ?

Les modificateurs daccs ne fournissent pas le contrle total sur linstanciation


dont vous auriez besoin. Vous pourriez vous assurer que ChemicalFactory soit la
seule classe pouvoir crer de nouvelles instances Chemical. Pour atteindre ce
niveau de contrle, vous pouvez appliquer une classe interne en dfinissant la classe
Chemical dans ChemicalFactory (voir le package com.oozinoz.chemical2).
Pour accder un type imbriqu, les clients doivent spcifier le type "contenant",
avec des expressions telles que les suivantes :
ChemicalFactory.Chemical c =
ChemicalFactory.getChemical("saltpeter");

pattern Livre Page 148 Vendredi, 9. octobre 2009 10:31 10

148

Partie II

Patterns de responsabilit

Vous pouvez simplifier lemploi dune classe imbrique en faisant de Chemical une
interface et en nommant la classe ChemicalImpl. Linterface Chemical peut spcifier
trois mthodes accesseurs, comme suit :
package com.oozinoz.chemical2;
public interface Chemical {
String getName();
String getSymbol();
double getAtomicWeight();
}

Les clients ne rfrenceront jamais directement la classe interne. Vous pouvez donc
la dfinir prive pour avoir la garantie que seul ChemicalFactory2 y aura accs.
Exercice 13.4
Compltez le code suivant pour ChemicalFactory2.java.

package com.oozinoz.chemical2;
import java.util.*;
public class ChemicalFactory2 {
private static Map chemicals = new HashMap();
/* Exercice ! */ implements Chemical {
private String name;
private String symbol;
private double atomicWeight;
ChemicalImpl(
String name,
String symbol,
double atomicWeight) {
this.name = name;
this.symbol = symbol;
this.atomicWeight = atomicWeight;
}
public String getName() {
return name;
}
public String getSymbol() {
return symbol;
}

pattern Livre Page 149 Vendredi, 9. octobre 2009 10:31 10

Chapitre 13

FLYWEIGHT

149

public double getAtomicWeight() {


return atomicWeight;
}
public String toString() {
return name + "(" + symbol + ")[" +
atomicWeight + "]";
}
}
/* Exercice ! */ {
chemicals.put("carbon",
factory.new ChemicalImpl("Carbon", "C", 12));
chemicals.put("sulfur",
factory.new ChemicalImpl("Sulfur", "S", 32));
chemicals.put("saltpeter",
factory.new ChemicalImpl(
"Saltpeter", "KN03", 101));
//...
}
public static Chemical getChemical(String name) {
return /* Exercice ! */
}
}

Rsum
Le pattern FLYWEIGHT vous permet de partager laccs des objets qui peuvent se
prsenter en grande quantit, tels que des caractres ou des substances chimiques.
Les objets flyweight doivent tre immuables, une proprit que vous pouvez tablir
en extrayant la partie immuable de la classe que vous voulez partager. Pour garantir
le partage des objets flyweight, vous pouvez fournir une classe factory partir de
laquelle les clients pourront obtenir des flyweights, puis forcer lemploi de cette
factory. Les modificateurs daccs vous donnent un certain contrle sur laccs
votre code par les autres dveloppeurs, mais vous bnficierez dun meilleur
contrle au moyen de classes internes en garantissant quune classe ne pourra tre
accessible que par la classe qui la contient. En vous assurant que les clients utiliseront comme il se doit votre factory flyweight, vous pouvez fournir un accs partag
scuris de nombreux objets.

pattern Livre Page 150 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 151 Vendredi, 9. octobre 2009 10:31 10

III
Patterns de construction

pattern Livre Page 152 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 153 Vendredi, 9. octobre 2009 10:31 10

14
Introduction la construction
Lorsque vous crez une classe Java, vous prvoyez normalement une fonctionnalit
pour la cration des objets en fournissant un constructeur. Un constructeur est
cependant utile uniquement si les clients savent quelle classe instancier et disposent
des paramtres que le constructeur attend. Plusieurs patterns de conception peuvent
intervenir dans les situations o ces conditions, ou dautres circonstances de
construction ordinaire, ne valent pas. Avant dexaminer ces types de conception
utiles o la construction ordinaire ne suffit pas, il peut tre utile de revoir ce quest
une construction classique en Java.

Quelques dfis de construction


Les constructeurs sont des mthodes spciales. Par bien des aspects, dont les modificateurs daccs, la surcharge ou les listes de paramtres, les constructeurs sapparentent des mthodes ordinaires. Dun autre ct, leur emploi et leur
comportement sont rgis par un nombre significatif de rgles syntaxiques et smantiques.
Exercice 14.1
Citez quatre rgles gouvernant lusage et le comportement des constructeurs
dans le langage Java.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.

pattern Livre Page 154 Vendredi, 9. octobre 2009 10:31 10

154

Partie III

Patterns de construction

Dans certains cas, Java fournit des constructeurs avec un comportement par dfaut.
Tout dabord, si une classe ne possde pas de constructeur dclar, Java en fournit
un par dfaut, lequel quivaut un constructeur public, nattendant aucun argument
et ne comportant aucune instruction dans son corps.
Un second comportement par dfaut du langage se produit lorsque la dclaration du
constructeur dune classe nutilise pas une variation de this() ou de super() pour
invoquer de faon explicite un autre constructeur. Java insre alors super() sans
argument. Cela peut provoquer des rsultats surprenants, comme avec la compilation
du code suivant :
package app.construction;
public class Fuse {
private String name;
// public Fuse(String name) { this.name = name; }
}

et :
package app.construction;
public class QuickFuse extends Fuse { }

Ce code compile correctement tant que vous ne retirez pas les marques de commentaire //.
Exercice 14.2
Expliquez lerreur qui se produira si vous retirez les marques de commentaire,
permettant ainsi la super-classe Fuse daccepter un nom dans son constructeur.

La faon la plus courante dinstancier des objets est dinvoquer loprateur new,
mais vous pouvez aussi utiliser la rflexion. La rflexion donne la possibilit de
travailler avec des types et des membres de type en tant quobjets. Mme si vous
nutilisez pas frquemment la rflexion, il nest pas trop difficile de suivre la logique dun programme sappuyant sur cette technique, comme dans lexemple
suivant :
package app.construction;
import java.awt.Point;
import java.lang.reflect.Constructor;

pattern Livre Page 155 Vendredi, 9. octobre 2009 10:31 10

Chapitre 14

Introduction la construction

155

public class ShowReflection {


public static void main(String args[]) {
Constructor[] cc = Point.class.getConstructors();
Constructor cons = null;
for (int i = 0; i < cc.length; i++)
if (cc[i].getParameterTypes().length == 2)
cons = cc[i];

Exercice 14.3
Quest-ce que le programme ShowReflection produit en sortie ?

La rflexion vous permet datteindre des rsultats qui sont autrement difficiles ou
impossibles atteindre.

Rsum
Dordinaire, vous fournissez des classes avec des constructeurs pour en permettre
linstanciation. Ceux-ci peuvent former une suite collaborative et chaque constructeur doit au final invoquer le constructeur de la super-classe. La mthode classique
dappel dun constructeur est lemploi de loprateur new mais vous pouvez aussi
recourir la rflexion pour instancier et utiliser des objets.

Au-del de la construction ordinaire


Le mcanisme de constructeur dans Java offre de nombreuses options de conception de classe. Toutefois, un constructeur dune classe nest efficace que si lutilisateur sait quelle classe instancier et connat les champs requis pour linstanciation.
Par exemple, le choix des composants dune GUI crer peut dpendre du matriel
sur lequel le programme doit sexcuter. Un quipement portable naura pas la
mme surface daffichage quun ordinateur. Il peut aussi arriver quun dveloppeur
sache quelle classe instancier mais ne possde pas toutes les valeurs initiales, ou
quil les ait dans le mauvais format. Par exemple, le dveloppeur peut avoir besoin
de crer un objet partir dune version dormante ou textuelle dun objet. Dans une
telle situation, lemploi ordinaire de constructeurs Java ne suffit pas et vous devez
recourir un pattern de conception.

pattern Livre Page 156 Vendredi, 9. octobre 2009 10:31 10

156

Partie III

Patterns de construction

Le tableau suivant dcrit lobjectif de patterns qui facilitent la construction.


Si vous envisagez de

Appliquez le pattern

Collecter progressivement des informations sur un objet avant de


demander sa construction

BUILDER

Diffrer la dcision du choix de la classe instancier

FACTORY METHOD

Construire une famille dobjets qui partagent certains aspects

ABSTRACT FACTORY

Spcifier un objet crer en donnant un exemple

PROTOTYPE

Reconstruire un objet partir dune version dormante ne


contenant que ltat interne de lobjet

MEMENTO

Lobjectif de chaque pattern de conception est de permettre la rsolution dun


problme dans un certain contexte. Les patterns de construction permettent
un client de construire un nouvel objet par lintermdiaire de moyens autres que
lappel dun constructeur de classe. Par exemple, lorsque vous obtenez progressivement les valeurs initiales dun objet, vous pouvez envisager dappliquer le pattern
BUILDER.

pattern Livre Page 157 Vendredi, 9. octobre 2009 10:31 10

15
BUILDER
Vous ne disposez pas toujours de toutes les informations ncessaires pour crer un
objet lorsque vient le moment de le construire. Il est particulirement pratique de
permettre la construction progressive dun objet, au rythme de lobtention des paramtres pour le constructeur, comme cela se produit avec lemploi dun analyseur
syntaxique ou avec une interface utilisateur. Cela peut aussi tre utile lorsque vous
souhaitez simplement rduire la taille dune classe dont la construction est relativement complique sans que cette complexit ait rellement de rapport avec le but
principal de la classe.
Lobjectif du pattern BUILDER est de dplacer la logique de construction dun
objet en dehors de la classe instancier.

Un objet constructeur ordinaire


Une situation banale dans laquelle vous pouvez tirer parti du pattern BUILDER est
celle o les donnes qui dfinissent lobjet voulu sont incorpores dans une chane
de texte. A mesure que votre code examine, ou analyse, les donnes, vous devez les
stocker telles que vous les trouvez. Que votre analyseur sappuie sur XML ou soit
une cration personnelle, il est possible que vous ne disposiez initialement pas de
suffisamment de donnes pour construire lobjet voulu. La solution fournie par
BUILDER est denregistrer les donnes extraites du texte dans un objet intermdiaire
jusqu ce que le programme soit prt lui demander de construire lobjet partir
de ces donnes.

pattern Livre Page 158 Vendredi, 9. octobre 2009 10:31 10

158

Partie III

Patterns de construction

Supposez quen plus de fabriquer des fuses, Oozinoz organise parfois des feux
dartifice. Les agences de voyages envoient des requtes de rservation dans le
format suivant :
Date, November 5, Headcount, 250, City, Springfield,
DollarsPerHead, 9,95, HasSite, False

Comme vous lavez sans doute remarqu, ce protocole remonte une poque antrieure XML (Extensible Markup Language), mais il sest montr suffisant jusqu
prsent.
La requte signale quand un client potentiel souhaite organiser un feu dartifice et
dans quelle ville cela doit se passer. Elle spcifie aussi le nombre de personnes
(Headcount) minimal garanti par le client et le prix par tte (DollarsPerHead) que
le client accepte de payer. Le client, dans cet exemple, souhaite organiser un show
pour 250 invits et est prt payer $9,95 par personne, soit un total de $2 487,50.
Lagence de voyages indique aussi que le client na pas de site lesprit (False)
pour le droulement du show.
La tche raliser consiste analyser le texte de la requte et crer un objet
Reservation reprsentant celle-ci. Nous pourrions accomplir cela en crant un
objet Reservation vide et en dfinissant ses paramtres mesure que notre analyseur (parser) les rencontre. Le problme est quun objet Reservation pourrait ne
pas reprsenter une requte valide. Par exemple, nous pourrions terminer lanalyse
du texte et raliser quil manque une date.
Pour nous assurer quun objet Reservation reprsente toujours une requte valide,
nous pouvons utiliser une classe ReservationBuilder. Lobjet ReservationBuilder peut stocker les attributs dune requte de rservation mesure que
lanalyseur les trouve, puis crer un objet Reservation en vrifiant sa validit.
La Figure 15.1 illustre les classes dont nous avons besoin pour cette conception.
La classe ReservationBuilder est abstraite ainsi que sa mthode build(). Nous
crerons des sous-classes ReservationBuilder concrtes qui varieront au niveau
de linsistance avec laquelle elles tentent de crer un objet Reservation lorsque
les donnes sont incompltes. Le constructeur de la classe ReservationParser
attend un builder NDT : nous utiliserons ce terme pour diffrencier lobjet
de stockage du constructeur traditionnel auquel passer des informations.

pattern Livre Page 159 Vendredi, 9. octobre 2009 10:31 10

Chapitre 15

BUILDER

Reservation

Reservation(
date:Date,
headcount:int,
city:String,
dollarsPerHead:double,
hasSite:bool)

159

ReservationBuilder

futurize(:Date):Date
getCity():String
setCity(:String)
getDate():date
setDate(:date)
getDollarsPerHead():Dollars

ReservationParser
-builder:ReservationBuilder

setDollarsPerHead(:Dollars)
hasSite():bool
setHasSite(:bool)
getHeadcount():int

ReservationParser(
:ReservationBuilder)
parse(s:String)

setHeadcount(:int)
build():Reservation

Figure 15.1
Une classe builder libre une classe spcifique de la logique de construction et peut accepter
progressivement des paramtres dinitialisation mesure quun analyseur syntaxique les
dcouvre.

La mthode parse() extrait des informations dune chane de rservation et les


transmet au builder, comme dans lextrait suivant :
public void parse(String s) throws ParseException {
String[] tokens = s.split(",");
for (int i = 0; i < tokens.length; i += 2) {
String type = tokens[i];
String val = tokens[i + 1];
if ("date".compareToIgnoreCase(type) == 0) {
Calendar now = Calendar.getInstance();
DateFormat formatter = DateFormat.getDateInstance();
Date d = formatter.parse(
val + ", " + now.get(Calendar.YEAR));
builder.setDate(ReservationBuilder.futurize(d));
} else if ("headcount".compareToIgnoreCase(type) == 0)
builder.setHeadcount(Integer.parseInt(val));

pattern Livre Page 160 Vendredi, 9. octobre 2009 10:31 10

160

Partie III

Patterns de construction

else if ("City".compareToIgnoreCase(type) == 0)
builder.setCity(val.trim());
else if ("DollarsPerHead".compareToIgnoreCase(type)==0)
builder.setDollarsPerHead(
new Dollars(Double.parseDouble(val)));
else if ("HasSite".compareToIgnoreCase(type) == 0)
builder.setHasSite(val.equalsIgnoreCase("true"));
}
}

Le code de parse() utilise une mthode String.split() pour diviser, ou dcouper, la chane fournie en entre. Le code attend une rservation sous forme dune
liste de types dinformation et de valeurs spars par une virgule. La mthode
String.compareToIgnoreCase() permet la comparaison de ne pas tenir compte
de la casse. Lorsque lanalyseur rencontre le mot "date", il examine la valeur qui
suit et la place dans le futur. La mthode futurize() avance lanne de la date
jusqu ce que cette dernire soit situe dans le futur. A mesure que vous progresserez dans lexamen du code, vous remarquerez plusieurs endroits o lanalyseur
pourrait sgarer, commencer par le dcoupage initial de la chane de rservation.
Exercice 15.1
Lobjet dexpression rgulire utilis par les appels de split() divise une liste
de valeurs spares par des virgules en chanes individuelles. Suggrez une
amlioration de cette expression rgulire, ou de lensemble de lapproche, qui
permettra lanalyseur de mieux reconnatre les informations de rservation.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.


Construction avec des contraintes
Vous devez vous assurer que les objets Reservation invalides ne soient jamais
instancis. Plus spcifiquement, supposez que toute rservation doit avoir une
valeur non nulle pour la date et la ville. Supposez aussi quune rgle mtier stipule
quOozinoz ne ralisera pas le show pour moins de 25 personnes ou moins de
$495,95. Ces limites pourraient tre enregistres dans une base de donnes, mais
pour linstant nous les reprsenterons sous forme de constantes dans le code Java,
comme dans lexemple suivant :
public abstract class ReservationBuilder {
public static final int MINHEAD = 25;

pattern Livre Page 161 Vendredi, 9. octobre 2009 10:31 10

Chapitre 15

BUILDER

161

public static final Dollars MINTOTAL = new Dollars(495.95);


// ...
}

Pour viter la cration dune instance de Reservation lorsquune requte est invalide, vous pourriez placer les contrles de logique mtier et les gnrations dexceptions dans le constructeur pour Reservation. Cette logique est toutefois
relativement indpendante de la fonction normale dun objet Reservation une fois
celui-ci cr. Lintroduction dun builder permettra de simplifier la classe Reservation en ne laissant que des mthodes ddies dautres fonctions que la construction. Lemploi dun builder donne aussi la possibilit de valider les paramtres dun
objet Reservation en proposant des ractions diffrentes en cas de paramtres
invalides. Finalement, dplac au niveau dune sous-classe ReservationBuilder,
le travail de construction peut se drouler progressivement mesure que lanalyseur
dcouvre les valeurs des attributs de rservation. La Figure 15.2 illustre des sousclasses ReservationBuilder concrtes qui diffrent dans leur faon de tolrer des
paramtres invalides.

ReservationBuilder

build():Reservation

Exception

UnforgivingBuilder

build():Reservation

throws

BuilderException

ForgivingBuilder

build():Reservation

throws

Figure 15.2
Les objets builders peuvent diffrer dans leur niveau de sensibilit et de gnration
dexceptions en cas de chane de rservation incomplte.

pattern Livre Page 162 Vendredi, 9. octobre 2009 10:31 10

162

Partie III

Patterns de construction

Le diagramme de la Figure 15.2 met en valeur un avantage dappliquer le pattern


BUILDER. En sparant la logique de construction de la classe Reservation, nous
pouvons traiter la construction comme une tche distincte et mme crer une hirarchie dapproches distincte. Les diffrences de comportement lors de la construction
ont peu de rapport avec la logique de rservation. Par exemple, les builders dans la
Figure 15.2 diffrent dans leur niveau de sensibilit pour ce qui est de la gnration
dune exception BuilderException. Un code utilisant un builder ressemblera
lextrait suivant :
package app.builder;
import com.oozinoz.reservation.*;
public class ShowUnforgiving {
public static void main(String[] args) {
String sample =
"Date, November 5, Headcount, 250, "
+ "City, Springfield, DollarsPerHead, 9.95, "
+ "HasSite, False";
ReservationBuilder builder = new UnforgivingBuilder();
try {
new ReservationParser(builder).parse(sample);
Reservation res = builder.build();
System.out.println("Builder non tolrant : " + res);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}

Lexcution de ce programme affiche un objet Reservation:


Date: Nov 5, 2001, Headcount: 250, City: Springfield,
Dollars/Head: 9.95, Has Site: false

A partir dune chane de requte de rservation, le code instancie un builder et un


analyseur, et demande celui-ci danalyser la chane. A mesure quil lit la chane,
lanalyseur transmet les attributs de rservation au builder en utilisant ses mthodes
set.
Aprs lanalyse, le code demande au builder de construire une rservation valide.
Cet exemple affiche simplement le texte dun message dexception au lieu dentreprendre une action plus consquente comme ce serait le cas pour une relle application.

pattern Livre Page 163 Vendredi, 9. octobre 2009 10:31 10

Chapitre 15

BUILDER

163

Exercice 15.2
La mthode build() de la classe UnforgivingBuilder gnre une exception
BuilderException si la valeur de la date ou de la ville est null, si le nombre de
personnes est trop bas, ou si le cot total de la rservation propose est trop
faible. Ecrivez le code de la mthode build() en fonction de ces spcifications.

Un builder tolrant
La classe UnforgivingBuilder rejette toute requte comportant la moindre erreur.
Une meilleure rgle de gestion serait dapporter des changements raisonnables aux
requtes auxquelles il manque certains dtails concernant la rservation.
Supposez quun analyste dOozinoz vous demande de dfinir le nombre de personnes un minimum si cette valeur dattribut est omise. De mme, si le prix accept
par tte est manquant, le builder pourrait dfinir cet attribut pour que le cot total
soit suprieur au minimum requis. Ces exigences sont simples, mais la conception
ncessite quelque rflexion. Par exemple, que devra faire le builder si une chane de
rservation fournit un cot par tte sans indiquer le nombre de personnes ?
Exercice 15.3
Rdigez une spcification pour ForgivingBuilder.build() en prvoyant ce
que le builder devrait faire en cas domission du nombre de personnes ou du prix
par tte.

Exercice 15.4
Aprs avoir revu votre approche, crivez le code de la mthode build() pour la
classe ForgivingBuilder.

Les classes ForgivingBuilder et UnforgivingBuilder garantissent que les


objets Reservation seront toujours valides. Votre conception apporte aussi de la
souplesse quant laction entreprendre en cas de problme dans la construction
dune rservation.

pattern Livre Page 164 Vendredi, 9. octobre 2009 10:31 10

164

Partie III

Patterns de construction

Rsum
Le pattern BUILDER spare la construction dun objet complexe de sa reprsentation. Il sensuit une simplification du processus de construction. Il permet une
classe de se concentrer sur la construction correcte dun objet en permettant la classe
principale de se concentrer sur le fonctionnement dune instance valide. Cela est
particulirement utile lorsque vous voulez garantir la validit dun objet avant de
linstancier et ne souhaitez pas que la logique associe apparaisse dans le constructeur de la classe. Un objet builder rend aussi possible une construction progressive,
ce qui se produit souvent lorsque vous crez un objet partir de lanalyse dun
texte.

pattern Livre Page 165 Vendredi, 9. octobre 2009 10:31 10

16
FACTORY METHOD
Lorsque vous dveloppez une classe, vous fournissez gnralement des constructeurs pour permettre aux clients de linstancier. Cependant, un client qui a besoin
dun objet ne sait pas, ou ne devrait pas savoir, quelle classe instancier parmi
plusieurs choix possibles.
Lobjectif du pattern FACTORY METHOD est de laisser un autre dveloppeur dfinir linterface permettant de crer un objet, tout en gardant un contrle sur le
choix de la classe instancier.

Un exemple classique : des itrateurs


Le pattern ITERATOR (itrateur) offre un moyen daccder de manire squentielle
aux lments dune collection (voir le Chapitre 28), mais FACTORY METHOD sous-tend
souvent la cration des itrateurs. La version 1.2 du JDK a introduit une interface
Collection qui inclut une mthode iterator(); toutes les collections limplmentent. Cette opration vite que lappelant ait savoir quelle classe instancier.
Une mthode iterator() cre un objet qui retourne une squence forme des lments
dune collection. Par exemple, le code suivant cre et affiche le contenu dune liste :
package app.factoryMethod;
import java.util.*;
public class ShowIterator {
public static void main(String[] args) {
List list = Arrays.asList(
new String[] {
"fountain", "rocket", "sparkler"});

pattern Livre Page 166 Vendredi, 9. octobre 2009 10:31 10

166

Partie III

Patterns de construction

Iterator iter = list.iterator();


while (iter.hasNext())
System.out.println(iter.next());
}
}

Exercice 16.1
Quelle est la classe relle de lobjet Iterator dans ce code ?

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.


Le pattern FACTORY METHOD dcharge le client du souci de savoir quelle classe
instancier.

Identification de FACTORY METHOD


Vous pourriez penser que nimporte quelle mthode crant et retournant un nouvel
objet est forcment une mthode factory. Cependant, dans la programmation oriente objet, les mthodes qui retournent de nouveaux objets sont chose courante, et
elles ne sont pas toutes des instances de FACTORY METHOD.
Exercice 16.2
Nommez deux mthodes frquemment utilises des bibliothques de classes Java
qui retournent un nouvel objet.
Le fait quune mthode cre un nouvel objet ne signifie pas ncessairement quil
sagit dun exemple de FACTORY METHOD. Une mthode factory est une opration
qui non seulement produit un nouvel objet mais vite au client de savoir quelle
classe instancier. Dans une conception FACTORY METHOD, vous trouvez plusieurs
classes qui implmentent la mme opration retournant le mme type abstrait, mais,
lors de la demande de cration dun nouvel objet, la classe qui est effectivement
instancie dpend du comportement de lobjet factory recevant la requte.
Exercice 16.3
Le nom de la classe javax.swing.BorderFactory semble indiquer un exemple du
pattern FACTORY METHOD. Expliquez en quoi lobjectif du pattern diffre de celui de
cette classe.

pattern Livre Page 167 Vendredi, 9. octobre 2009 10:31 10

Chapitre 16

FACTORY METHOD

167

Garder le contrle sur le choix de la classe instancier


En gnral, un client qui requiert un objet instancie la classe voulue en utilisant un
de ses constructeurs. Il se peut aussi parfois que le client ne sache pas exactement
quelle classe instancier. Cela peut se produire, par exemple, dans le cas ditrateurs,
la classe requise dpendant du type de collection que le client souhaite parcourir,
mais aussi frquemment dans du code dapplication.
Supposez quOozinoz soit prt laisser les clients acheter des feux dartifice
crdit. Ds le dbut du dveloppement du systme dautorisation de crdit, vous
acceptez de prendre en charge la conception dune classe CreditCheckOnline
dont lobjectif sera de vrifier si un client peut disposer dun certain montant de
crdit chez Oozinoz.
En entamant le dveloppement, vous ralisez que lorganisme de crdit sera parfois
hors ligne. Lanalyste du projet dtermine que, dans ce cas, il faut que le reprsentant du centre de rception des appels puisse disposer dune bote de dialogue pour
prendre une dcision sur la base de quelques questions.
Vous crez donc une classe CreditCheckOffline et implmentez le processus en
respectant les spcifications. Initialement, vous concevez les classes comme illustr
Figure 16.1. La mthode creditLimit() accepte un numro didentification de
client et retourne sa limite de crdit.
Avec les classes de la Figure 16.1, vous pouvez fournir des informations de limite
de crdit, que lorganisme de crdit soit ou non en ligne. Le problme qui se
prsente maintenant est que lutilisateur de vos classes doit connatre la classe
instancier, mais vous tes celui qui sait si lorganisme est ou non disponible.
Dans ce scnario, vous devez vous appuyer sur linterface pour crer un objet, mais
garder le contrle sur le choix de la classe instancier. Une solution possible est de
changer les deux classes pour implmenter une interface standard et crer une
mthode factory qui retourne un objet de ce type. Spcifiquement, vous pourriez :
m
m

faire une interface Java CreditCheck qui inclut la mthode creditLimit();


changer les deux classes de contrle de crdit afin quelles implmentent linterface CreditCheck;
crer une classe CreditCheckFactory avec une mthode createCreditCheck() qui retournera un objet de type CreditCheck.

pattern Livre Page 168 Vendredi, 9. octobre 2009 10:31 10

168

Partie III

Figure 16.1
Une de ces classes
sera instancie pour
vrifier la limite de
crdit dun client.

Patterns de construction

com.oozinoz.credit

CreditCheckOnline

creditLimit(id:int):Dollars

CreditCheckOffline

creditLimit(id:int):Dollars

En implmentant createCreditCheck(), vous utiliserez vos informations de


disponibilit de lorganisme de crdit pour dcider de la classe instancier.
Exercice 16.4
Dessinez un diagramme de classes pour cette nouvelle stratgie, qui permet de
crer un objet de vrification de crdit tout en conservant la matrise sur le choix
de la classe instancier.

Grce limplmentation de FACTORY METHOD, lutilisateur de vos services pourra


appeler la mthode createCreditCheck() et obtenir un objet de contrle de crdit
qui fonctionnera indpendamment de la disponibilit de lagence.
Exercice 16.5
Supposez que la classe CreditCheckFactory comprenne une mthode
isAgencyUp() indiquant si lagence est disponible et crivez le code pour createCreditCheck().

pattern Livre Page 169 Vendredi, 9. octobre 2009 10:31 10

Chapitre 16

FACTORY METHOD

169

Application de FACTORY METHOD dans une hirarchie parallle


Le pattern FACTORY METHOD apparat souvent lorsque vous utilisez une hirarchie
parallle pour modliser un domaine de problmes. Une hirarchie parallle est
une paire de hirarchies de classes dans laquelle chaque classe dune hirarchie
possde une classe correspondante dans lautre hirarchie. Une telle conception
intervient gnralement lorsque vous dcidez de dplacer un sous-ensemble
doprations hors dune hirarchie dj existante.
Considrez la construction de bombes ariennes comme illustr au Chapitre 5. Pour
les fabriquer, Oozinoz utilise des machines organises selon le modle du
diagramme prsent la Figure 16.2.
Figure 16.2
La hirarchie Machine intgre
une logique de contrle des
machines physiques et de
planification.

Machine

getAvailable():Date
start()
stop()
...

Mixer

ShellAssembler

StarPress

Fuser

Pour concevoir une bombe, des substances sont mlanges dans un mixeur (Mixer)
puis passes une presse extrudeuse (StarPress) qui produit des granules, ou toiles. Celles-ci sont tasses dans une coque sphrique contenant en son centre de la
poudre noire et le tout est plac au-dessus dune chasse, ou charge de propulsion, au
moyen dune assembleuse (ShellAssembler). Un dispositif dallumage est ensuite
insr (Fuser), lequel servira la mise feu de la charge de propulsion et celle de
la coque centrale.
Imaginez que vous vouliez que la mthode getAvailable() prvoie le moment o
une machine termine le traitement en cours et est disponible pour un autre travail.

pattern Livre Page 170 Vendredi, 9. octobre 2009 10:31 10

170

Partie III

Patterns de construction

Cela peut ncessiter lemploi de diverses mthodes prives qui ajouteront un certain
volume de code chacune de nos classes de machines. Plutt que dajouter la logique de planification la hirarchie Machine, vous pourriez prfrer utiliser une
hirarchie MachinePlanner distincte. Vous avez besoin dune classe de planification distincte pour la plupart des types de machines, sauf pour les mixeurs et les
sertisseuses de dispositifs dallumage, qui sont toujours disponibles pour du travail
supplmentaire et peuvent se suffire dune classe BasicPlanner.
Exercice 16.6
Compltez le diagramme de la hirarchie parallle Machine/MachinePlanner
de la Figure 16.3.
Figure 16.3
Epurez la hirarchie
Machine en dplaant la logique de
planification vers une
hirarchie parallle.

Machine

MachinePlanner
#machine:Machine

createPlanner():??
MachinePlanner(
m:Machine)
getAvailable():Date
??

??

??

??

??

??

??

Exercice 16.7
Ecrivez une mthode createPlanner() pour que la classe Machine retourne un
objet BasicPlanner, et crivez une mthode createPlanner() pour la classe
StarPress.

pattern Livre Page 171 Vendredi, 9. octobre 2009 10:31 10

Chapitre 16

FACTORY METHOD

171

Rsum
Lobjectif du pattern FACTORY METHOD est de permettre un fournisseur de services
dexonrer le client du besoin de savoir quelle classe instancier. Ce pattern intervient
dans la bibliothque de classes Java, notamment dans la mthode iterator() de
linterface Collection.
FACTORY METHOD se prsente souvent au niveau du code du client, lorsquil est
ncessaire de dcharger les clients de la ncessit de connatre la classe partir de
laquelle crer un objet. Ce besoin disoler le client peut apparatre lorsque le choix
de la classe instancier dpend dun facteur dont le client na pas connaissance,
comme la disponibilit dun service externe. Vous pouvez galement rencontrer
FACTORY METHOD lorsque vous introduisez une hirarchie parallle pour viter
quun ensemble de classes soit encombr par de nombreux aspects comportementaux. Vous pouvez ainsi relier des hirarchies en permettant aux sous-classes dune
hirarchie de dterminer quelle classe instancier dans la hirarchie correspondante.

pattern Livre Page 172 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 173 Vendredi, 9. octobre 2009 10:31 10

17
ABSTRACT FACTORY
Comme nous lavons vu dans le chapitre prcdent, il est parfois utile, lors de la
cration dobjets, de garder un contrle sur le choix de la classe instancier. Dans
ce cas, vous pouvez appliquer le pattern FACTORY METHOD avec une mthode qui
utilise un facteur externe pour dterminer la classe instancier. Dans certaines
circonstances, ce facteur peut tre thmatique, couvrant plusieurs classes.
Lobjectif du pattern ABSTRACT FACTORY, ou KIT, est de permettre la cration
de familles dobjets ayant un lien ou interdpendants.

Un exemple classique : le kit de GUI


Un kit de GUI est un exemple classique dapplication du pattern ABSTRACT
FACTORY. Cest un objet factory abstrait qui fournit les composants graphiques
un client laborant une interface utilisateur. Il dtermine lapparence que revtent
les boutons, les champs de texte ou tout autre lment. Un kit tablit un style
spcifique, en dfinissant les couleurs darrire-plan, les formes, ou autres aspects
dune GUI. Vous pourriez ainsi tablir un certain style pour la totalit dun
systme ou, au fil du temps, introduire des changements dans une application
existante, par exemple pour reflter un changement de version ou une modification des graphiques standards de la socit. Ce pattern permet ainsi dapporter
de la convivialit, de contribuer un apprentissage et une utilisation plus aiss
dune application en jouant sur son apparence. La Figure 17.1 illustre un exemple
avec la classe UI dOozinoz.

pattern Livre Page 174 Vendredi, 9. octobre 2009 10:31 10

174

Partie III

Patterns de construction

Figure 17.1
Les instances de la
classe UI sont des
objets factory qui
crent des familles de
composants de GUI.

UI
NORMAL:UI
createButton():JButton
getFont():Font
createPaddedPanel(c:Component):JPanel
getIcon(imageName:String):Icon

BetaUI

Les sous-classes de la classe UI peuvent redfinir nimporte quel lment de lobjet


factory. Une application qui construit une GUI partir dune instance de la classe
UI peut par la suite produire un style diffrent en se fondant sur une instance dune
sous-classe de UI. Par exemple, Oozinoz utilise une classe Visualization pour
aider les ingnieurs mettre en place de nouvelles lignes de fabrication. Lcran de
visualisation est illustr Figure 17.2.
Figure 17.2
Cette application ajoute
des machines dans la
partie suprieure gauche
de la fentre et laisse lutilisateur les positionner par
un glisser-dposer. Il peut
annuler un ajout ou un
positionnement.

pattern Livre Page 175 Vendredi, 9. octobre 2009 10:31 10

Chapitre 17

ABSTRACT FACTORY

175

Lapplication de visualisation de la Figure 17.2 permet un utilisateur dajouter des


machines et de les dplacer au moyen de la souris le programme qui affiche cette
visualisation est ShowVisualization dans le rpertoire app.abstractFactory.
Lapplication obtient ses boutons partir dun objet UI que la classe Visualization
accepte dans son constructeur. La Figure 17.3 illustre la classe Visualization.
Figure 17.3
La classe Visualization
construit une GUI
au moyen dun objet
factory UI.

Visualization

UI

Visualization(ui:UI)
#undoButton():JButton
...

La classe Visualization construit sa GUI laide dun objet UI. Par exemple, le
code de la mthode undoButton() se prsente comme suit :
protected JButton undoButton() {
if (undoButton == null) {
undoButton = ui.createButtonCancel();
undoButton.setText("Undo");
undoButton.setEnabled(false);
undoButton.addActionListener(mediator.undoAction());
}
return undoButton;
}

Ce code cre un bouton dannulation et modifie son texte (pour quil indique
"Undo"). La classe UI dtermine la taille et la position de limage et du texte sur le
bouton. Le code gnrateur de bouton de la classe UI se prsente comme suit :
public JButton createButton() {
JButton button = new JButton();
button.setSize(128, 128);
button.setFont(getFont());
button.setVerticalTextPosition(AbstractButton.BOTTOM);
button.setHorizontalTextPosition(AbstractButton.CENTER);
return button;
}
public JButton createButtonOk() {
JButton button = createButton();
button.setIcon(getIcon("images/rocket-large.gif"));
button.setText("Ok!");
return button;
}

pattern Livre Page 176 Vendredi, 9. octobre 2009 10:31 10

176

Partie III

Patterns de construction

public JButton createButtonCancel() {


JButton button = createButton();
button.setIcon(getIcon("images/rocket-large-down.gif"));
button.setText("Cancel!");
return button;
}

Afin de gnrer un autre style pour lapplication de visualisation des machines,


nous pouvons crer une sous-classe qui redfinit certains des lments de la classe
factory UI. Nous pourrons ensuite passer une instance de cette nouvelle classe factory
au constructeur de la classe Visualization.
Supposez que nous ayons introduit une nouvelle version de la classe Visualization avec des fonctionnalits supplmentaires. Pendant sa phase de bta-test, nous
dcidons de changer linterface utilisateur. Nous aimerions en fait avoir des polices
en italiques et substituer aux images de fuses des images provenant des fichiers
cherry-large.gif et cherry-largedown.gif. Voici un exemple de code dune
classe BetaUI drive de UI:
public class BetaUI extends UI {
public BetaUI () {
Font oldFont = getFont();
font = new Font(
oldFont.getName(),
oldFont.getStyle() | Font.ITALIC,
oldFont.getSize());
}
public JButton createButtonOk() {
// Exercice !
}
public JButton createButtonCancel() {
// Exercice !
}
}

Exercice 17.1
Compltez le code pour la classe BetaUI.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.

pattern Livre Page 177 Vendredi, 9. octobre 2009 10:31 10

Chapitre 17

ABSTRACT FACTORY

177

Le code suivant excute la visualisation avec le nouveau style :


package app.abstractFactory;
// ...
public class ShowBetaVisualization {
public static void main(String[] args) {
JPanel panel = new Visualization(new BetaUI());
SwingFacade.launch(panel, "Operational Model");
}
}

Ce programme excute la visualisation avec lapparence illustre Figure 17.4. Les


instances de UI et de BetaUI fournissent diffrentes familles de composants graphiques afin de proposer diffrents styles. Bien que ce soit une application utile du
pattern ABSTRACT FACTORY, la conception est quelque peu fragile. En particulier, la
classe BetaUI dpend de la possibilit de redfinir les mthodes charges de la cration et daccder certaines variables dinstance dclares protected, notamment
font, de la classe UI.
Figure 17.4
Sans changement
dans le code de la
classe Visualization,
lapplication affiche
la nouvelle interface
produite par la classe
BetaUI.

Exercice 17.2
Suggrez un changement de conception qui permettrait toujours de dvelopper
une varit dobjets factory, mais en rduisant la dpendance des sous-classes
lgard des modificateurs de mthodes de la classe UI.

pattern Livre Page 178 Vendredi, 9. octobre 2009 10:31 10

178

Partie III

Patterns de construction

Le pattern ABSTRACT FACTORY affranchit les clients du besoin de savoir quelles


classes instancier lorsquils ncessitent de nouveaux objets. A cet gard, il sapparente un ensemble de mthodes FACTORY METHOD. Dans certains cas, une conception
FACTORY METHOD peut voluer en une conception ABSTRACT FACTORY.

Classe FACTORY abstraite et pattern FACTORY METHOD


Le Chapitre 16 a introduit une paire de classes implmentant linterface CreditCheck. Dans la conception prsente, la classe CreditCheckFactory instancie une
de ces classes lorsquun client appelle sa mthode createCreditCheck(), et la
classe qui est instancie dpend de la disponibilit de lorganisme de crdit. Cette
conception vite aux autres dveloppeurs dtre dpendants de cette information.
La Figure 17.5 illustre la classe CreditCheckFactory et les implmentations de
linterface CreditCheck.
Figure 17.5
CreditCheckFactory

Une conception

FACTORY METHOD
qui exonre le code
client de lobligation
de connatre la classe
instancier pour
vrifier des informations de crdit.

createCreditCheck():CreditCheck

interface
CreditCheck

creditLimit(id:int):Dollars

CreditCheckOnline

CreditCheckOffline

La classe CreditCheckFactory fournit dhabitude des informations provenant de


lorganisme de crdit sur la limite autorise pour un client donn. En outre, le
package credit possde des classes qui peuvent rechercher des informations
dexpdition et de facturation pour un client. La Figure 17.6 illustre le package
com.oozinoz.credit original.
Supposez maintenant quun analyste des besoins dOozinoz vous signale que la
socit est prte prendre en charge les clients vivant au Canada. Pour travailler
avec le Canada, vous utiliserez un autre organisme de crdit ainsi que dautres sources
de donnes pour dterminer les informations dexpdition et de facturation.

pattern Livre Page 179 Vendredi, 9. octobre 2009 10:31 10

Chapitre 17

ABSTRACT FACTORY

179

com.oozinoz.credit

CreditCheckFactory

isAgencyUp():boolean
createCreditCheck():CreditCheck

interface

CreditCheckOnline

CreditCheck
CreditCheckOffline

ShippingCheck

hasTariff()

BillingCheck

isResidential()

Figure 17.6
Les classes dans ce package vrifient le crdit dun client, ladresse dexpdition et ladresse
de facturation.

Lorsquun client tlphone, lapplication utilise par le centre de rception des


appels doit recourir une famille dobjets pour effectuer une varit de contrles.
Cette famille devra tre diffrente selon que lappel proviendra du Canada ou des
Etats-Unis. Vous pouvez appliquer le pattern ABSTRACT FACTORY pour permettre la
cration de ces familles dobjets.
Lexpansion de lactivit au Canada doublera pratiquement le nombre de classes
sous-tendant les vrifications de crdit. Supposez que vous dcidiez de coder ces
classes dans trois packages. Le package credit contiendra maintenant trois interfaces "Check" et une classe factory abstraite. Cette classe aura trois mthodes de
cration pour gnrer les objets appropris chargs de vrifier les informations
de crdit, de facturation et denvoi. Vous inclurez aussi la classe CreditCheckOffline dans ce package, partant du principe que vous pourrez lutiliser pour effectuer

pattern Livre Page 180 Vendredi, 9. octobre 2009 10:31 10

180

Partie III

Patterns de construction

les contrles en cas dindisponibilit de lorganisme de crdit indpendamment de


lorigine dun appel. La Figure 17.7 montre la nouvelle composition du package
com.oozinoz.credit.
Figure 17.7
Le package revu
contient principalement des interfaces
et une classe factory
abstraite.

com.oozinoz.credit

interface
CreditCheck

CreditCheckOffline

creditLimit(id:int)

interface
BillingCheck

isResidential()

interface
ShippingCheck

CreditCheckFactory

isAgencyUp():bool
createCreditCheck()
createBillingCheck()
createShippingCheck()

hasTariff()

Pour implmenter les interfaces de credit avec des classes concrtes, vous pouvez
introduire deux nouveaux packages : com.oozinoz.credit.ca et com.oozinoz.credit.us. Chacun de ces packages peut contenir une version concrte de la
classe factory et des classes pour implmenter chacune des interfaces de credit.
Les classes factory concrtes pour les appels provenant du Canada et des Etats-Unis
sont relativement simples. Elles retournent les versions canadiennes ou tats-uniennes des interfaces "Check", sauf si lorganisme de crdit local est hors ligne, auquel
cas elles retournent toutes deux un objet CreditCheckOffline. Comme dans le
chapitre prcdent, la classe CreditCheckFactory possde une mthode
isAgencyUp() qui indique si lorganisme de crdit est disponible.

pattern Livre Page 181 Vendredi, 9. octobre 2009 10:31 10

Chapitre 17

ABSTRACT FACTORY

com.oozinoz.credit.ca

CheckFactoryCanada

181

com.oozinoz.credit

CreditCheckFactory

interface
BillingCheck

interface
ShippingCheck

interface
CreditCheck

Figure 17.8
Les classes du package com.oozinoz.credit.ca et leurs relations avec les classes
de com.oozinoz.credit.

Exercice 17.4
Compltez le code pour CheckFactoryCanada.java:
package com.oozinoz.credit.ca;
import com.oozinoz.credit.*;
public class CheckFactoryCanada extends CreditCheckFactory {
// Exercice !
}

A ce stade, vous disposez dune conception qui applique le pattern ABSTRACT


FACTORY pour permettre la cration de familles dobjets chargs de vrifier diffrentes
informations concernant un client. Une instance de la classe CreditCheckFactory
abstraite sera soit de la classe CheckFactoryCanada, soit de la classe CheckFactoryUS, et les objets de contrle gnrs seront appropris pour le pays reprsent
par lobjet factory.

pattern Livre Page 182 Vendredi, 9. octobre 2009 10:31 10

182

Partie III

Patterns de construction

Packages et classes factory abstraites


On peut quasiment dire quun package contient habituellement une famille de classes,
et quune classe factory abstraite produit une famille dobjets. Dans lexemple
prcdent, vous avez utilis des packages distincts pour supporter des classes
factory abstraites pour le Canada et les Etats-Unis, avec un troisime package fournissant des interfaces communes pour les objets produits par les classes factory.
Exercice 17.5
Justifiez la dcision de placer chaque classe factory et ses classes associes dans
un package distinct, ou argumentez en faveur dune autre approche juge suprieure.

Rsum
Le pattern ABSTRACT FACTORY vous permet de prvoir la possibilit pour un client
de crer des objets faisant partie dune famille dobjets entretenant une relation.
Une application classique de ce pattern concerne la cration de familles de composants de GUI, ou kits. Dautres aspects peuvent aussi tre traits sous forme de
familles dobjets, tels que le pays de rsidence dun client. Comme pour FACTORY
METHOD, ABSTRACT FACTORY vous permet dexonrer le client de la ncessit de
savoir quelle classe instancier pour crer un objet, en vous permettant de lui fournir
une classe factory produisant des objets lis par un aspect commun.

pattern Livre Page 183 Vendredi, 9. octobre 2009 10:31 10

18
PROTOTYPE
Lorsque vous dveloppez une classe, vous prvoyez habituellement des constructeurs pour permettre aux applications clientes de linstancier. Il y a toutefois des
situations o vous souhaitez empcher le code utilisateur de vos classes dappeler
directement un constructeur. Les patterns orients construction dcrits jusqu
prsent dans cette partie, BUILDER, FACTORY METHOD et ABSTRACT FACTORY, offrent
tous la possibilit de mettre en place ce type de prvention en tablissant des mthodes qui instancient une classe pour le compte du client. Le pattern PROTOTYPE dissimule galement la cration dun objet mais emploie une approche diffrente.
Lobjectif du pattern PROTOTYPE est de fournir de nouveaux objets par la copie
dun exemple plutt que de produire de nouvelles instances non initialises
dune classe.

Des prototypes en tant quobjets factory


Supposez que vous utilisiez le pattern ABSTRACT FACTORY chez Oozinoz pour fournir des interfaces utilisateurs pour diffrents contextes. La Figure 18.1 illustre les
classes factory de GUI pouvant voluer.
Figure 18.1
Trois classes factory
abstraites, ou kits,
pour produire diffrents styles de GUI.

UIKit

createButton()

HandheldUI

WideScreenUI

createGrid()
createGroupBox()
createPaddedPanel()
font():Font
...

BetaUI

pattern Livre Page 184 Vendredi, 9. octobre 2009 10:31 10

184

Partie III

Patterns de construction

Les utilisateurs dOozinoz apprcient la productivit rsultant du fait de pouvoir


appliquer une GUI approprie pour un contexte donn. Le problme que vous
rencontrez est que vos utilisateurs souhaitent plusieurs kits de GUI de plus, alors
quil devient encombrant de crer une nouvelle classe pour chaque contexte envisag
par eux. Pour stopper la prolifration des classes factory de GUI, un dveloppeur
dOozinoz suggre lapplication du pattern PROTOTYPE de la manire suivante :
m
m

supprimer les sous-classes de UIKit;


faire en sorte que chaque instance de UIKit devienne une factory de GUI qui
fonctionne en gnrant des copies de composants prototypes ;
placer le code qui cre de nouveaux objets UIKit dans des mthodes statiques
de la classe UIKit.

Avec cette conception, un objet UIKit aura un jeu complet de variables prototypes
dinstance : un objet bouton, un objet grille, un objet panneau avec relief de
remplissage, etc. Le code qui crera un nouvel objet UIKit dfinira les valeurs des
composants prototypes afin de produire lapparence dsire. Les mthodes de cration,
create-(), retourneront des copies de ces composants.
Par exemple, nous pouvons crer une mthode statique handheldUI() de la classe
UI. Cette mthode instanciera UIKit, dfinira les variables dinstance avec des
valeurs appropries pour un cran dquipement portable, et retournera lobjet
utiliser en tant que kit de GUI.
Exercice 18.1
Une conception selon PROTOTYPE diminuera le nombre de classes quOozinoz
utilise pour grer plusieurs kits de GUI. Citez deux avantages ou inconvnients
supplmentaires lis cette approche.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.


La faon normale de crer un objet est dinvoquer un constructeur dune classe. Le
pattern PROTOTYPE offre une solution souple, permettant de dterminer au moment
de lexcution lobjet utiliser en tant que modle pour le nouvel objet. Cette
approche dans Java ne permet cependant pas de nouveaux objets davoir des
mthodes diffrentes de celles de leur parent. Il vous faudra donc valuer les avantages et les inconvnients de cette technique et procder son exprimentation pour
dterminer si elle rpond vos besoins. Pour pouvoir lappliquer, vous devrez
matriser les mcanismes de la copie dobjets dans Java.

pattern Livre Page 185 Vendredi, 9. octobre 2009 10:31 10

Chapitre 18

PROTOTYPE

185

Prototypage avec des clones


Lobjectif du pattern PROTOTYPE est de fournir de nouveaux objets en copiant un
exemple. Lorsque vous copiez un objet, le nouvel objet aura les mmes attributs et
le mme comportement que ses parents. Le nouvel objet peut galement hriter de
certaines ou de toutes les valeurs de donnes de lobjet parent. Par exemple, une
copie dun panneau avec relief de remplissage devrait avoir la mme valeur de
remplissage que loriginal.
Il est important de se demander ceci : lorsque vous copiez un objet, lopration
fournit-elle des copies des valeurs dattributs de lobjet original ou la copie partaget-elle ces valeurs avec loriginal ? Il est facile doublier de se poser cette question
ou dy rpondre de faon incorrecte. Les dfauts apparaissent souvent lorsque les
dveloppeurs font des suppositions errones sur les mcanismes de la copie. De
nombreuses classes dans les bibliothques de classes Java offrent un support pour la
copie, mais en tant que dveloppeur, vous devez comprendre comment la copie
fonctionne, surtout si vous voulez utiliser PROTOTYPE.
Exercice 18.2
La classe Object inclut une mthode clone() dont tous les objets hritent. Si cette
mthode ne vous est pas familire, reportez-vous laide en ligne ou la documentation. Ecrivez ensuite dans vos propres termes ce que cette mthode effectue.

Exercice 18.3
Supposez que la classe Machine possdait deux attributs : un entier ID et un
emplacement, Location, sous forme dune classe distincte.
Dessinez un diagramme objet montrant un objet Machine, son objet Location,
et tout autre objet rsultant de lappel de clone() sur lobjet Machine.

La mthode clone() facilite lajout dune mthode copy() une classe. Par exemple, vous pourriez crer une classe de panneaux pouvant tre clons au moyen du
code suivant :
package com.oozinoz.ui;
import javax.swing.JPanel;

pattern Livre Page 186 Vendredi, 9. octobre 2009 10:31 10

186

Partie III

Patterns de construction

public class OzPanel extends JPanel implements Cloneable {


// Dangereux !
public OzPanel copy() {
return (OzPanel) this.clone();
}
// ...
}

Figure 18.2
La classe OzPanel
hrite dun grand
nombre de champs
et de variables de
ses super-classes.

java.lang.Object

javax.swing.Component
// Plus de 40 champs
getBackground()
getFont()
getForeground()
// Davantage de mthodes

java.awt.Container
// Plus de 10 champs
// Toujours plus de mthodes

java.awt.JComponent
// Plus de 20 champs
// Plus de mthodes

javax.swing.JPanel

com.oozinoz.ui.OzPanel

pattern Livre Page 187 Vendredi, 9. octobre 2009 10:31 10

Chapitre 18

PROTOTYPE

187

La mthode copy() dans ce code rend le clonage public et convertit la copie dans le
type adquat. Le problme de ce code est que la mthode clone() crera des copies
de tous les attributs dun objet JPanel, indpendamment du fait que vous compreniez ou non la fonction de ces attributs. Notez que les attributs de la classe JPanel
incluent les attributs des classes anctres, comme le montre la Figure 18.2.
Comme le suggre la Figure 18.2, la classe OzPanel hrite dun nombre important
de proprits de la classe Component, et ce sont souvent les seuls attributs quil
vous faut copier lorsque vous travaillez avec des objets de GUI.
Exercice 18.4
Ecrivez une mthode OzPanel.copy2() qui copie un panneau sans sappuyer
sur clone(). Supposez que les seuls attributs importants pour une copie sont
background, font et foreground.

Rsum
Le pattern PROTOTYPE permet un client de crer de nouveaux objets en copiant un
exemple. Une grande diffrence entre appeler un constructeur et copier un objet est
quune copie inclut gnralement un certain tat de lobjet original. Vous pouvez
utiliser cela votre avantage, surtout lorsque diffrentes catgories dobjets ne
diffrent que par leurs attributs et non dans leurs comportements. Dans ce cas, vous
pouvez crer de nouvelles classes au moment de lexcution en gnrant des objets
prototypes que le client peut copier.
Lorsque vous devez crer une copie, la mthode Object.clone() peut tre utile,
mais vous devez vous rappeler quelle cre un nouvel objet avec les mmes champs.
Cet objet peut ne pas tre une copie convenable, et toute difficult lie une opration
de copie plus importante relve de votre responsabilit. Si un objet prototype
possde trop de champs, vous pouvez crer un nouvel objet par instanciation et en
dfinissant ses champs de manire ne reprsenter que les aspects de lobjet original
que vous voulez copier.

pattern Livre Page 188 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 189 Vendredi, 9. octobre 2009 10:31 10

19
MEMENTO
Il y a des situations o lobjet que vous voulez crer existe dj. Cela se produit
lorsque vous voulez laisser un utilisateur annuler des oprations, revenir une
version prcdente dun travail, ou reprendre un travail suspendu.
Lobjectif du pattern MEMENTO est de permettre le stockage et la restauration
de ltat dun objet.

Un exemple classique : dfaire une opration


Le Chapitre 17 a introduit une application de visualisation permettant ses utilisateurs dexprimenter la modlisation des flux matriels dans une usine. Supposez
que la fonctionnalit du bouton Undo nait pas encore t implmente. Nous
pouvons appliquer le pattern MEMENTO pour faire fonctionner ce bouton.
Un objet mmento contient des informations dtat. Dans lapplication de visualisation, ltat que nous devons prserver est celui de lapplication. Lors de lajout ou
du dplacement dune machine, un utilisateur devrait tre en mesure dannuler
lopration en cliquant sur le bouton Undo. Pour ajouter cette fonctionnalit, nous
devons dcider de la faon de capturer ltat de lapplication dans un objet
mmento. Nous devrons aussi dcider du moment auquel le faire, et comment le
restaurer au besoin. Lorsque lapplication dmarre, elle apparat comme illustr
Figure 19.1.
Lapplication dmarre vierge, ce qui est malgr tout un tat. Dans ce cas, le bouton
Undo devrait tre dsactiv. Aprs quelques ajouts et dplacements, la fentre pourrait
ressembler lexemple de la Figure 19.2.

pattern Livre Page 190 Vendredi, 9. octobre 2009 10:31 10

190

Partie III

Patterns de construction

Figure 19.1
Lorsque lapplication
dmarre, le panneau
est vierge et le bouton
Undo est dsactiv.

Figure 19.2
Lapplication aprs
quelques ajouts et
positionnements de
machines.

Ltat quil nous faut enregistrer dans un mmento est une liste des emplacements
des machines qui ont t places par lutilisateur. Nous pouvons empiler ces mmentos,
en en dpilant un chaque fois que lutilisateur clique sur le bouton Undo :
m

Chaque fois que lutilisateur ajoute ou dplace une machine, le code doit crer
un mmento du factory simul et lajouter une pile.

pattern Livre Page 191 Vendredi, 9. octobre 2009 10:31 10

Chapitre 19

MEMENTO

191

Chaque fois quil clique sur le bouton Undo, le code doit retirer le mmento le
plus rcent, le plus haut dans la pile, et restaurer la simulation dans ltat qui y
aura t enregistr.

Lorsque lapplication dmarre, vous empilez un mmento vide qui nest jamais
prlev pour garantir que le sommet de la pile sera toujours un mmento valide.
Chaque fois que la pile ne contient quun mmento, vous dsactivez le bouton
Undo.
Nous pourrions crire le code de ce programme dans une seule classe, mais nous
envisageons lajout de fonctionnalits pour grer la modlisation oprationnelle et
dautres fonctions que les utilisateurs pourront ventuellement demander. Finalement, lapplication pouvant devenir plus grande, il est sage de sappuyer sur une
conception MVC (Modle-Vue-Contrleur) . La Figure 19.3 illustre une conception
qui place le travail de modlisation de lobjet factory en classes distinctes.
Figure 19.3
Cette conception divise le
travail de lapplication en
classes distinctes, pour
modliser lobjet factory,
fournir les lments de
GUI et grer les clics de
lutilisateur.

Visualization

VisMediator

FactoryModel

Cette conception vous permet de vous concentrer dabord sur le dveloppement


dune classe FactoryModel qui ne possde pas de composants de GUI et aucune
dpendance lgard de la GUI.
La classe FactoryModel est au cur de la conception. Elle est responsable de la
gestion de la configuration actuelle des machines et des mmentos des configurations
antrieures.
Chaque fois quun client demande lobjet factory dajouter ou de dplacer une
machine, celui-ci cre une copie, un objet mmento, de lemplacement actuel des
machines, et place lobjet sur la pile de mmentos. Dans cet exemple, nous navons
pas besoin dune classe Memento spciale. Chaque mmento est simplement une
liste de points : la liste des emplacements de lquipement un moment donn.

pattern Livre Page 192 Vendredi, 9. octobre 2009 10:31 10

192

Partie III

Patterns de construction

Le modle de conception de lusine doit prvoir des vnements pour permettre aux
clients de senregistrer pour signaler leur intrt connatre les changements dtat
de lusine. Cela permet la GUI dinformer le modle de changements que
lutilisateur effectue. Supposez que vous vouliez que le factory laisse les clients
senregistrer pour connatre les vnements dajout et de dplacement de machine.
La Figure 19.4 illustre une conception pour une classe FactoryModel.

Stack

FactoryModel
-mementos:Stack
-listeners:ArrayList

ArrayList

add(loc:Point)
drag(oldLoc:Point,newLoc:Point)
getLocations:List
canUndo:boolean
undo()
notifyListeners()
addChangeListener(:ChangeListener)

Figure 19.4
La classe FactoryModel conserve une pile de configurations matrielles et permet aux clients
de senregistrer pour tre notifis des changements intervenant dans lusine.

La conception de la Figure 19.4 prvoit que la classe FactoryModel donne aux


clients la possibilit de senregistrer pour tre notifis de plusieurs vnements.
Par exemple, considrez lvnement dajout dune machine. Tout objet ChangeListener enregistr sera notifi de ce changement :
package com.oozinoz.visualization;
// ...
public class FactoryModel {
private Stack mementos;
private ArrayList listeners = new ArrayList();
public FactoryModel() {

pattern Livre Page 193 Vendredi, 9. octobre 2009 10:31 10

Chapitre 19

MEMENTO

193

mementos = new Stack();


mementos.push(new ArrayList());
}
//...
}

Le constructeur dbute la configuration initiale de lusine sous forme dune liste


vierge. Les autres mthodes de la classe grent la pile des mmentos et dclenchent
les vnements qui correspondent tout changement. Par exemple, pour ajouter une
machine la configuration actuelle, un client peut appeler la mthode suivante :
public void add(Point location) {
List oldLocs = (List) mementos.peek();
List newLocs = new ArrayList(oldLocs);
newLocs.add(0, location);
mementos.push(newLocs);
notifyListeners();
}

Ce code cre une nouvelle liste demplacements des machines et la place sur la pile
gre par le modle. Une subtilit du code est de sassurer que la nouvelle machine
soit dabord dans cette liste. Cest un signe pour la visualisation quelle doit alors
apparatre devant les autres machines que laffichage pourrait faire se chevaucher.
Un client qui senregistre pour recevoir les notifications de changements pourrait
actualiser la vue de son modle en se reconstruisant lui-mme entirement la
rception dun vnement de la part du modle. La configuration la plus rcente du
modle est toujours disponible dans getLocations(), dont le code se prsente
ainsi :
public List getLocations() {
return (List) mementos.peek();
}

La mthode undo() de la classe FactoryModel permet un client de changer le


modle de positionnement de machines pour restituer une version prcdente. Lorsque
ce code sexcute, il invoque aussi notifyListeners().
Exercice 19.1
Ecrivez le code de la mthode undo() de la classe FactoryModel.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.

pattern Livre Page 194 Vendredi, 9. octobre 2009 10:31 10

194

Partie III

Patterns de construction

Un client intress peut fournir une fonctionnalit dannulation doprations en


enregistrant un listener et en fournissant une mthode qui reconstruit la vue du
modle. La classe Visualization est un client de ce type.
La conception MVC illustre la Figure 19.3 spare les tches dinterprtation des
actions de lutilisateur de celles de gestion de la GUI. La classe Visualization
cre ses composants de GUI mais fait passer la gestion des vnements de GUI un
mdiateur. La classe VisMediator traduit les vnements de GUI en changements appropris dans le modle. Lorsque celui-ci change, la GUI peut ncessiter une actualisation. La classe Visualization senregistre pour recevoir les
notifications fournies par la classe FactoryModel. Notez la sparation des responsabilits.
m

La visualisation change les vnements du modle en changements de la GUI.

Le mdiateur traduit les vnements de GUI en changements du modle.

La Figure 19.5 illustre en dtail les trois classes qui collaborent.


Figure 19.5
Le mdiateur traduit
les vnements de
GUI en changements
du modle, et la
visualisation ragit
aux vnements
de changements
du modle pour
actualiser la GUI.

Visualization

addButton()

VisMediator

buttonPanel()
createPictureBox()
addAction()

machinePanel()
undoButton()

mouseDownAction()

main()

mouseMotionAction()

stateChanged()

undoAction()

FactoryModel

Supposez que, pendant le dplacement dune image de machine, un utilisateur la


dpose accidentellement au mauvais endroit et clique sur le bouton Undo. Pour
pouvoir grer ce clic, la visualisation enregistre le mdiateur pour quil reoive les

pattern Livre Page 195 Vendredi, 9. octobre 2009 10:31 10

Chapitre 19

MEMENTO

195

notifications dvnements de bouton. Le code du bouton Undo dans la classe


Visualization se prsente comme suit :
protected JButton undoButton() {
if (undoButton == null) {
undoButton = ui.createButtonCancel();
undoButton.setText("Undo");
undoButton.setEnabled(false);
undoButton.addActionListener(mediator.undoAction());
}
return undoButton;
}

Ce code dlgue au mdiateur la responsabilit de la gestion dun clic. Le mdiateur


informe le modle de tout changement demand et traduit une requte dannulation
dopration en un changement du modle au moyen du code suivant :
private void undo(ActionEvent e) {
factoryModel.undo();
}

La variable factoryModel dans cette mthode est une instance de FactoryModel,


que la classe Visualization cre et passe au mdiateur via le constructeur de la
classe VisMediator. Nous avons dj examin la commande pop() de la classe
FactoryModel. Le flux de messages qui est gnr lorsque lutilisateur clique sur
Undo est prsent Figure 19.6.

:FactoryModel

:JButton

:VisMediator

:Visualization

undo()
undo()
stateChanged()

Figure 19.6
Le diagramme illustre les messages gnrs suite lactivation du bouton Undo.

pattern Livre Page 196 Vendredi, 9. octobre 2009 10:31 10

196

Partie III

Patterns de construction

Lorsque la classe FactoryModel prlve de la pile la configuration prcdente


quelle a stocke en tant que mmento, la mthode undo() notifie les ChangeListeners. La classe Visualization a prvu son enregistrement cet effet dans
son constructeur :
public Visualization(UI ui) {
super(new BorderLayout());
this.ui = ui;
mediator = new VisMediator(factoryModel);
factoryModel.addChangeListener(this);
add(machinePanel(), BorderLayout.CENTER);
add(buttonPanel(), BorderLayout.SOUTH);
}

Pour chaque position de machine dans le modle, la visualisation conserve un objet


Component quil cre avec la mthode createPictureBox(). La mthode stateChanged() doit nettoyer tous les composants en place dans le panneau et rtablir
les encadrs des positions restaures. La mthode stateChanged() doit aussi
dsactiver le bouton Undo sil ne reste quun mmento sur la pile.
Exercice 19.2
Ecrivez la mthode stateChanged() pour la classe Visualization.

Le pattern MEMENTO permet de sauvegarder et de restaurer ltat dun objet. Une


application courante de ce pattern est la gestion de la fonctionnalit dannulation
doprations dans les applications. Dans certaines applications, comme dans
lexemple de visualisation des machines de lusine, lentrept o stocker les informations sauvegardes peut tre un autre objet. Dans dautres cas, les mmentos
peuvent tre stocks sous une forme plus durable.

Dure de vie des mmentos


Un mmento est un petit entrept qui conserve ltat dun objet. Vous pouvez crer
un mmento en utilisant un autre objet, une chane ou un fichier. La dure anticipe
entre le stockage et la reconstruction dun objet a un impact sur la stratgie que
vous utilisez dans la conception dun mmento. Il peut sagir dun court instant,
mais aussi dheures, de jours ou dannes.

pattern Livre Page 197 Vendredi, 9. octobre 2009 10:31 10

Chapitre 19

MEMENTO

197

Exercice 19.3
Indiquez deux raisons qui peuvent motiver lenregistrement dun mmento dans
un fichier plutt que sous forme dobjet.

Persistance des mmentos entre les sessions


Une session se produit lorsquun utilisateur excute un programme, ralise des transactions par son intermdiaire, puis le quitte. Supposez que vos utilisateurs souhaitent pouvoir sauvegarder une simulation dune session et la restaurer dans une autre
session. Cette fonctionnalit est un concept normalement appel stockage persistant.
Le stockage persistant satisfait lobjectif du pattern MEMENTO et constitue une extension
naturelle de la fonctionnalit dannulation que nous avons dj implmente.
Supposez que vous driviez une sous-classe Visualization2 de la classe Visualization, qui possde une barre de menus avec un menu File comportant les
options Save As et Restore From :
package com.oozinoz.visualization;
import javax.swing.*;
import com.oozinoz.ui.SwingFacade;
import com.oozinoz.ui.UI;
public class Visualization2 extends Visualization {
public Visualization2(UI ui) {
super(ui);
}
public JMenuBar menus() {
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("File");
menuBar.add(menu);
JMenuItem menuItem = new JMenuItem("Save As...");
menuItem.addActionListener(mediator.saveAction());
menu.add(menuItem);
menuItem = new JMenuItem("Restore From...");
menuItem.addActionListener(
mediator.restoreAction());
menu.add(menuItem);
return menuBar;
}

pattern Livre Page 198 Vendredi, 9. octobre 2009 10:31 10

198

Partie III

Patterns de construction

public static void main(String[] args) {


Visualization2 panel = new Visualization2(UI.NORMAL);
JFrame frame = SwingFacade.launch(
panel, "Operational Model");
frame.setJMenuBar(panel.menus());
frame.setVisible(true);
}
}

Ce code requiert lajout des mthodes saveAction() et restoreAction() la


classe VisMediator. Les objets MenuItem provoquent lappel de ces actions lorsque le menu est slectionn. Lorsque la classe Visualization2 sexcute, la GUI
se prsente comme illustr Figure 19.7.
Figure 19.7
Lajout dun menu
permet lutilisateur
denregistrer un
mmento que lapplication pourra restaurer
lors dune prochaine
session.

Un moyen facile de stocker un objet, telle la configuration du modle de visualisation, est de le srialiser. Le code de la mthode saveAction() dans la classe
VisMediator pourrait tre comme suit :
public ActionListener saveAction() {
return new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
VisMediator.this.save((Component)e.getSource());

pattern Livre Page 199 Vendredi, 9. octobre 2009 10:31 10

Chapitre 19

MEMENTO

199

} catch (Exception ex) {


System.out.println(
"Echec de sauvegarde : " + ex.getMessage());
}
}};
}
public void save(Component source) throws Exception {
JFileChooser dialog = new JFileChooser();
dialog.showSaveDialog(source);
if (dialog.getSelectedFile() == null)
return;
FileOutputStream out = null;
ObjectOutputStream s = null;
try {
out = new FileOutputStream(dialog.getSelectedFile());
s = new ObjectOutputStream(out);
s.writeObject(factoryModel.getLocations());
} finally {
if (s != null) s.close();
}
}

Exercice 19.4
Ecrivez le code de la mthode restoreAction() de la classe VisMediator.

Louvrage Design Patterns dcrit ainsi lobjectif du pattern MEMENTO: "Sans


enfreindre les rgles dencapsulation, il capture et externalise ltat interne dun
objet afin de pouvoir le restaurer ultrieurement."
Exercice 19.5
Dans ce cas, nous avons utilis la srialisation Java pour enregistrer la configuration dans un fichier au format binaire. Supposez que nous layons enregistr dans
le format XML (texte). Expliquez brivement pourquoi, votre avis, lenregistrement dun mmento au format texte serait une atteinte la rgle dencapsulation.

pattern Livre Page 200 Vendredi, 9. octobre 2009 10:31 10

200

Partie III

Patterns de construction

Vous devriez comprendre ce quun dveloppeur signifie lorsquil indique quil cre
des mmentos en stockant les donnes dobjets au moyen de la srialisation ou de
lenregistrement dans un fichier XML. Cest lide des patterns de conception : en
utilisant un vocabulaire commun, nous pouvons discuter de concepts de conception
et de leurs applications.

Rsum
Le pattern MEMENTO permet de capturer ltat dun objet de manire pouvoir le
restaurer ultrieurement. La mthode de stockage utilise cet effet dpend du type
de restauration faire, aprs un clic ou une frappe au clavier ou lors dune
prochaine session aprs un certain temps. La raison la plus courante de raliser cela
est toutefois de supporter la fonction dannulation dactions prcdentes dans une
application. Dans ce cas, vous pouvez stocker ltat dun objet dans un autre objet.
Pour que le stockage de ltat soit persistant, vous pouvez utiliser, entre autres
moyens, la srialisation dobjet.

pattern Livre Page 201 Vendredi, 9. octobre 2009 10:31 10

IV
Patterns dopration

pattern Livre Page 202 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 203 Vendredi, 9. octobre 2009 10:31 10

20
Introduction aux oprations
Lorsque vous crivez une mthode Java, vous produisez une unit fondamentale de
traitement qui intervient un niveau au-dessus de celui de lcriture dune instruction. Vos mthodes doivent participer une conception, une architecture et un plan
de test densemble, et en mme temps lcriture de mthodes est au cur de la
POO. En dpit de ce rle central, une certaine confusion rgne quant ce que les
mthodes sont vraiment et comment elles fonctionnent, qui vient probablement du
fait que les dveloppeurs et les auteurs ont tendance utiliser de faon interchangeable les termes mthode et opration. De plus, les concepts dalgorithme et
de polymorphisme, bien que plus abstraits que les mthodes, sont au final mis en
uvre par elles.
Une dfinition claire des termes algorithme, polymorphisme, mthode et opration
vous aidera comprendre plusieurs patterns de conception. En particulier, STATE,
STRATEGY et INTERPRETER fonctionnent tous trois en implmentant une opration
dans des mthodes travers plusieurs classes, mais une telle observation na
dutilit que si nous nous entendons sur le sens de mthode et dopration.

Oprations et mthodes
Parmi les nombreux termes relatifs au traitement quune classe peut tre amene
effectuer, il est particulirement utile de distinguer une opration dune mthode.
Le langage UML dfinit cette diffrence comme suit :
m

Une opration est la spcification dun service qui peut tre demand par une
instance dune classe.
Une mthode est limplmentation dune opration.

pattern Livre Page 204 Vendredi, 9. octobre 2009 10:31 10

204

Partie IV

Patterns dopration

Notez que la signification dopration se situe un niveau dabstraction au-dessus de


la notion de mthode.
Une opration spcifie quelque chose quune classe accomplit et spcifie linterface
pour appeler ce service. Plusieurs classes peuvent implmenter la mme opration de
diffrentes manires. Par exemple, nombre de classes implmentent lopration
toString() chacune sa faon. Chaque classe qui implmente une opration utilise
pour cela une mthode. Cette mthode contient ou est le code qui permet
lopration de fonctionner pour cette classe.
Les dfinitions de mthode et opration permettent de clarifier la structure de
nombreux patterns de conception. Etant donn que ces patterns se situent un niveau
au-dessus des classes et des mthodes, il nest pas surprenant que les oprations
soient prdominantes dans de nombreux patterns. Par exemple, COMPOSITE permet
dappliquer des oprations la fois des lments et des groupes, et PROXY
permet un intermdiaire implmentant les mmes oprations quun objet cible de
sinterposer pour grer laccs cet objet.
Exercice 20.1
Utilisez les termes opration et mthode pour expliquer comment le pattern
CHAIN OF RESPONSIBILITY implmente une opration.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.

Dans Java, la dclaration dune mthode inclut un en-tte (header) et un corps


(body). Le corps est la srie dinstructions qui peuvent tre excutes en invoquant
la signature de la mthode. Len-tte inclut le type de retour et la signature de la
mthode et peut aussi inclure des modificateurs et une clause throws. Voici le
format de len-tte dune mthode :
modificateurs type-retour signature clause-throws
Exercice 20.2
Parmi les neuf modificateurs de mthodes Java, numrez tous ceux que vous
pouvez.

pattern Livre Page 205 Vendredi, 9. octobre 2009 10:31 10

Chapitre 20

Introduction aux oprations

205

Signatures
En surface, la signification dopration est semblable celle de signature, ces
deux termes dsignant linterface dune mthode. Lorsque vous crivez une
mthode, elle devient invocable conformment sa signature. La Section 8.4.2 de
louvrage JavaTM Language Specification [Arnold et Gosling 1998] donne la dfinition
suivante dune signature :
La signature dune mthode est constitue du nom de la mthode ainsi que du
nombre et des types de paramtres formels quelle reoit.
Notez que la signature dune mthode ninclut pas son type de retour. Toutefois, si
la dclaration dune mthode remplace la dclaration dune autre mthode, une
erreur de compilation surviendra si elles possdent des types de retour diffrents.
Exercice 20.3
La mthode Bitmap.clone() retourne toujours une instance de la classe Bitmap
bien que son type de retour soit Object. Serait-elle compile sans erreur si son
type de retour tait Bitmap?

Une signature spcifie quelle mthode est invoque lorsquun client effectue un
appel. Une opration est la spcification dun service qui peut tre demand. Les
termes signature et opration ont une signification analogue, bien quils ne soient
pas synonymes. La diffrence a trait principalement au contexte dans lequel ils
sappliquent. Le terme opration est employ en rapport avec lide que des
mthodes de diffrentes classes peuvent avoir la mme interface. Le terme signature est employ en rapport avec les rgles qui dterminent comment Java fait
correspondre lappel dune mthode une mthode de lobjet rcepteur. Une signature dpend du nom et des paramtres dune mthode mais pas du type de retour de
celle-ci.

Exceptions
Dans son livre La maladie comme mtaphore, Susan Sontag observe ceci : "En
naissant, nous acqurons une double nationalit qui relve du royaume des bienportants comme de celui des malades." Cette mtaphore peut aussi sappliquer aux
mthodes : au lieu de se terminer normalement, une mthode peut gnrer une

pattern Livre Page 206 Vendredi, 9. octobre 2009 10:31 10

206

Partie IV

Patterns dopration

exception ou invoquer une autre mthode pour cela. Lorsquune mthode se


termine normalement, le contrle du programme revient au point situ juste aprs
lappel. Un autre ensemble de rgles sappliquent dans le royaume des exceptions.
Lorsquune exception est gnre, lenvironnement dexcution Java doit trouver
une instruction try/catch correspondante. Cette instruction peut exister dans la
mthode qui a gnr lexception, dans la mthode qui a appel la mthode
courante, ou dans la mthode qui a appel la mthode prcdente, et ainsi de suite
en remontant la pile dappels. En labsence dinstruction try/catch correspondante,
le programme plante.
Nimporte quelle mthode peut gnrer une exception en utilisant une instruction
throw. Par exemple :
throw new Exception("Bonne chance !");

Si votre application utilise une mthode qui gnre une exception que vous navez
pas prvue, cela peut la faire planter. Pour viter ce genre de comportement, vous
devez disposer dun plan architectural qui spcifie les points dans votre application
o les exceptions sont interceptes et gres de faon approprie. Vous pensez
probablement quil nest pas trs commode davoir dclarer lventualit dune
exception. Dans C#, par exemple, les mthodes nont pas besoin de dclarer des
exceptions. Dans C++, une exception peut apparatre sans que le compilateur doive
vrifier quelle a t prvue par les appelants.
Exercice 20.4
Contrairement Java, C# nimpose pas aux mthodes de dclarer les exceptions
quelles peuvent tre amenes gnrer. Pensez-vous que Java constitue une
amlioration cet gard ? Expliquez votre rponse.

Algorithmes et polymorphisme
Les algorithmes et le polymorphisme sont deux concepts importants en programmation, mais il peut tre difficile de donner une explication de ces termes. Si vous
voulez montrer quelquun une mthode, vous pouvez modifier le code source
dune classe en mettant en vidence les lignes de code appropries. Parfois, un
algorithme peut exister entirement dans une mthode, mais il sappuie le plus
souvent sur linteraction de plusieurs mthodes. Louvrage Introduction to

pattern Livre Page 207 Vendredi, 9. octobre 2009 10:31 10

Chapitre 20

Introduction aux oprations

207

Algorithms (Introduction lalgorithmique) [Cormen, Leiserson, et Rivest 1990,


p. 1] affirme ceci :
Un algorithme est une procdure de calcul bien dfinie qui reoit une ou
plusieurs valeurs en entre et produit une ou plusieurs valeurs en sortie.
Un algorithme est une procdure, cest--dire une squence dinstructions, qui
accepte une entre et produit un rsultat. Comme il a t dit, une seule mthode peut
constituer un algorithme : elle accepte une entre sa liste de paramtres et
produit sa valeur de retour en sortie. Toutefois, nombre dalgorithmes requirent
plusieurs mthodes pour sexcuter dans un programme orient objet. Par exemple,
lalgorithme isTree() du Chapitre 5, consacr au pattern COMPOSITE, ncessite
quatre mthodes, comme le montre la Figure 20.1.
Figure 20.1
MachineComponent

Quatre mthodes
isTree() forment
lalgorithme et collaborent pour dterminer si une instance
de MachineComponent
est un arbre.

isTree():bool
isTree(:Set):bool

Machine

isTree(:Set):bool

MachineComposite

isTree(:Set):bool

Exercice 20.5
Combien dalgorithmes, doprations et de mthodes la Figure 20.1 comprendelle ?

Un algorithme ralise un traitement. Il peut tre contenu dans une seule mthode ou
bien ncessiter de nombreuses mthodes. En POO, les algorithmes qui requirent
plusieurs mthodes sappuient souvent sur le polymorphisme pour autoriser

pattern Livre Page 208 Vendredi, 9. octobre 2009 10:31 10

208

Partie IV

Patterns dopration

plusieurs implmentations dune mme opration. Le polymorphisme est le principe selon lequel la mthode appele dpend la fois de lopration invoque et de
la classe du rcepteur de lappel. Par exemple, vous pourriez vous demander quelle
mthode est excute lorsque Java rencontre lexpression m.isTree(). Cela
dpend. Si m est une instance de Machine, Java invoquera Machine.isTree(). Sil
sagit dune instance de MachineComposite, il invoquera MachineComposite.isTree(). De manire informelle, le polymorphisme signifie que la mthode
approprie sera invoque pour le type dobjet appropri. Nombre de patterns
emploient le principe de polymorphisme, lequel est parfois directement li
lobjectif du pattern.

Rsum
Mme si les termes opration, mthode, signature et algorithme semblent souvent
avoir une signification proche, prserver leur distinction facilite la description de
concepts importants. A linstar dune signature, une opration est la spcification
dun service. Le terme opration est employ en rapport avec lide que de
nombreuses mthodes peuvent avoir la mme interface. Le terme signature est
employ en rapport avec les rgles de recherche de la mthode approprie. La dfinition dune mthode inclut sa signature, cest--dire son nom et sa liste de paramtres, ainsi que des modificateurs, un type de retour et le corps de la mthode. Une
mthode possde une signature et implmente une opration.
La voie normale dinvocation dune mthode consiste lappeler. Une mthode doit
normalement se terminer en retournant une valeur, mais interrompra son excution
si elle rencontre une exception non gre.
Un algorithme est une procdure qui accepte une entre et produit un rsultat. Une
mthode accepte elle aussi une entre et produit un rsultat, et comme elle contient
un corps procdural, certains auteurs qualifient ce corps dalgorithme. Mais tant
donn que la procdure algorithmique peut faire intervenir de nombreuses oprations et mthodes, ou peut exister au sein dune mme mthode, il est plus correct
de rserver le terme algorithme pour dsigner une procdure produisant un rsultat.
Beaucoup de patterns de conception impliquent la distribution dune opration
travers plusieurs classes. On peut galement dire que ces patterns sappuient sur le
polymorphisme, principe selon lequel la slection dune mthode dpend de la
classe de lobjet qui reoit lappel.

pattern Livre Page 209 Vendredi, 9. octobre 2009 10:31 10

Chapitre 20

Introduction aux oprations

209

Au-del des oprations ordinaires


Diffrentes classes peuvent implmenter une opration de diffrentes manires.
Autrement dit, Java supporte le polymorphisme. La puissance de ce concept pourtant
simple apparat dans plusieurs patterns de conception.
Si vous envisagez de

Appliquez le pattern

Implmenter un algorithme dans une mthode, remettant plus tard


la dfinition de certaines tapes de lalgorithme pour permettre des
sous-classes de les redfinir

TEMPLATE METHOD

Distribuer une opration afin que chaque classe reprsente un tat


diffrent

STATE

Encapsuler une opration, rendant les implmentations interchangeables

STRATEGY

Encapsuler un appel de mthode dans un objet

COMMAND

Distribuer une opration de faon que chaque implmentation


sapplique un type diffrent de composition

INTERPRETER

Les patterns dopration conviennent dans des contextes o vous avez besoin de
plusieurs mthodes, gnralement avec la mme signature, pour participer une
conception. Par exemple, le pattern TEMPLATE METHOD permet des sous-classes
dimplmenter des mthodes qui ajustent leffet dune procdure dfinie dans une
super-classe.

pattern Livre Page 210 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 211 Vendredi, 9. octobre 2009 10:31 10

21
TEMPLATE METHOD
Les mthodes ordinaires ont un corps qui dfinit une squence dinstructions. Il est
galement assez commun pour une mthode dinvoquer des mthodes de lobjet
courant et dautres objets. Dans ce sens, les mthodes ordinaires sont des "modles"
(template) qui exposent une srie dinstructions que lordinateur doit suivre.
Le pattern TEMPLATE METHOD implique un type plus spcifique de modle.
Lorsque vous crivez une mthode, vous pouvez vouloir dfinir la structure gnrale dun algorithme tout en laissant la possibilit dimplmenter diffremment
certaines tapes. Dans ce cas, vous pouvez dfinir la mthode mais faire de ces
tapes des mthodes abstraites, des mthodes stub, ou des mthodes dfinies dans
une interface spare. Cela produit un modle plus rigide qui dfinit spcifiquement
quelles tapes dun algorithme peuvent ou doivent tre fournies par dautres
classes.
Lobjectif du pattern TEMPLATE METHOD est dimplmenter un algorithme dans
une mthode, laissant dautres classes le soin de dfinir certaines tapes de
lalgorithme.

Un exemple classique : algorithme de tri


Les algorithmes de tri ne datent pas dhier et sont hautement rutilisables. Imaginez
quun homme prhistorique ait labor une mthode pour trier des flches en fonction du degr dafftage de leur tte. La mthode consiste aligner les flches puis
effectuer une srie de permutations gauche-droite, remplaant chaque flche par

pattern Livre Page 212 Vendredi, 9. octobre 2009 10:31 10

212

Partie IV

Patterns dopration

une flche plus affte situe sur sa gauche. Cet homme pourrait ensuite appliquer
la mme mthode pour trier les flches selon leur porte ou tout autre critre.
Les algorithmes de tri varient en termes dapproche et de rapidit, mais tous se
fondent sur le principe primitif de comparaison de deux lments ou attributs. Si
vous disposez dun algorithme de tri et pouvez comparer un certain attribut de deux
lments, lalgorithme vous permettra dobtenir une collection dlments tris
daprs cet attribut.
Le tri est un exemple de TEMPLATE METHOD. Il sagit dune procdure qui nous
permet de modifier une tape critique, savoir la comparaison de deux objets, afin
de pouvoir rutiliser lalgorithme pour divers attributs de diffrentes collections
dobjets.
A notre poque, le tri est probablement lalgorithme le plus frquemment rimplment, le nombre dimplmentations dpassant vraisemblablement le nombre de
programmeurs. Mais moins davoir trier une norme collection, vous navez
gnralement pas besoin dcrire votre propre algorithme.
Les classes Arrays et Collections fournissent une mthode sort() statique qui
reoit comme argument un tableau trier ainsi quun Comparator optionnel. La
mthode sort() de la classe ArrayList est une mthode dinstance qui dtermine
le rcepteur du message sort(). A un autre gard, ces mthodes partagent une
stratgie commune qui dpend des interfaces Comparable et Comparator, comme
illustr Figure 21.1.
Les mthodes sort() des classes Arrays et Collections vous permettent de fournir une instance de linterface Comparator si vous le souhaitez. Si vous employez
une mthode sort() sans fournir une telle instance, elle sappuiera sur la mthode
compareTo() de linterface Comparable. Une exception surviendra si vous tentez
de trier des lments sans fournir une instance de Comparator et que ces lments
nimplmentent pas linterface Comparable. Mais notez que les types les plus rudimentaires, tels que String, implmentent Comparable.
Les mthodes sort() reprsentent un exemple de TEMPLATE METHOD. Les bibliothques de classes incluent un algorithme qui vous permet de fournir une tape critique :
la comparaison de deux lments. La mthode compare() retourne un nombre infrieur, gal, ou suprieur 0. Ces valeurs correspondent lide que, dans le sens
que vous dfinissez, lobjet o1 est infrieur, gal, ou suprieur lobjet o2. Par
exemple, le code suivant trie une collection de fuses en fonction de leur apoge

pattern Livre Page 213 Vendredi, 9. octobre 2009 10:31 10

Chapitre 21

TEMPLATE METHOD

213

Figure 21.1
La mthode sort()
de la classe Collections
utilise les interfaces
prsentes ici.

java.util
interface
Comparator

compare(o1:Object,o2:Object):int

Collections

sort(l: List, c:Comparator)

java.lang
interface
Comparable

compareTo(obj:Object):int

puis de leur nom (le constructeur de Rocket reoit le nom, la masse, le prix,
lapoge et la pousse de la fuse) :
package app.templateMethod;
import java.util.Arrays;
import com.oozinoz.firework.Rocket;
import com.oozinoz.utility.Dollars;
public class ShowComparator {
public static void main(String args[]) {
Rocket r1 = new Rocket(
"Sock-it", 0.8, new Dollars(11.95), 320, 25);
Rocket r2 = new Rocket(
"Sprocket", 1.5, new Dollars(22.95), 270, 40);
Rocket r3 = new Rocket(
"Mach-it", 1.1, new Dollars(22.95), 1000, 70);

pattern Livre Page 214 Vendredi, 9. octobre 2009 10:31 10

214

Partie IV

Patterns dopration

Rocket r4 = new Rocket(


"Pocket", 0.3, new Dollars(4.95), 150, 20);
Rocket[] rockets = new Rocket[] { r1, r2, r3, r4 };
System.out.println("Tries par apoge : ");
Arrays.sort(rockets, new ApogeeComparator());
for (int i = 0; i < rockets.length; i++)
System.out.println(rockets[i]);
System.out.println();
System.out.println("Tries par nom : ");
Arrays.sort(rockets, new NameComparator());
for (int i = 0; i < rockets.length; i++)
System.out.println(rockets[i]);
}
}

Voici le comparateur ApogeeComparator:


package app.templateMethod;
import java.util.Comparator;
import com.oozinoz.firework.Rocket;
public class ApogeeComparator implements Comparator {
// Exercice !
}

Voici le comparateur NameComparator:


package app.templateMethod;
import java.util.Comparator;
import com.oozinoz.firework.Rocket;
public class NameComparator implements Comparator {
// Exercice !
}

Laffichage du programme dpend de la faon dont Rocket implmente


toString() mais montre les fuses tries des deux manires :
Tries par apoge :
Pocket
Sprocket
Sock-it
Mach-it

pattern Livre Page 215 Vendredi, 9. octobre 2009 10:31 10

Chapitre 21

TEMPLATE METHOD

215

Tries par nom :


Mach-it
Pocket
Sock-it
Sprocket

Exercice 21.1
Ecrivez le code manquant dans les classes ApogeeComparator et NameComparator pour que le programme puisse trier correctement une collection de
fuses.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.

Le tri est un algorithme gnral qui, lexception dune tape, na rien voir avec
les spcificits de votre domaine ou application. Cette tape critique est la comparaison dlments. Aucun algorithme ninclut, par exemple, dtape pour comparer
les apoges de deux fuses. Votre application doit donc fournir cette tape. Les
mthodes sort() et linterface Comparator vous permettent dinsrer une tape
spcifique dans un algorithme de tri gnral.
TEMPLATE METHOD ne se limite pas aux cas o seule ltape manquante est propre
un domaine. Parfois, lalgorithme entier sapplique un domaine particulier.

Compltion dun algorithme


Les patterns TEMPLATE METHOD et ADAPTER sont semblables en ce quils permettent
tous deux un dveloppeur de simplifier et de spcifier la faon dont le code dun
autre dveloppeur complte une conception. Dans ADAPTER, un dveloppeur peut
spcifier linterface dun objet requis par la conception, et un autre peut crer un
objet qui fournit linterface attendue mais en utilisant les services dune classe existante possdant une interface diffrente. Dans TEMPLATE METHOD, un dveloppeur
peut fournir un algorithme gnral, et un autre fournir une tape essentielle de
lalgorithme. Considrez la presse toiles de la Figure 21.2.
La presse toiles fabrique par la socit Aster Corporation accepte des moules en
mtal vides et presse dedans des toiles de feu dartifice. La machine possde des
trmies qui dispensent les produits chimiques quelle mlange en une pte et presse
dans les moules. Lorsquelle sarrte, elle interrompt son traitement du moule qui

pattern Livre Page 216 Vendredi, 9. octobre 2009 10:31 10

216

Partie IV

Patterns dopration

Figure 21.2
Une presse toiles
Aster possde des
tapis dentre et de
sortie qui transportent les moules.
Oozinoz ajoute un
tapis de rcupration
qui collecte la pte
dcharge.

Tapis de rcupration
Dchargement
de la pte

Zone de travail
Tapis
d'entre

Tapis
de sortie

se trouve dans la zone de travail, et transfre tous les moules de son tapis dentre
vers son tapis de sortie sans les traiter. Elle dcharge ensuite son stock de pte et
rince grande eau sa zone de travail. Elle orchestre toute cette activit en utilisant
un ordinateur de bord et la classe AsterStarPress illustre Figure 21.3.
Figure 21.3
Les presses toiles
Aster utilisent une
classe abstraite dont
vous devez driver
une sous-classe pour
pouvoir vous en
servir chez Oozinoz.

Code Aster

AsterStarPress

dischargePaste()
flush()

Code Oozinoz

OzAsterStarPress

markMoldIncomplete(
moldID:int)

inProcess():bool
markMoldIncomplete(
moldID:int)
shutDown()

MaterialManager

stopProcessing()
usherInputMolds()
getManager():
MaterialManager
setMoldIncomplete(
moldID:int)

pattern Livre Page 217 Vendredi, 9. octobre 2009 10:31 10

Chapitre 21

TEMPLATE METHOD

217

La presse toiles Aster est intelligente et indpendante, et a t conue pour


pouvoir oprer dans une unit de production intelligente avec laquelle elle doit
communiquer. Par exemple, la mthode shutDown() notifie lunit de production
lorsque le moule en cours de traitement est incomplet :
public void shutdown() {
if (inProcess()) {
stopProcessing();
markMoldIncomplete(currentMoldID);
}
usherInputMolds();
dischargePaste();
flush();
}

La mthode markMoldIncomplete() et la classe AsterStarPress sont abstraites.


Chez Oozinoz, vous devez crer une sous-classe qui implmente la mthode requise
et charger ce code dans lordinateur de la presse. Vous pouvez implmenter markMoldIncomplete() en passant les informations concernant le moule incomplet au
singleton MaterialManager qui garde trace de ltat matriel.
Exercice 21.2
Ecrivez le code de la mthode markMoldIncomplete() de la classe OzAsterStarPress:
public class OzAsterStarPress extends AsterStarPress {
public void markMoldIncomplete(int id) {
// Exercice !
}
}

Les concepteurs de la presse toiles Aster Star connaissent parfaitement le fonctionnement des units de production pyrotechniques et ont implment la communication avec lunit aux points de traitement appropris. Il se peut nanmoins que
vous ayez besoin dtablir la communication un point que ces dveloppeurs ont
omis.

pattern Livre Page 218 Vendredi, 9. octobre 2009 10:31 10

218

Partie IV

Patterns dopration

Hooks
Un hook est un appel de mthode plac par un dveloppeur un point spcifique
dune procdure pour permettre dautres dveloppeurs dy insrer du code. Lorsque vous adaptez le code dun autre dveloppeur et avez besoin de disposer dun
contrle un certain point auquel vous navez pas accs, vous pouvez demander un
hook. Un dveloppeur serviable insrera un appel de mthode au niveau de ce point
et fournira aussi gnralement une version stub de la mthode hook pour viter
dautres clients de devoir la remplacer.
Considrez la presse Aster qui dcharge sa pte chimique et rince abondamment sa
zone de travail lorsquelle sarrte. Elle doit dcharger la pte pour empcher que
celle-ci ne sche et bloque la machine. Chez Oozinoz, vous rcuprez la pte et la
dcoupez en ds qui serviront de petites toiles dans des chandelles romaines
(une chandelle romaine est un tube stationnaire qui contient un mlange de charges explosives et dtoiles). Une fois la pte dcharge, vous faites en sorte quun
robot la place sur un tapis spar, comme illustr Figure 21.2. Il importe de
procder au dchargement avant que la machine ne lave sa zone de travail. Le
problme est que vous voulez prendre le contrle entre les deux instructions de la
mthode shutdown():
dischargePaste();
flush();

Vous pourriez remplacer dischargePaste() par une mthode qui ajoute un appel
pour collecter la pte :
public void dischargePaste() {
super.dischargePaste();
getFactory().collectPaste();
}

Cette mthode insre une tape aprs le dchargement de la pte. Cette tape utilise
un singleton Factory pour collecter la pte. Lorsque la mthode shutdown()
sexcutera, le robot recueillera la pte dcharge avant que la presse ne soit rince.
Malheureusement, le code de dischargePaste() introduit un risque. Les dveloppeurs de chez Aster ne sauront certainement pas que vous avez dfini ainsi cette
mthode. Sils modifient leur code pour dcharger la pte un moment o vous ne
voulez pas la collecter, une erreur surviendra.

pattern Livre Page 219 Vendredi, 9. octobre 2009 10:31 10

Chapitre 21

TEMPLATE METHOD

219

Les dveloppeurs cherchent gnralement rsoudre les problmes en crivant du


code. Mais ici, il sagit de rsoudre un problme potentiel en communiquant les uns
avec les autres.
Exercice 21.3
Rdigez une note lattention des dveloppeurs de chez Aster leur demandant
dintroduire un changement qui vous permettra de collecter la pte dcharge en
toute scurit avant que la machine ne rince sa zone de travail.

Ltape fournie par une sous-classe dans TEMPLATE METHOD peut tre ncessaire
pour complter lalgorithme ou peut reprsenter une tape optionnelle qui sinsre
dans le code dune sous-classe, souvent la demande dun autre dveloppeur. Bien
que lobjectif de ce pattern soit de laisser une classe spare dfinir une partie dun
algorithme, vous pouvez aussi lappliquer lorsque vous refactorisez un algorithme
apparaissant dans plusieurs mthodes.

Refactorisation pour appliquer TEMPLATE METHOD


Lorsque TEMPLATE METHOD est appliqu, vous trouverez des hirarchies de classes
o une super-classe fournit la structure gnrale dun algorithme et o des sousclasses en fournissent certaines tapes. Vous pouvez adopter cette approche, refactorisant en vue dappliquer TEMPLATE METHOD, lorsque vous trouvez des algorithmes similaires dans des mthodes diffrentes (refactoriser consiste transformer
des programmes en des programmes quivalents mais mieux conus). Considrez
les hirarchies parallles Machine et MachinePlanner introduites au Chapitre 16,
consacr au pattern FACTORY METHOD. Comme le montre la Figure 21.4, la classe
Machine fournit une mthode createPlanner() en tant que FACTORY METHOD qui
retourne une sous-classe approprie de MachinePlanner.
Deux des sous-classes de Machine instancient des sous-classes spcifiques de la
hirarchie MachinePlanner lorsquil leur est demand de crer un planificateur.
Ces classes, ShellAssembler et StarPress, posent un mme problme en ce
quelles ne peuvent crer un MachinePlanner qu la demande.

pattern Livre Page 220 Vendredi, 9. octobre 2009 10:31 10

220

Partie IV

Patterns dopration

Figure 21.4
Un objet Machine
peut crer une
instance approprie
de MachinePlanner
pour lui-mme.

MachinePlanner

Machine

createPlanner():
MachinePlanner

ShellAssembler

MachinePlanner(
m:Machine)

BasicPlanner

ShellPlanner

createPlanner():
MachinePlanner

StarPress

StarPressPlanner

createPlanner():
MachinePlanner

Fuser

Mixer

Si vous examinez le code de ces classes, vous noterez que les sous-classes
emploient des techniques similaires pour procder une initialisation paresseuse
(lazy-initialization) dun planificateur. Par exemple, la classe ShellAssembler
possde une mthode getPlanner() qui initialise un membre planner:
public ShellPlanner getPlanner() {
if (planner == null)
planner = new ShellPlanner(this);
return planner;
}

pattern Livre Page 221 Vendredi, 9. octobre 2009 10:31 10

Chapitre 21

TEMPLATE METHOD

221

Dans la classe ShellPlanner, planner est de type ShellPlanner. La classe StarPress comprend aussi un membre planner mais le dclare comme tant de type
StarPressPlanner. La mthode getPlanner() de la classe StarPress opre
aussi une initialisation paresseuse de lattribut planner:
public StarPressPlanner getPlanner() {
if (planner == null)
planner = new StarPressPlanner(this);
return planner;
}

Les autres sous-classes de Machine adoptent une approche analogue pour crer un
planificateur seulement lorsquil est ncessaire. Cela prsente une opportunit de
refactorisation, vous permettant de nettoyer et de rduire votre code. En supposant
que vous dcidiez dajouter la classe Machine un attribut planner de type MachinePlanner, cela vous permettrait de supprimer cet attribut des sous-classes et
dliminer les mthodes getPlanner() existantes.
Exercice 21.4
Ecrivez le code de la mthode getPlanner() de la classe Machine.

Vous pouvez souvent refactoriser votre code en une instance de TEMPLATE METHOD
en rendant abstraite la structure gnrale de mthodes qui se ressemblent, cest-dire en plaant cette structure dans une super-classe et en laissant aux sous-classes
le soin de fournir ltape qui diffre dans leur implmentation de lalgorithme.

Rsum
Lobjectif de TEMPLATE METHOD est de dfinir un algorithme dans une mthode,
laissant certaines tapes abstraites, non dfinies, ou dfinies dans une interface, de
sorte que dautres classes puissent se charger de les implmenter.
Ce pattern fonctionne comme un contrat entre les dveloppeurs. Un dveloppeur
fournit la structure gnrale dun algorithme, et un autre en fournit une certaine
tape. Il peut sagir dune tape qui complte lalgorithme ou qui sert de hook vous
permettant dinsrer votre code des points spcifiques de la procdure.

pattern Livre Page 222 Vendredi, 9. octobre 2009 10:31 10

222

Partie IV

Patterns dopration

TEMPLATE METHOD nimplique pas que vous deviez crire la mthode modle avant
les sous-classes dimplmentation. Il peut arriver que vous tombiez sur des mthodes
similaires dans une hirarchie existante. Vous pourriez alors en extraire la structure
gnrale dun algorithme et la placer dans une super-classe, appliquant ce pattern
pour simplifier et rorganiser votre code.

pattern Livre Page 223 Vendredi, 9. octobre 2009 10:31 10

22
STATE
Ltat dun objet est une combinaison des valeurs courantes de ses attributs. Lorsque vous appelez une mthode set dun objet ou assignez une valeur lun de ses
champs, vous changez son tat. Les objets modifient souvent aussi eux-mmes leur
tat lorsque leurs mthodes sexcutent.
Le terme tat (state) est parfois employ pour dsigner un attribut changeant dun
objet. Par exemple, nous pourrions dire quune machine est dans un tat actif ou
inactif. Dans un tel cas, la partie changeante de ltat de lobjet est laspect le plus
important de son comportement. En consquence, la logique qui dpend de ltat de
lobjet peut se trouver rpartie dans de nombreuses mthodes de la classe. Une logique semblable ou identique peut ainsi apparatre de nombreuses fois, augmentant le
travail de maintenance du code.
Une faon dviter cet parpillement de la logique dpendant de ltat dun objet est
dintroduire un nouveau groupe de classes, chacune reprsentant un tat diffrent.
Il faut ensuite placer le comportement spcifique un tat dans la classe approprie.
Lobjectif du pattern STATE est de distribuer la logique dpendant de ltat
dun objet travers plusieurs classes qui reprsentent chacune un tat diffrent.

Modlisation dtats
Lorsque vous modlisez un objet dont ltat est important, il se peut quil dispose
dune variable qui garde trace de la faon dont il devrait se comporter, selon son
tat. Cette variable apparat peut-tre dans des instructions if en cascade complexes
qui se concentrent sur la faon de ragir aux vnements expriments par lobjet.

pattern Livre Page 224 Vendredi, 9. octobre 2009 10:31 10

224

Partie IV

Patterns dopration

Cette approche de modlisation de ltat prsente deux problmes. Premirement,


les instructions if peuvent devenir complexes ; deuximement, lorsque vous ajustez la faon dont vous modlisez ltat, il est souvent ncessaire dajuster les
instructions if de plusieurs mthodes. Le pattern STATE offre une approche plus
claire et plus simple, utilisant une opration distribue. Il vous permet de modliser
des tats en tant quobjets, encapsulant la logique dpendant de ltat dans des classes distinctes. Avant de voir ce pattern luvre, il peut tre utile dexaminer un
systme qui modlise des tats sans y recourir. Dans la section suivante, nous refactoriserons ce code pour dterminer si STATE peut amliorer la conception.
Considrez le logiciel dOozinoz qui modlise les tats dune porte de carrousel.
Un carrousel est un grand rack intelligent qui accepte des produits par une porte et
les stocke daprs leur code-barres. La porte fonctionne au moyen dun seul bouton.
Lorsquelle est ferme, toucher le bouton provoque son ouverture. Le toucher avant
quelle soit compltement ouverte la fait se fermer. Lorsquelle est compltement
ouverte, elle commence se fermer automatiquement aprs deux secondes. Vous
pouvez empcher cela en touchant le bouton pendant que la porte est encore
ouverte. La Figure 22.1 montre les tats et les transitions de cette porte. Le code
correspondant est prsent un peu plus loin.

touch

Closed

complete

touch

Opening

Closing
touch

complete

timeout
touch
StayOpen

Open
touch

Figure 22.1
La porte du carrousel dispose dun bouton de contrle ragissant au toucher et permettant
de changer ses tats.

pattern Livre Page 225 Vendredi, 9. octobre 2009 10:31 10

Chapitre 22

STATE

225

Ce diagramme est une machine tats UML. De tels diagrammes peuvent tre
beaucoup plus informatifs quune description textuelle.
Exercice 22.1
Supposez que vous ouvriez la porte et passiez une caisse de produits de lautre
ct. Y a-t-il un moyen de faire en sorte que la porte commence se fermer avant
le dlai de deux secondes ?

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.


Vous pourriez introduire dans le logiciel du carrousel un objet Door quil actualiserait avec les changements dtat de la porte. La Figure 22.2 prsente la classe Door.
Figure 22.2
La classe Door modlise une porte de
carrousel, sappuyant
sur les vnements
de changement
dtat envoys par
le carrousel.

Observable

Door

complete()
setState(state:int)
status():String
timeout()
touch()

La classe Door est Observable de sorte que les clients, telle une interface GUI,
puissent afficher ltat de la porte. La dfinition de cette classe tablit les diffrents
tats que peut prendre la porte :
package com.oozinoz.carousel;
import java.util.Observable;

pattern Livre Page 226 Vendredi, 9. octobre 2009 10:31 10

226

Partie IV

Patterns dopration

public class Door extends Observable {


public final int CLOSED = -1;
public final int OPENING = -2;
public final int OPEN = -3;
public final int CLOSING = -4;
public final int STAYOPEN = -5;
private int state = CLOSED;
// ...
}

(Vous pourriez choisir dutiliser un type numr si vous programmez avec Java 5.)
La description textuelle de ltat de la porte dpendra videmment de ltat dans
lequel elle se trouve :
public String status() {
switch (state) {
case OPENING:
return "Opening";
case OPEN:
return "Open";
case CLOSING:
return "Closing";
case STAYOPEN:
return "StayOpen";
default:
return "Closed";
}
}

Lorsquun utilisateur touche le bouton du carrousel, ce dernier gnre un appel de


la mthode touch() dun objet Door. Le code de Door pour une transition dtat
reflte les informations de la Figure 22.1 :
public void touch() {
switch (state) {
case OPENING:
case STAYOPEN:
setState(CLOSING);
break;
case CLOSING:
case CLOSED:
setState(OPENING);
break;

pattern Livre Page 227 Vendredi, 9. octobre 2009 10:31 10

Chapitre 22

STATE

227

case OPEN:
setState(STAYOPEN);
break;
default:
throw new Error("Ne peut se produire");
}
}

La mthode setState() de la classe Door notifie aux observateurs le changement


dtat de la porte :
private void setState(int state) {
this.state = state;
setChanged();
notifyObservers();
}

Exercice 22.2
Ecrivez le code des mthodes complete() et timeout() de la classe Door.

Refactorisation pour appliquer STATE


Le code de Door est quelque peu complexe car lutilisation de la variable state est
rpartie travers toute la classe. En outre, il peut tre difficile de comparer les
mthodes de transition dtat, plus particulirement touch(), avec la machine
tats de la Figure 22.1. Le pattern STATE peut vous aider simplifier ce code. Pour
lappliquer dans cet exemple, il faut dfinir chaque tat de la porte en tant que classe
distincte, comme illustr Figure 22.3.
La refactorisation illustre dans cette figure cre une classe spare pour chaque
tat dans lequel la porte peut se trouver. Chacune delles contient la logique permettant de rpondre au toucher du bouton pendant que la porte est dans un certain tat.
Par exemple, le fichier DoorClosed.java contient le code suivant :
package com.oozinoz.carousel;
public class DoorClosed extends DoorState {
public DoorClosed(Door2 door) {
super(door);
}
public void touch() {
door.setState(door.OPENING);
}
}

pattern Livre Page 228 Vendredi, 9. octobre 2009 10:31 10

228

Partie IV

Patterns dopration

Door2

DoorState

complete()

DoorState(d:Door2)

setState(
state:DoorState)

complete()
status()

status()

timeout()

timeout()

touch()

touch()

DoorClosed
DoorOpening

DoorClosing
touch()
...

touch()

touch()

...

...

DoorOpen

DoorStayOpen

touch()

touch()

...

...

Figure 22.3
Le diagramme reprsente les tats de la porte en tant que classes dans une structure
qui reflte la machine tats de la porte.

La mthode touch() de la classe DoorClosed informe lobjet Door2 du nouvel tat


de la porte. Cet objet est celui reu par le constructeur de DoorClosed. Cette approche implique que chaque objet tat contienne une rfrence un objet Door2 pour
pouvoir informer la porte des transitions dtat. Un objet tat ne peut donc sappliquer ici qu une seule porte. La prochaine section dcrit comment modifier cette
conception pour quun mme ensemble dtats suffise pour un nombre quelconque
de portes.

pattern Livre Page 229 Vendredi, 9. octobre 2009 10:31 10

Chapitre 22

STATE

229

La gnration dun objet Door2 doit saccompagner de la cration dune suite


dtats appartenant la porte :
package com.oozinoz.carousel;
import java.util.Observable;
public class Door2 extends Observable {
public final DoorState CLOSED = new DoorClosed(this);
public final DoorState CLOSING = new DoorClosing(this);
public final DoorState OPEN = new DoorOpen(this);
public final DoorState OPENING = new DoorOpening(this);
public final DoorState STAYOPEN = new DoorStayOpen(this);
private DoorState state = CLOSED;
// ...
}

La classe abstraite DoorState requiert des sous-classes pour implmenter


touch(). Cela est cohrent avec la machine tats, dans laquelle tous les tats
possdent une transition touch(). Cette classe ne dfinit pas les autres transitions,
les laissant stub, pour permettre aux sous-classes de les remplacer ou dignorer les
messages non pertinents :
package com.oozinoz.carousel;
public abstract class DoorState {
protected Door2 door;
public abstract void touch();
public void complete() { }
public void timeout() { }
public String status() {
String s = getClass().getName();
return s.substring(s.lastIndexOf(.) + 1);
}
public DoorState(Door2 door) {
this.door = door;
}
}

Notez que la mthode status() fonctionne pour tous les tats et est beaucoup plus
simple quavant la refactorisation.

pattern Livre Page 230 Vendredi, 9. octobre 2009 10:31 10

230

Partie IV

Patterns dopration

La nouvelle conception ne change pas le rle dun objet Door2 en ce quil reoit
toujours les changements dtat de la part du carrousel, mais maintenant il les passe
simplement son objet state courant :
package com.oozinoz.carousel;
import java.util.Observable;
public class Door2 extends Observable {
// variables et constructeur...
public void touch() {
state.touch();
}
public void complete() {
state.complete();
}
public void timeout() {
state.timeout();
}
public String status() {
return state.status();
}
protected void setState(DoorState state) {
this.state = state;
setChanged();
notifyObservers();
}
}

Les mthodes touch(), complete(), timeout() et status() illustrent le rle du


polymorphisme dans cette conception. Chacune delles est toujours un genre
dinstruction switch. Dans chaque cas, lopration est fixe, mais la classe du rcepteur, cest--dire la classe de state, varie quant elle. La rgle du polymorphisme
est que la mthode qui sexcute dpend de la signature de lopration et de la
classe du rcepteur. Que se passe-t-il lorsque vous appelez touch()? Cela dpend
de ltat de la porte. Le code accomplit toujours un "switch", mais, en sappuyant
sur le polymorphisme, il est plus simple quauparavant.
La mthode setState() de la classe Door2 est maintenant utilise par des sousclasses de DoorState. Celles-ci ressemblent leurs contreparties dans la machine
tats de la Figure 22.1. Par exemple, le code de DoorOpen gre les appels de
touch() et timeout(), les deux transitions de ltat Open dans la machine :
package com.oozinoz.carousel;
public class DoorOpen extends DoorState {

pattern Livre Page 231 Vendredi, 9. octobre 2009 10:31 10

Chapitre 22

STATE

231

public DoorOpen(Door2 door) {


super(door);
}
public void touch() {
door.setState(door.STAYOPEN);
}
public void timeout() {
door.setState(door.CLOSING);
}
}

Exercice 22.3
Ecrivez le code de DoorClosing.java.

La nouvelle conception donne lieu un code beaucoup plus simple, mais il se peut
que vous ne soyez pas compltement satisfait du fait que les "constantes" utilises
par la classe Door soient en fait des variables locales.

Etats constants
Le pattern STATE rpartit la logique dpendant de ltat dun objet dans plusieurs
classes qui reprsentent les diffrents tats de lobjet. Il ne spcifie toutefois pas
comment grer la communication et les dpendances entre les objets tat et lobjet
central auquel ils sappliquent. Dans la conception prcdente, chaque classe dtat
acceptait un objet Door dans son constructeur. Les objets tat conservaient cet objet
et sen servaient pour actualiser ltat de la porte. Cette approche nest pas forcment mauvaise, mais elle a pour effet quinstancier un objet Door entrane linstanciation dun ensemble complet dobjets DoorState. Vous pourriez prfrer une
conception qui cre un seul ensemble statique dobjets DoorState et require que
la classe Door gre toutes les actualisations rsultant des changements dtat.
Une faon de rendre les objets tat constants est de faire en sorte que les classes
dtat identifient simplement ltat suivant, laissant le soin la classe Door dactualiser sa variable state. Dans une telle conception, la mthode touch() de la classe
Door, par exemple, actualise la variable state comme suit :
public void touch() {
state = state.touch();
}

pattern Livre Page 232 Vendredi, 9. octobre 2009 10:31 10

232

Partie IV

Patterns dopration

Notez que le type de retour de la mthode touch() de la classe Door est void. Les
sous-classes de DoorState implmenteront aussi touch() mais retourneront une
valeur DoorState. Par exemple, voici prsent le code de la mthode touch() de
DoorOpen:
public DoorState touch() {
return DoorState.STAYOPEN;
}

Dans cette conception, les objets DoorState ne conservent pas de rfrence vers un
objet Door, aussi lapplication requiert-elle une seule instance de chaque objet
DoorState.
Une autre approche pour rendre les objets DoorState constants est de faire passer
lobjet Door central pendant les transitions dtat. Pour cela, il faut ajouter un paramtre Door aux mthodes complete(), timeout() et touch(). Elles recevront
alors lobjet Door en tant que paramtre et actualiseront son tat sans conserver de
rfrence vers lui.
Exercice 22.4
Compltez le diagramme de classes de la Figure 22.4 pour reprsenter une
conception o les objets DoorState sont constants et qui fait passer un objet
Door pendant les transitions dtat.
Figure 22.4
Une fois complt, ce
diagramme reprsentera une conception
qui rend les tats de
la porte constants.

Door

DoorState
CLOSED:DoorClosed

pattern Livre Page 233 Vendredi, 9. octobre 2009 10:31 10

Chapitre 22

STATE

233

Lorsque vous appliquez le pattern STATE, vous disposez dune libert totale dans la
faon dont votre conception organise la communication des changements dtat.
Les classes dtat peuvent conserver une rfrence lobjet central dont ltat est
modlis. Sinon, vous pouvez faire passer cet objet durant les transitions. Vous
pouvez aussi faire en sorte que les sous-classes soient de simples fournisseurs
dinformations dterminant ltat suivant mais nactualisant pas lobjet central.
Lapproche que vous choisissez dpend du contexte de votre application ou de
considrations esthtiques.
Si vos tats sont utiliss par diffrents threads, assurez-vous que vos mthodes de
transition sont synchronises pour garantir labsence de conflit lorsque deux threads
tentent de modifier ltat au mme moment.
La puissance du pattern STATE est de permettre la centralisation de la logique de
diffrents tats dans une mme classe.

Rsum
De manire gnrale, ltat dun objet dpend de la valeur collective de ses variables dinstance. Dans certains cas, la plupart des attributs de lobjet sont assez statiques une fois dfinis, lexception dun attribut qui est dynamique et joue un rle
prdominant dans la logique de la classe. Cet attribut peut reprsenter ltat de
lobjet tout entier et peut mme tre nomm state.
Une variable dtat dominante peut exister lorsquun objet modlise une entit du
monde rel dont ltat est important, telle quune transaction ou une machine. La
logique qui dpend de ltat de lobjet peut alors apparatre dans de nombreuses
mthodes. Vous pouvez simplifier un tel code en plaant les comportements spcifiques aux diffrents tats dans une hirarchie dobjets tat. Chaque classe dtat
peut ainsi contenir le comportement pour un seul tat du domaine. Cela permet
galement dtablir une correspondance directe entre les classes dtat et les tats
dune machine tats.
Pour grer les transitions entre les tats, vous pouvez laisser lobjet central conserver des rfrences vers un ensemble dtats. Ou bien, dans les mthodes de transition dtat, vous pouvez faire passer lobjet central dont ltat change. Vous pouvez
sinon faire des classes dtat des fournisseurs dinformations qui indiquent simplement un tat subsquent sans actualiser lobjet central. Quelle que soit la manire
dont vous procdez, le pattern STATE simplifie votre code en distribuant une opration
travers une collection de classes qui reprsentent les diffrents tats dun objet.

pattern Livre Page 234 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 235 Vendredi, 9. octobre 2009 10:31 10

23
STRATEGY
Une stratgie est un plan, ou une approche, pour atteindre un but en fonction de
certaines conditions initiales. Elle est donc semblable un algorithme, lequel est
une procdure gnrant un rsultat partir dun ensemble dentres. Habituellement, une stratgie dispose dune plus grande latitude quun algorithme pour
accomplir son objectif. Cette latitude signifie galement que les stratgies apparaissent
souvent par groupes, ou familles, dalternatives.
Lorsque plusieurs stratgies apparaissent dans un programme, le code peut devenir
complexe. La logique qui entoure les stratgies doit slectionner lune delles, et ce
code de slection peut lui-mme devenir complexe. Lexcution de plusieurs stratgies peut emprunter diffrents chemins dans le code, lequel peut mme tre contenu
dans une seule mthode. Lorsque la slection et lexcution de diverses mthodes
conduisent un code complexe, vous pouvez appliquer le pattern STRATEGY pour le
simplifier.
Lopration stratgique dfinit les entres et les sorties dune stratgie mais laisse
limplmentation aux classes individuelles. Les classes qui implmentent les diverses approches implmentent la mme opration et sont donc interchangeables,
prsentant des stratgies diffrentes mais la mme interface aux clients. Le pattern
STRATEGY permet une famille de stratgies de coexister sans que leurs codes
respectifs sentremlent. Il spare aussi la logique de slection dune stratgie des
stratgies elles-mmes.
Lobjectif du pattern STRATEGY est dencapsuler des approches, ou stratgies,
alternatives dans des classes distinctes qui implmentent chacune une opration commune.

pattern Livre Page 236 Vendredi, 9. octobre 2009 10:31 10

236

Partie IV

Patterns dopration

Modlisation de stratgies
Le pattern STRATEGY aide organiser et simplifier le code en encapsulant diffrentes
approches dun problme dans plusieurs classes. Avant de le voir luvre, il peut
tre utile dexaminer un programme qui modlise des stratgies sans lappliquer.
Dans la section suivante, nous refactoriserons ce code en appliquant STRATEGY pour
amliorer sa qualit.
Considrez la politique publicitaire dOozinoz qui suggre aux clients qui visitent son
site Web ou prennent contact avec son centre dappels quel artifice acheter. Oozinoz
utilise deux moteurs de recommandation du commerce pour dterminer larticle
appropri proposer. La classe Customer choisit et applique un des deux moteurs.
LikeMyStuff
Customer
suggest(:Customer):Object
isRegistered():bool
getRecommended():Firework
spendingSince(d:Date):
double

Rel8

advise(:Customer):Object
Firework

...
getRandom():Firework
lookup(name:String):
Firework

Figure 23.1
La classe Customer sappuie sur dautres classes pour ses recommandations, parmi lesquelles
deux moteurs de recommandation du commerce.

Un des moteurs de recommandation, Rel8, suggre un achat en se fondant sur les


similitudes du client avec dautres clients. Pour que cela fonctionne, le client doit
stre enregistr au pralable et avoir fourni des informations sur ses prfrences en
matire dartifices et autres distractions. Sil nest pas encore enregistr, Oozinoz

pattern Livre Page 237 Vendredi, 9. octobre 2009 10:31 10

Chapitre 23

STRATEGY

237

utilise lautre moteur, LikeMyStuff, qui suggre un achat sur la base des achats
rcents du client. Si aucun des deux moteurs ne dispose de suffisamment de
donnes pour assurer sa fonction, le logiciel de publicit choisit un artifice au
hasard. Une promotion spciale peut nanmoins avoir la priorit sur toutes ces
considrations, mettant en avant un artifice particulier quOozinoz cherche
vendre. La Figure 23.1 prsente les classes qui collaborent pour suggrer un artifice
un client.
Les moteurs LikeMyStuff et Rel8 acceptent un objet Customer et suggrent quel
artifice proposer au client. Tous deux sont configurs chez Oozinoz pour grer des
artifices, mais LikeMyStuff requiert une base de donnes tandis que Rel8 travaille
essentiellement partir dun modle objet. Le code de la mthode getRecommended()
de la classe Customer reflte la politique publicitaire dOozinoz :
public Firework getRecommended() {
// En cas de promotion dun artifice particulier, le retourner.
try {
Properties p = new Properties();
p.load(ClassLoader.getSystemResourceAsStream(
"config/strategy.dat"));
String promotedName = p.getProperty("promote");
if (promotedName != null) {
Firework f = Firework.lookup(promotedName);
if (f != null) return f;
}
} catch (Exception ignored) {
// Si la ressource est manquante ou na pas t charge,
// se rabattre sur lapproche suivante.
}
// Si le client est enregistr, le comparer aux autres clients.
if (isRegistered()) {
return (Firework) Rel8.advise(this);
}
// Vrifier les achats du client sur lanne coule.
Calendar cal = Calendar.getInstance();
cal.add(Calendar.YEAR, -1);
if (spendingSince(cal.getTime()) > 1000)
return (Firework) LikeMyStuff.suggest(this);
// Retourner nimporte quel artifice.
return Firework.getRandom();
}

pattern Livre Page 238 Vendredi, 9. octobre 2009 10:31 10

238

Partie IV

Patterns dopration

Ce code est extrait du package com.oozinoz.recommendation de la base de code


Oozinoz accessible ladresse www.oozinoz.com. La mthode getRecommended() sattend ce que, sil y a une promotion, elle soit nomme dans un fichier
strategy.dat dans un rpertoire config. Voici quoi ressemblerait un tel
fichier :
promote=JSquirrel

En labsence de ce fichier, la mthode utilise le moteur Rel8 si le client est inscrit.


Si le client nest pas inscrit, elle utilise le moteur LikeMyStuff lorsque le client a
dj dpens un certain montant au cours de lanne passe. Si aucune meilleure
recommandation nest possible, le code slectionne et propose un artifice quelconque.
Cette mthode fonctionne, et vous avez probablement dj vu pire comme code,
mais nous pouvons certainement lamliorer.

Refactorisation pour appliquer STRATEGY


La mthode getRecommended() prsente plusieurs problmes. Dabord, elle est
longue, au point que des commentaires doivent expliquer ses diffrentes parties.
Les mthodes courtes sont plus faciles comprendre et ont rarement besoin dtre
expliques, elles sont donc gnralement prfrables aux mthodes longues.
Ensuite, non seulement elle choisit une stratgie mais elle lexcute galement, ce
qui constitue deux fonctions distinctes qui peuvent tre spares. Vous pouvez
simplifier ce code en appliquant STRATEGY. Pour cela, vous devez :
m

crer une interface qui dfinit lopration stratgique ;

implmenter linterface avec des classes qui reprsentent chacune une stratgie ;

refactoriser le code pour slectionner et utiliser une instance de la classe d e


stratgie approprie.

Supposez que vous criez une interface Advisor, comme illustr Figure 23.2.
Figure 23.2
Linterface Advisor
dfinit une opration
que diverses classes
peuvent implmenter
avec diffrentes
stratgies.

interface
Advisor

recommend(c:Customer):Firework

pattern Livre Page 239 Vendredi, 9. octobre 2009 10:31 10

Chapitre 23

STRATEGY

239

Linterface Advisor dclare quune classe qui limplmente peut accepter un client
et recommander un artifice. Ltape suivante consiste refactoriser la mthode
getRecommended() de la classe Customer pour crer des classes reprsentant
chacune des stratgies de recommandation. Chaque classe fournit une implmentation
diffrente de la mthode recommend() spcifie par linterface Advisor.

Customer

interface
Advisor

BIG_SPENDER_DOLLARS:int
getAdvisor():Advisor

recommend(c:Customer):Firework

isRegistered():boolean
isBigSpender():boolean
getRecommended():Firework

GroupAdvisor

spendingSince(d:Date):
double
ItemAdvisor

PromotionAdvisor

RandomAdvisor

Figure 23.3
Compltez ce diagramme pour montrer la refactorisation du logiciel de recommandation,
avec les stratgies apparaissant comme implmentations dune interface commune.

Une fois que vous disposez des classes de stratgie, vous devez y placer le code de
la mthode getRecommended() de la classe Customer. Les deux classes les plus
simples sont GroupAdvisor et ItemAdvisor. Elles doivent seulement envelopper
les appels pour les moteurs de recommandation. Une interface ne pouvant dfinir
que des mthodes dinstance, GroupAdvisor et ItemAdvisor doivent tre instancies pour supporter linterface Advisor. Comme un seul objet de chaque classe est
ncessaire, Customer devrait inclure une seule instance statique de chaque classe.

pattern Livre Page 240 Vendredi, 9. octobre 2009 10:31 10

240

Partie IV

Patterns dopration

La Figure 23.4 illustre une conception pour ces classes.

interface
Advisor

recommend(c:Customer):
Firework

GroupAdvisor

recommend(c:Customer):
Firework

Rel8

advise(c:Customer):Object

ItemAdvisor

recommend(c:Customer):
Firework

LikeMyStuff

suggest(c:Customer):Object

Figure 23.4
Les implmentations de linterface Advisor fournissent lopration stratgique recommend(),
sappuyant sur les moteurs de recommandation.

Les classes Advisor traduisent les appels de recommend() en interfaces requises


par les moteurs sous-jacents. Par exemple, la classe GroupAdvisor traduit ces
appels en linterface advise() requise par le moteur Rel8:
public Firework recommend(Customer c) {
return (Firework) Rel8.advise(c);
}

Exercice 23.2
Outre le pattern STRATEGY, quel autre pattern apparat dans les classes GroupAdvisor et ItemAdvisor?

pattern Livre Page 241 Vendredi, 9. octobre 2009 10:31 10

Chapitre 23

STRATEGY

241

Les classes GroupAdvisor et ItemAdvisor oprent en traduisant un appel de la


mthode recommend() en un appel dun moteur de recommandation. Il faut aussi
crer une classe PromotionAdvisor et une classe RandomAdvisor, en refactorisant
le code de la mthode getRecommended() de Customer. A linstar de GroupAdvisor
et ItemAdvisor, ces classes fournissent aussi lopration recommend().
Le constructeur de PromotionAdvisor devrait dterminer sil existe une promotion
en cours. Vous pourriez ensuite ajouter cette classe une mthode hasItem() indiquant
sil y a un article en promotion :
public class PromotionAdvisor implements Advisor {
private Firework promoted;
public PromotionAdvisor() {
try {
Properties p = new Properties();
p.load(ClassLoader.getSystemResourceAsStream(
"config/strategy.dat"));
String promotedFireworkName = p.getProperty("promote");
if (promotedFireworkName != null)
promoted = Firework.lookup(promotedFireworkName);
} catch (Exception ignored) {
// Ressource introuvable ou non charge
promoted = null;
}
}
public boolean hasItem() {
return promoted != null;
}
public Firework recommend(Customer c) {
return promoted;
}
}

La classe RandomAdvisor est simple :


public class RandomAdvisor implements Advisor {
public Firework recommend(Customer c) {
return Firework.getRandom();
}
}

La refactorisation de Customer permet de sparer la slection dune stratgie de


son utilisation. Un attribut advisor dun objet Customer contient le choix
courant de la stratgie appliquer. La classe Customer2 refactorise procde une

pattern Livre Page 242 Vendredi, 9. octobre 2009 10:31 10

242

Partie IV

Patterns dopration

initialisation paresseuse de cet attribut avec une logique qui reflte la politique
publicitaire dOozinoz :
private Advisor getAdvisor() {
if (advisor == null) {
if (promotionAdvisor.hasItem())
advisor = promotionAdvisor;
else if (isRegistered())
advisor = groupAdvisor;
else if (isBigSpender())
advisor = itemAdvisor;
else
advisor = randomAdvisor;
}
return advisor;
}

Exercice 23.3
Ecrivez le nouveau code de la mthode Customer.getRecommended().

Comparaison de STRATEGY et STATE


Le code refactoris consiste presque entirement en des mthodes simples dans des
classes simples. Cela reprsente un avantage en soi et facilite lajout de nouvelles
stratgies. La refactorisation se fonde principalement sur le principe de distribuer
une opration travers un groupe de classes associes. A cet gard, STRATEGY est
identique STATE. En fait, certains dveloppeurs se demandent mme si ces deux
patterns sont vraiment diffrents.
Dun ct, la diffrence entre modliser des tats et modliser des stratgies peut
paratre subtile. En effet, STATE et STRATEGY semblent faire une utilisation du polymorphisme quasiment identique sur le plan structurel.
Dun autre ct, dans le monde rel, les stratgies et les tats reprsentent clairement des concepts diffrents, et cette diffrence donne lieu divers problmes
de modlisation. Par exemple, les transitions sont importantes lorsquil sagit de
modliser des tats tandis quelles sont hors de propos lorsquil sagit de choisir
une stratgie. Une autre diffrence est que STRATEGY peut permettre un client de
slectionner ou de fournir une stratgie, une ide qui sapplique rarement STATE.
Etant donn que ces deux patterns nont pas le mme objectif, nous continuerons de

pattern Livre Page 243 Vendredi, 9. octobre 2009 10:31 10

Chapitre 23

STRATEGY

243

les considrer comme diffrents. Mais vous devez savoir que tout le monde ne
reconnat pas cette distinction.

Comparaison de STRATEGY et TEMPLATE METHOD


Le Chapitre 21, consacr TEMPLATE METHOD, a pris le triage comme exemple de
TEMPLATE METHOD. Vous pouvez utiliser lalgorithme sort() de la classe Arrays
ou Collection pour trier nimporte quelle liste dobjets, ds lors que vous fournissez une tape pour comparer deux objets. Vous pourriez avancer que lorsque vous
fournissez une tape de comparaison pour un algorithme de tri, vous changez la stratgie. En supposant par exemple que vous vendiez des fuses, le fait de les prsenter
tries par prix ou tries par pousses reprsente deux stratgies marketing diffrentes.
Exercice 23.4
Expliquez en quoi la mthode Arrays.sort() constitue un exemple de
TEMPLATE METHOD et/ou de STRATEGY.

Rsum
Il arrive que la logique qui modlise des stratgies alternatives apparaisse dans une
seule classe, souvent mme dans une seule mthode. De telles mthodes tendent tre
trop compliques et mler la logique de slection dune stratgie avec son excution.
Pour simplifier votre code, vous pouvez crer un groupe de classes, une pour
chaque stratgie, puis dfinir une opration et la distribuer travers ces classes.
Chaque classe peut ainsi encapsuler une stratgie, rduisant considrablement le
code. Vous devez aussi permettre au client qui utilise une stratgie den slectionner
une. Ce code de slection peut tre complexe mme lissue de la refactorisation,
mais vous devriez pouvoir le rduire jusqu ce quil ressemble presque du
pseudo-code dcrivant la slection dune stratgie dans le domaine du problme.
Typiquement, un client conserve la stratgie slectionne dans une variable contextuelle. Lexcution de la stratgie revient ainsi simplement transmettre au contexte
lappel de lopration stratgique, en utilisant le polymorphisme pour excuter la
stratgie approprie. En encapsulant les stratgies alternatives dans des classes
spares implmentant chacune une opration commune, le pattern STRATEGY
permet de crer un code clair et simple qui modlise une famille dapproches pour
rsoudre un problme.

pattern Livre Page 244 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 245 Vendredi, 9. octobre 2009 10:31 10

24
COMMAND
Le moyen classique de dclencher lexcution dune mthode est de lappeler.
Il arrive souvent nanmoins que vous ne puissiez pas contrler le moment prcis ou
le contexte de son excution. Dans de telles situations, vous pouvez lencapsuler
dans un objet. En stockant les informations ncessaires linvocation dune mthode
dans un objet, vous pouvez la passer en tant que paramtre, permettant ainsi un
client ou un service de dterminer quand linvoquer.
Lobjectif du pattern COMMAND est dencapsuler une requte dans un objet.

Un exemple classique : commandes de menus


Les kits doutils qui supportent des menus appliquent gnralement le pattern
COMMAND. Chaque lment de menu saccompagne dun objet qui sait comment se
comporter lorsque lutilisateur clique dessus. Cette conception permet de sparer la
logique GUI de lapplication. La bibliothque Swing adopte cette approche, vous
permettant dassocier un ActionListener chaque JMenuItem.
Comment faire pour quune classe appelle une de vos mthodes lorsque lutilisateur
clique ? Il faut pour cela recourir au polymorphisme, cest--dire rendre le nom de
lopration fixe et laisser limplmentation varier. Pour JMenuItem, lopration est
actionPerformed(). Lorsque lutilisateur fait un choix, llment JMenuItem
appelle la mthode actionPerformed() de lobjet qui sest enregistr en tant que
listener.

pattern Livre Page 246 Vendredi, 9. octobre 2009 10:31 10

246

Partie IV

Patterns dopration

Exercice 24.1
Le fonctionnement des menus Java facilite lapplication du pattern COMMAND mais
ne vous demande pas dorganiser votre code en commandes. En fait, il est
frquent de dvelopper une application dans laquelle un seul objet coute tous les
vnements dune interface GUI. Quel pattern cela vous voque-t-il ?

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.


Lorsque vous dveloppez une application Swing, vous pouvez enregistrer un seul
listener pour tous les vnement GUI, plus particulirement lorsque les composants
GUI interagissent. Toutefois, pour les menus, il ne sagit gnralement pas de
lapproche suivre. Si vous deviez utiliser un seul objet pour couter les menus, il
devrait dterminer pour chaque vnement lobjet GUI qui la gnr. Au lieu de
cela, lorsque vous avez plusieurs lments de menu qui donnent lieu des actions
indpendantes, il peut tre prfrable dappliquer COMMAND.
Lorsquun utilisateur slectionne un lment de menu, il invoque la mthode
actionPerformed(). Lorsque vous crez llment, vous pouvez lui associer un
ActionListener, avec une mthode actionPerformed() spcifique au comportement de la commande. Plutt que de dfinir une nouvelle classe pour implmenter
ce petit comportement, il est courant demployer une classe anonyme.
Considrez la classe Visualization2 du package com.oozinoz.visualization.
Elle fournit une barre de menus avec un menu File (Fichier) qui permet lutilisateur denregistrer et de restaurer les visualisations dune unit de production Oozinoz
simule. Ce menu comporte des lments Save As (Enregistrer sous) et
Restore From (Restaurer partir de). Le code qui cre ces lments enregistre
des listeners qui attendent la slection de lutilisateur. Ces listeners implmentent la
mthode actionPerformed() en appelant les mthodes save() et load() de la
classe Visualization2:
package com.oozinoz.visualization;
import java.awt.event.*;
import javax.swing.*;
import com.oozinoz.ui.*;
public class Visualization2 extends Visualization {
public static void main(String[] args) {
Visualization2 panel = new Visualization2(UI.NORMAL);
JFrame frame =
SwingFacade.launch(panel, "Operational Model");

pattern Livre Page 247 Vendredi, 9. octobre 2009 10:31 10

Chapitre 24

COMMAND

247

frame.setJMenuBar(panel.menus());
frame.setVisible(true);
}
public Visualization2(UI ui) {
super(ui);
}
public JMenuBar menus() {
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("File");
menuBar.add(menu);
JMenuItem menuItem = new JMenuItem("Save As...");
menuItem.addActionListener(new ActionListener() {
// Exercice !
});
menu.add(menuItem);
menuItem = new JMenuItem("Restore From...");
menuItem.addActionListener(new ActionListener() {
// Exercice !
});
menu.add(menuItem);
return menuBar;
}
public void save() { /* omis */ }
public void restore() { /* omis */ }
}

Exercice 24.2
Compltez le code des sous-classes anonymes de ActionListener, en remplaant
la mthode actionPerformed(). Notez que cette mthode attend un argument
ActionEvent.

Lorsque vous associez des commandes un menu, vous les placez dans un contexte
fourni par un autre dveloppeur : le framework de menus Java. Dans dautres utilisations de COMMAND, vous aurez le rle de dvelopper le contexte dans lequel les
commandes sexcuteront. Par exemple, vous pourriez vouloir fournir un service de
minutage qui enregistre la dure dexcution des mthodes.

pattern Livre Page 248 Vendredi, 9. octobre 2009 10:31 10

248

Partie IV

Patterns dopration

Emploi de COMMAND pour fournir un service


Supposez que vous vouliez permettre aux dveloppeurs de connatre la dure
dexcution dune mthode. Vous disposez dune interface Command dont voici
lessence :
public abstract void execute();

Vous disposez galement de la classe CommandTimer suivante :


package com.oozinoz.utility;
import com.oozinoz.robotInterpreter.Command;
public class CommandTimer {
public static long time(Command command) {
long t1 = System.currentTimeMillis();
command.execute();
long t2 = System.currentTimeMillis();
return t2 - t1;
}
}

Vous pourriez tester la mthode time() au moyen dun test JUnit ressemblant
ce qui suit. Notez que ce test nest pas exact car il peut chouer si le timer est irrgulier :
package app.command;
import com.oozinoz.robotInterpreter.Command;
import com.oozinoz.utility.CommandTimer;
import junit.framework.TestCase;
public class TestCommandTimer extends TestCase {
public void testSleep() {
Command doze = new Command() {
public void execute() {
try {
Thread.sleep(
2000 + Math.round(10 * Math.random()));
} catch (InterruptedException ignored) {
}
}
};
long actual = // Exercice !

pattern Livre Page 249 Vendredi, 9. octobre 2009 10:31 10

Chapitre 24

COMMAND

249

long expected = 2000;


long delta = 5;
assertTrue(
"Devrait tre " + expected + " +/- " + delta + " ms",
expected - delta <= actual
&& actual <= expected + delta);
}
}

Exercice 24.3
Compltez les instructions dassignation qui dfinissent la valeur de actual de
manire que la commande doze soit minute.

Hooks
Le Chapitre 21, consacr au pattern TEMPLATE METHOD, a introduit la presse toiles Aster, une machine intelligente qui inclut du code sappuyant sur ce pattern. Le
code de cette machine vous permet de remplacer une mthode qui marque un moule
comme tant incomplet sil est en cours de traitement au moment o elle est arrte.
La classe AsterStarPress est abstraite, vous demandant de driver une sousclasse contenant une mthode markMoldIncomplete(). La mthode shutDown()
de AsterStarPress utilise cette mthode pour garantir que lobjet du domaine sait
que le moule est incomplet :
public void shutdown() {
if (inProcess()) {
stopProcessing();
markMoldIncomplete(currentMoldID);
}
usherInputMolds();
dischargePaste();
flush();
}

Il peut vous sembler peu commode dtendre AsterStarPress avec une classe que
vous devez introduire dans lordinateur de bord de la presse. Supposez que vous
demandiez aux dveloppeurs de chez Aster de fournir le hook sous une forme diffrente, en utilisant le pattern COMMAND. La Figure 24.1 illustre une commande Hook

pattern Livre Page 250 Vendredi, 9. octobre 2009 10:31 10

250

Partie IV

Patterns dopration

que la classe AsterStarPress peut utiliser, vous permettant de paramtrer la


presse en cours dexcution.

AsterStarPress

interface
Hook

moldIncompleteHook:Hook
getCurrentMoldID():int

execute(p:AsterStarPress)

dischargePaste()
flush()
inProcess():boolean

NullHook

shutDown()
stopProcessing()
usherInputMolds()

execute(p:AsterStarPress)

setMoldIncompleteHook(:Hook)

Figure 24.1
Une classe peut fournir un hook, cest--dire un moyen dinsrer du code personnalis,
en invoquant une certaine commande un point prcis dans une procdure.

Dans la classe AsterStarPress originale, la mthode shutDown() sappuyait sur


une tape devant tre dfinie par des sous-classes. Dans la nouvelle conception,
cette mthode utilise un hook pour excuter le code client aprs que le traitement a
t interrompu mais avant la fin du processus darrt :
public void shutDown() {
if (inProcess()) {
stopProcessing();
// Exercice !
}
usherInputMolds();
dischargePaste();
flush();
}

Exercice 24.4
Compltez le code de la nouvelle mthode shutDown().

pattern Livre Page 251 Vendredi, 9. octobre 2009 10:31 10

Chapitre 24

COMMAND

251

Cet exemple est reprsentatif dun autre pattern, NULL OBJECT [Woolf 1998], qui
est seulement un peu moins connu que ceux dcrits dans Design Patterns. Ce
pattern permet dviter davoir vrifier lventualit dun pointeur null en introduisant un objet par dfaut qui est sans effet (voir Refactoring [Fowler et al. 1999]
pour une explication de la faon dappliquer ce pattern votre code). Le pattern
COMMAND reprsente une conception alternative TEMPLATE METHOD pour les hooks,
et est semblable en termes dobjectif, ou de structure, plusieurs autres patterns.

COMMAND en relation avec dautres patterns


COMMAND ressemble au pattern INTERPRETER, ces deux patterns tant compars
dans le prochain chapitre. Il ressemble aussi un pattern dans lequel un client sait
quand une action est requise mais ne sait pas exactement quelle opration appeler.
Exercice 24.5
Quel pattern convient dans la situation o un client sait quand crer un objet mais
ne sait pas quelle classe instancier ?

Outre des similitudes avec dautres patterns, COMMAND collabore souvent galement
avec dautres patterns. Par exemple, vous pourriez combiner COMMAND et MEDIATOR
dans une conception MVC. Le Chapitre 19, consacr au pattern MEMENTO, en donne
un exemple. La classe Visualization gre la logique de contrle GUI mais confie
un mdiateur la logique relative au modle. Par exemple, cette classe utilise le code
suivant pour effectuer une initialisation paresseuse de son bouton Undo :
protected JButton undoButton() {
if (undoButton == null) {
undoButton = ui.createButtonCancel();
undoButton.setText("Undo");
undoButton.setEnabled(false);
undoButton.addActionListener(mediator.undoAction());
}
return undoButton;
}

Ce code applique COMMAND, introduisant une mthode undo() dans une instance de
la classe ActionListener. Il applique galement MEDIATOR, laissant un objet central
servir de mdiateur pour les vnements appartenant un modle objet sous-jacent.

pattern Livre Page 252 Vendredi, 9. octobre 2009 10:31 10

252

Partie IV

Patterns dopration

Pour que la mthode undo() fonctionne, le code mdiateur doit restaurer une
version antrieure de lunit de production simule, offrant lopportunit dappliquer
un autre pattern qui accompagne souvent COMMAND.
Exercice 24.6
Quel pattern permet dassurer le stockage et la restauration de ltat dun objet ?

Rsum
Le pattern COMMAND sert encapsuler une requte dans un objet, vous permettant de
grer des appels de mthodes en tant quobjets, les passant et les invoquant lorsque
le moment ou les conditions sont appropris. Un exemple classique de lintrt de
ce pattern a trait aux menus. Les lments de menu savent quand excuter une
action mais ne savent pas quelle mthode appeler. COMMAND permet de paramtrer
un menu avec des appels de mthodes correspondant aux options quil contient.
COMMAND peut aussi tre utilis pour permettre lexcution de code client dans le
contexte dun service. Un service excute souvent du code avant et aprs linvocation du code client. Outre le fait de contrler le moment ou le contexte dexcution
dune mthode, ce pattern peut offrir un mcanisme commode pour fournir des
hooks, permettant un code client optionnel de sexcuter dans le cadre dun algorithme.
Un autre aspect fondamental de COMMAND est quil entretient plusieurs relations intressantes avec dautres patterns. Il peut constituer une alternative TEMPLATE
METHOD et peut aussi souvent collaborer avec MEDIATOR et MEMENTO.

pattern Livre Page 253 Vendredi, 9. octobre 2009 10:31 10

25
INTERPRETER
A linstar du pattern COMMAND, le pattern INTERPRETER produit un objet excutable. Ces
deux patterns diffrent en ce que INTERPRETER implique la cration dune hirarchie
de classes dans laquelle chaque classe implmente, ou interprte, une opration
commune de sorte quelle corresponde au nom de la classe. A cet gard, INTERPRETER
est semblable aux patterns STATE et STRATEGY. Dans ces trois patterns, une opration commune apparat dans une collection de classes, chacune delles implmentant
lopration de manire diffrente.
Le pattern INTERPRETER ressemble aussi au pattern COMPOSITE, lequel dfinit une
interface commune pour des lments individuels ou des groupes dlments.
COMPOSITE ne requiert pas des moyens diffrents de former des groupes, bien quil
le permette. Par exemple, la hirarchie ProcessComponent du Chapitre 5, consacr
au pattern COMPOSITE, autorise des squences et des alternances de flux de processus. Dans INTERPRETER, lide quil y ait diffrents types de compositions est
essentielle (un INTERPRETER est souvent plac au-dessus dune structure COMPOSITE). La faon dont une classe compose dautres composants aide dfinir
comment une classe INTERPRETER implmente une opration.
INTERPRETER nest pas un pattern facile comprendre. Vous pourriez avoir besoin
de rviser le pattern COMPOSITE car nous nous y rfrerons dans ce chapitre.
Lobjectif du pattern INTERPRETER est de vous permettre de composer des
objets excutables daprs un ensemble de rgles de composition que vous
dfinissez.

pattern Livre Page 254 Vendredi, 9. octobre 2009 10:31 10

254

Partie IV

Patterns dopration

Un exemple de INTERPRETER
Les robots quOozinoz utilise pour dplacer des produits dans une ligne de traitement comportent un interprteur qui contrle le robot mais dispose dun contrle
limit sur les machines de la ligne. Vous pourriez voir les interprteurs comme se
destinant aux langages de programmation, mais le cur du pattern INTERPRETER
est constitu dune collection de classes qui permettent la composition dinstructions. Linterprteur des robots dOozinoz consiste en une hirarchie de classes qui
encapsulent des commandes de robot. La hirarchie comporte une classe abstraite
Command en son sommet et inclut tous les niveaux une opration execute(). La
Figure 25.1 prsente la classe Robot et deux des commandes supportes par linterprteur.

com.oozinoz.robotInterpreter

Robot
singleton:Robot
Command
carry(
fromMachine:Machine,
toMachine:Machine)

CarryCommand

*
execute()

CommandSequence

fromMachine:Machine
toMachine:Machine

add(c:Command)
execute()

CarryCommand(
fromMachine:Machine,
toMachine:Machine)
execute()

Figure 25.1
Une hirarchie interprteur supporte la programmation lors de lexcution dun robot dusine.

pattern Livre Page 255 Vendredi, 9. octobre 2009 10:31 10

Chapitre 25

INTERPRETER

255

Cette figure pourrait suggrer que le pattern COMMAND est prsent dans cette conception, avec une classe Command au sommet de la hirarchie. Toutefois, ce pattern a
pour objectif dencapsuler une mthode dans un objet, ce que ne fait pas ici la
hirarchie Command. Au lieu de cela, cette conception requiert que les sous-classes
de Command rinterprtent lopration execute(), ce qui est lobjectif du pattern
INTERPRETER: vous permettre de composer des objets excutables.
Une hirarchie INTERPRETER typique inclurait plus de deux sous-classes et tendrait brivement la hirarchie Command. Les deux sous-classes de la Figure 25.1
suffisent nanmoins pour un exemple initial :
package app.interpreter;
import com.oozinoz.machine.*;
import com.oozinoz.robotInterpreter.*;
public class ShowInterpreter {
public static void main(String[] args) {
MachineComposite dublin = OozinozFactory.dublin();
ShellAssembler assembler =
(ShellAssembler) dublin.find("ShellAssembler:3302");
StarPress press = (StarPress) dublin.find("StarPress:3404");
Fuser fuser = (Fuser) dublin.find("Fuser:3102");
assembler.load(new Bin(11011));
press.load(new Bin(11015));
CarryCommand carry1 = new CarryCommand(assembler, fuser);
CarryCommand carry2 = new CarryCommand(press, fuser);
CommandSequence seq = new CommandSequence();
seq.add(carry1);
seq.add(carry2);
seq.execute();
}
}

Ce code de dmonstration fait quun robot dusine dplace deux caisses de produits
des machines oprationnelles vers un tampon de dchargement. Il fonctionne avec
un composite de machines retourn par la mthode dublin() de la classe OozinozFactory. Ce modle de donnes reprsente une unit de production prvue pour un
nouveau site Dublin en Irlande. Le code localise trois machines au sein de lusine,
charge les caisses de produits sur deux dentre elles, puis cre des commandes

pattern Livre Page 256 Vendredi, 9. octobre 2009 10:31 10

256

Partie IV

Patterns dopration

partir de la hirarchie Command. La dernire instruction du programme appelle la


mthode execute() dun objet CommandSequence pour que le robot excute les
actions contenues dans la commande seq.
Un objet CommandSequence interprte lopration execute() en transmettant
lappel chaque sous-commande :
package com.oozinoz.robotInterpreter;
import java.util.ArrayList;
import java.util.List;
public class CommandSequence extends Command {
protected List commands = new ArrayList();
public void add(Command c) {
commands.add(c);
}
public void execute() {
for (int i = 0; i < commands.size(); i++) {
Command c = (Command) commands.get(i);
c.execute();
}
}
}

La classe CarryCommand interprte lopration execute() en interagissant avec le


robot pour dplacer une caisse dune machine vers une autre :
package com.oozinoz.robotInterpreter;
import com.oozinoz.machine.Machine;
public class CarryCommand extends Command {
protected Machine fromMachine;
protected Machine toMachine;
public CarryCommand(
Machine fromMachine, Machine toMachine) {
this.fromMachine = fromMachine;
this.toMachine = toMachine;
}
public void execute() {
Robot.singleton.carry(fromMachine, toMachine);
}
}

pattern Livre Page 257 Vendredi, 9. octobre 2009 10:31 10

Chapitre 25

INTERPRETER

257

La classe CarryCommand a t conue pour fonctionner spcifiquement dans le


domaine dune ligne de production contrle par des robots. On peut facilement
imaginer dautres classes spcifiques un domaine, telles quune classe StartUpCommand ou ShutdownCommand pour contrler les machines. Il serait galement
utile davoir une classe ForCommand qui excute une commande travers un ensemble de machines. La Figure 25.2 illustre ces extensions de la hirarchie Command.
Figure 25.2
Le pattern INTERPRETER
permet plusieurs sousclasses de rinterprter
la signification dune
opration commune.

Command

execute()

CommandSequence

CarryCommand

ForCommand

StartUpCommand

ShutdownCommand

Une partie de la conception de la classe ForCommand apparat claire demble. Le


constructeur de cette classe accepterait vraisemblablement une collection de machines et un objet COMMAND qui serait excut en tant que corps dune boucle for. La
partie la plus dlicate est la liaison de la boucle et du corps. Java 5 possde une
instruction for tendue qui tablit une variable recevant une nouvelle valeur chaque
fois que le corps est excut. Nous mulerons cette approche. Considrez linstruction
suivante :
for (Command c: commands)
c.execute();

Java associe lidentifiant c dclar par linstruction for la variable c du corps de


la boucle. Pour crer une classe INTERPRETER qui mule cela, nous avons besoin
dun mcanisme pour grer et valuer des variables. La Figure 25.3 prsente une
hirarchie Term qui sert cela.

pattern Livre Page 258 Vendredi, 9. octobre 2009 10:31 10

258

Partie IV

Patterns dopration

com.oozinoz.robotInterpreter2

Term

eval():Machine

Variable

Constant

name:String

machine:Machine

Variable(name:String)

Constant(m:Machine)

assign(t:Term)

equals(:Object):boolean

equals(:Object):boolean

eval():Machine

eval():Machine

Figure 25.3
La hirarchie Term fournit des variables pouvant reprsenter des machines.

La hirarchie Term est semblable la hirarchie Command en ce quune certaine


opration, en loccurrence eval(), apparat tous les niveaux. Vous pourriez
penser que cette hirarchie est elle-mme un exemple de INTERPRETER, malgr
labsence de classes de composition, telles que CommandSequence, qui accompagnent
gnralement ce pattern.
Cette hirarchie permet de nommer des machines individuelles en tant que constantes et dassigner des variables ces constantes ou dautres variables. Elle apporte
aussi plus de souplesse aux classes INTERPRETER spcifiques un domaine. Par
exemple, le code de StartUpCommand peut tre conu pour fonctionner avec un
objet Term plutt quavec une machine spcifique :
package com.oozinoz.robotInterpreter2;
import com.oozinoz.machine.Machine;
public class StartUpCommand extends Command {
protected Term term;

pattern Livre Page 259 Vendredi, 9. octobre 2009 10:31 10

Chapitre 25

INTERPRETER

259

public StartUpCommand(Term term) {


this.term = term;
}
public void execute() {
Machine m = term.eval();
m.startup();
}
}

De mme, pour ajouter plus de souplesse la classe CarryCommand, nous pouvons


la modifier pour quelle fonctionne avec des objets Term:
package com.oozinoz.robotInterpreter2;
public class CarryCommand extends Command {
protected Term from;
protected Term to;
public CarryCommand(Term fromTerm, Term toTerm) {
from = fromTerm;
to = toTerm;
}
public void execute() {
Robot.singleton.carry(from.eval(), to.eval());
}
}

Aprs avoir conu la hirarchie Command pour quelle fonctionne avec des objets
Term, nous pouvons crire la classe ForCommand de faon quelle dfinisse la valeur
dune variable et excute une commande body dans une boucle :
package com.oozinoz.robotInterpreter2;
import
import
import
import

java.util.List;
com.oozinoz.machine.Machine;
com.oozinoz.machine.MachineComponent;
com.oozinoz.machine.MachineComposite;

public class
protected
protected
protected

ForCommand extends Command {


MachineComponent root;
Variable variable;
Command body;

public ForCommand(
MachineComponent mc, Variable v, Command body) {
this.root = mc;
this.variable = v;
this.body = body;
}

pattern Livre Page 260 Vendredi, 9. octobre 2009 10:31 10

260

Partie IV

Patterns dopration

public void execute() {


execute(root);
}
private void execute(MachineComponent mc) {
if (mc instanceof Machine) {
// Exercice !
return;
}
MachineComposite comp = (MachineComposite) mc;
List children = comp.getComponents();
for (int i = 0; i < children.size(); i++) {
MachineComponent child =
(MachineComponent) children.get(i);
execute(child);
}
}
}

Le code execute() de la classe ForCommand recourt au transtypage (casting) pour


parcourir un arbre de composant-machine. Le Chapitre 28, sur le pattern ITERATOR,
prsente des techniques plus rapides et plus lgantes pour explorer un composite.
Pour le pattern INTERPRETER, limportant est dinterprter correctement la requte
execute() pour chaque nud de larbre.
Exercice 25.1
Compltez le code de la mthode execute() de la classe ForCommand.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.

La classe ForCommand nous permet de commencer composer des programmes, ou


scripts, de commandes pour lunit de production. Voici par exemple un programme
qui compose un objet interprteur qui arrte toutes les machines dans lunit :
package app.interpreter;
import com.oozinoz.machine.*;
import com.oozinoz.robotInterpreter2.*;
class ShowDown {
public static void main(String[] args) {

pattern Livre Page 261 Vendredi, 9. octobre 2009 10:31 10

Chapitre 25

INTERPRETER

261

MachineComposite dublin = OozinozFactory.dublin();


Variable v = new Variable("machine");
Command c = new ForCommand(
dublin, v, new ShutDownCommand(v));
c.execute();
}
}

Lorsque ce programme appelle la mthode execute(), lobjet ForCommand c


linterprte en traversant le composant-machine fourni et, pour chaque machine :
m

Il dfinit la valeur de la variable v.

Il invoque lopration execute() de lobjet ShutDownCommand fourni.

Si nous ajoutons des classes qui contrlent le flux logique, telles quune classe
IfCommand et une classe WhileCommand, nous pouvons crer un interprteur riche
en fonctionnalits. Ces classes doivent disposer dun moyen pour modliser une
condition boolenne. Nous pourrions par exemple avoir besoin de modliser si une
variable machine est gale une certaine machine. A cet effet, nous pourrions introduire une nouvelle hirarchie dobjets Term, bien quil soit plus simple demprunter
une ide du langage C : nous disons que null signifie faux et que tout le reste signifie
vrai. Nous pouvons ainsi tendre la hirarchie Term, comme le montre la Figure 25.4.
Figure 25.4
Term

La hirarchie Term
inclut des classes
qui modlisent
des conditions
boolennes.

eval():Machine

Equals
term1:Term
term2:Term

HasMaterial
term:Term
hasMaterial(t:Term)

equals(t1:Term,t2:Term)

eval():Machine

eval():Machine

Variable

Constant

pattern Livre Page 262 Vendredi, 9. octobre 2009 10:31 10

262

Partie IV

Patterns dopration

La classe Equals compare deux termes et retourne null pour signifier que la condition dgalit est fausse. Une conception raisonnable serait de faire en sorte que la
mthode eval() de cette classe retourne un de ses termes si lgalit est vraie,
comme suit :
package com.oozinoz.robotInterpreter2;
import com.oozinoz.machine.Machine;
public class Equals extends Term {
protected Term term1;
protected Term term2;
public Equals(Term term1, Term term2) {
this.term1 = term1;
this.term2 = term2;
}
public Machine eval() {
Machine m1 = term1.eval();
Machine m2 = term2.eval();
return m1.equals(m2) ? m1 : null;
}
}

La classe HasMaterial tend lide dune valeur de classe boolenne un exemple


spcifique un domaine :
package com.oozinoz.robotInterpreter2;
import com.oozinoz.machine.Machine;
public class HasMaterial extends Term {
protected Term term;
public HasMaterial(Term term) {
this.term = term;
}
public Machine eval() {
Machine m = term.eval();
return m.hasMaterial() ? m : null;
}
}

Maintenant que nous avons ajout lide de termes boolens notre package interprteur, nous pouvons ajouter des classes de contrle de flux, comme illustr
Figure 25.5.

pattern Livre Page 263 Vendredi, 9. octobre 2009 10:31 10

Chapitre 25

INTERPRETER

263

Figure 25.5
Nous pouvons
obtenir un interprteur plus riche
en fonctionnalits en
ajoutant sa hirarchie des classes de
contrle de flux.

Command

execute()

CommandSequence

CarryCommand

ForCommand

StartUpCommand

IfCommand

ShutdownCommand

WhileCommand

NullCommand

La classe NullCommand est utile pour les cas o nous avons besoin dune
commande qui ne fait rien, comme lorsque la branche else dune instruction if est
vide :
package com.oozinoz.robotInterpreter2;
public class NullCommand extends Command {
public void execute() {
}
}
package com.oozinoz.robotInterpreter2;
public class
protected
protected
protected

IfCommand extends Command {


Term term;
Command body;
Command elseBody;

public IfCommand(
Term term, Command body, Command elseBody) {
this.term = term;
this.body = body;
this.elseBody = elseBody;
}

pattern Livre Page 264 Vendredi, 9. octobre 2009 10:31 10

264

Partie IV

Patterns dopration

public void execute() {


// Exercice !
}
}

Exercice 25.2
Compltez le code de la mthode execute() de la classe IfCommand.

Exercice 25.3
Ecrivez le code de la classe WhileCommand.

Nous pourrions utiliser la classe WhileCommand avec un interprteur qui dcharge


une presse toiles :
package app.interpreter;
import com.oozinoz.machine.*;
import com.oozinoz.robotInterpreter2.*;
public class ShowWhile {
public static void main(String[] args) {
MachineComposite dublin = OozinozFactory.dublin();
Term starPress = new Constant(
(Machine) dublin.find("StarPress:1401"));
Term fuser = new Constant(
(Machine) dublin.find("Fuser:1101"));
starPress.eval().load(new Bin(77));
starPress.eval().load(new Bin(88));
WhileCommand command = new WhileCommand(
new HasMaterial(starPress),
new CarryCommand(starPress, fuser));
command.execute();
}
}

Lobjet command est un interprteur qui interprte execute() pour signifier de


dcharger toutes les caisses de la presse 1401.

pattern Livre Page 265 Vendredi, 9. octobre 2009 10:31 10

Chapitre 25

INTERPRETER

265

Exercice 25.4
Fermez ce livre et expliquez brivement la diffrence entre COMMAND et INTERPRETER.

Nous pourrions ajouter dautres classes la hirarchie de linterprteur pour avoir


plus de types de contrle ou pour des tches relevant dautres domaines. Nous pourrions aussi tendre la hirarchie Term. Par exemple, il pourrait tre utile de disposer
dune sous-classe Term qui localise un tampon de dchargement proche dune autre
machine.
Les utilisateurs des hirarchies Command et Term peuvent composer des "programmes" dexcution complexes. Par exemple, il ne serait pas trop difficile de crer
un objet qui, lorsquil sexcute, dcharge tous les produits de toutes les machines,
lexception des tampons de dchargement. Voici le pseudo-code dun tel
programme :
for (m dans usine)
if (not (m est tamponDechargement))
td = chercheMethDecharg pour m
while (m contientProduit)
transporte (m, td)

Si nous crivions du code Java pour accomplir ces tches, le rsultat serait plus
volumineux et moins simple que le pseudo-code. Aussi, pourquoi ne pas transformer ce dernier en du code rel en crant un analyseur syntaxique capable de lire un
langage spcifique un domaine pour manipuler les produits de lusine et de crer
des objets interprteur notre place ?

Interprteurs, langages et analyseurs syntaxiques


Le pattern INTERPRETER spcifie comment les interprteurs fonctionnent mais pas
comment il faut les instancier ou les composer. Dans ce chapitre, vous avez cr des
interprteurs manuellement, en crivant directement les lignes de code Java. Une
approche plus courante est dutiliser un analyseur syntaxique (parser), cest-dire un objet qui peut reconnatre du texte et dcomposer sa structure partir dun
ensemble de rgles pour le mettre dans une forme adapte un traitement subsquent. Vous pourriez par exemple crire un analyseur qui cre un interprteur de
commandes machine correspondant au pseudo-code prsent plus haut.

pattern Livre Page 266 Vendredi, 9. octobre 2009 10:31 10

266

Partie IV

Patterns dopration

Au moment de la rdaction du prsent livre, il existait peu doutils pour analyser le


code Java et seulement quelques ouvrages sur le sujet. Pour dterminer si un
support plus large a t dvelopp depuis, recherchez sur le Web la chane "Java
parser tools". La plupart des kits doutils danalyse incluent un gnrateur de parser.
Pour lutiliser, vous devez employer une syntaxe spciale dcrivant la grammaire de
votre langage, et loutil gnrera un analyseur partir de votre description. Cet
analyseur reconnatra ensuite les instances de votre langage. Vous pouvez sinon
crire vous-mme un analyseur caractre gnral en appliquant le pattern INTERPRETER. Louvrage Building Parsers with JavaTM [Metsker 2001] explique cette
technique, avec des exemples en Java.

Rsum
Le pattern INTERPRETER permet de composer des objets excutables partir dune
hirarchie de classes que vous crez. Chaque classe implmente une opration
commune qui possde habituellement un nom gnrique, tel que execute(). Bien
que les exemples de ce chapitre ne le montrent pas, cette mthode reoit souvent un
objet "contexte" additionnel qui stocke un tat important.
Le nom de chaque classe reflte gnralement la faon dont elle implmente, ou
interprte, lopration commune. Chacune delles dfinit un moyen de composer
des commandes ou bien reprsente une commande terminale qui entrane une
action.
Les interprteurs saccompagnent souvent dune conception supportant lintroduction de variables et dexpressions boolennes ou arithmtiques. Ils collaborent
souvent aussi avec un analyseur syntaxique pour crer un petit langage qui simplifie
la gnration de nouveaux objets interprteur.

pattern Livre Page 267 Vendredi, 9. octobre 2009 10:31 10

V
Patterns dextension

pattern Livre Page 268 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 269 Vendredi, 9. octobre 2009 10:31 10

26
Introduction aux extensions
Lorsque vous programmez en Java, vous ne partez pas de zro mais "hritez" de
toute la puissance des bibliothques de classes de ce langage. Vous hritez aussi
habituellement du code de vos prdcesseurs et de vos collgues. Lorsque vous ne
rorganisez ou namliorez pas le code existant, vous ltendez. Vous pourriez dire
que la programmation en Java est une extension. Il vous est peut-tre dj arriv
dhriter dune base de code dont la qualit tait mdiocre. Mais le code que vous
ajoutez est-il rellement meilleur ? La rponse est parfois subjective. Ce chapitre
passe en revue certains principes du dveloppement orient objet qui vous permettront
dvaluer la qualit de votre travail.
Outre les techniques classiques dextension dune base de code, vous pourriez
appliquer des patterns de conception pour ajouter de nouveaux comportements.
Aprs avoir expos les principes de la conception oriente objet dans le cadre dun
dveloppement ordinaire, cette section revoit certains patterns contenant un lment
dextension et introduit ceux non encore abords.

Principes de la conception oriente objet


Les ponts de pierre existent depuis plusieurs milliers dannes, ce qui a laiss
lhomme le temps de dvelopper des principes de conception bien prouvs. La
programmation oriente objet existe, elle, depuis peut-tre cinquante ans, aussi
nest-il pas surprenant que ses principes de conception en soient un stade moins
avanc. Nous disposons toutefois dexcellents forums sur les principes reconnus
actuellement, lun des meilleurs tant Portland Pattern Repository ladresse
www.c2.com [Cunningham]. Vous trouverez sur ce site les principes les plus efficaces pour valuer des conceptions OO. Lun deux, notamment, est le principe de
substitution de Liskov.

pattern Livre Page 270 Vendredi, 9. octobre 2009 10:31 10

270

Partie V

Patterns dextension

Le principe de substitution de Liskov


Les nouvelles classes devraient tre des extensions logiques et cohrentes de leurs
super-classes. Un compilateur Java garantit un certain niveau de cohrence, mais
nombre des principes de cohrence lui chappent. Une rgle qui peut vous aider
amliorer vos conceptions est le principe de substitution de Liskov, ou LSP (Liskov
Substitution Principle) [Liskov 1987], qui peut tre paraphras comme suit :
Une instance dune classe devrait fonctionner comme une instance de sa superclasse.
Une conformit basique avec ce principe est intgre aux langages OO, tels que Java.
Par exemple, il est valide de se rfrer un objet UnloadBuffer (tampon de dchargement) en tant que Machine puisque UnloadBuffer est une sous-classe de
Machine:
Machine m = new UnloadBuffer(3501);

Certains aspects de la conformit avec LSP font appel lintelligence humaine, ou


en tout cas plus dintelligence que nen possdent les compilateurs actuels. Considrez les hirarchies de classes de la Figure 26.1.
Figure 26.1
Ce diagramme
sapplique aux
questions suivantes :
un tampon de
dchargement est-il
une machine ?
Un cercle est-il une
ellipse ?

Machine

Ellipse

addTub(t:Tub)

setWidth()

getTubs():List

setHeight()

UnloadBuffer

addTub(t:Tub)

Circle

setRadius()

getTubs():List

Un tampon de dchargement est certainement une machine, mais modliser ce fait


dans une hirarchie de classes peut donner lieu des problmes. Chez Oozinoz,
toutes les machines, lexception des tampons de dchargement, peuvent recevoir
un bac (tub) de produits chimiques et signaler les bacs qui se trouvent ct delles.
Aussi est-il utile de remonter ce comportement au niveau de la classe Machine.

pattern Livre Page 271 Vendredi, 9. octobre 2009 10:31 10

Chapitre 26

Introduction aux extensions

271

Mais cest une erreur que dinvoquer addTub() ou getTubs() sur un objet UnloadBuffer. Devrions-nous gnrer des exceptions si de tels appels avaient lieu ?
Supposez quun autre dveloppeur crive une mthode qui interroge toutes les
machines dune trave pour crer une liste complte de tous les bacs de produits
prsents dans cette trave. Lorsque ce code arrive un tampon de dchargement, il
rencontre une exception si la mthode getTubs() de la classe UnloadBuffer en
gnre une. Il sagit dune violation totale du principe de Liskov : si vous utilisez un
objet UnloadBuffer en tant quobjet Machine, votre programme peut planter. Au
lieu de gnrer une exception, imaginez que nous dcidions dignorer simplement
les appels de getTubs() et addTub() dans la classe UnloadBuffer, ce qui reprsente toujours une violation de ce principe : si vous ajoutez un bac une machine,
le bac risque de disparatre.
Ces violations ne constituent pas toujours des erreurs de conception. Dans le cas
des machines dOozinoz, il convient dvaluer lintrt de placer dans la classe
Machine des comportements qui sappliquent presque toutes les machines par
rapport aux inconvnients lis au non-respect de LSP. Limportant est de connatre
ce principe et dtre capable de dterminer en quoi certaines considrations de
conception justifient sa violation.
Exercice 26.1
Un cercle est certainement un cas spcial dellipse. Dterminez nanmoins si la
relation des classes Ellipse et Circle dans la Figure 26.1 est une violation de LSP.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.


La loi de Demeter
Vers la fin des annes 80, des membres du Demeter Project la Northeastern
University de Boston ont tent de codifier les rgles garantissant la "bonne sant"
dun programme, leur attribuant le nom de loi de Demeter, ou LOD (Law Of Demeter).
Karl Lieberherr et Ian Holland [1989] en ont donn une description dtaille dans
un article intitul "Assuring good style for object-oriented programs", qui dit ceci :
De manire informelle, la loi stipule que chaque mthode peut envoyer des
messages uniquement un ensemble limit dobjets : aux objets argument, la
pseudo-variable [this] et aux sous-parties immdiates de [this].

pattern Livre Page 272 Vendredi, 9. octobre 2009 10:31 10

272

Partie V

Patterns dextension

Larticle donne galement une dfinition plus formelle, mais il est plus facile de
donner des exemples de violations de cette loi que de tenter dexpliquer son propos.
Supposez que vous disposiez dun objet MaterialManager avec une mthode qui
reoit un objet Tub en tant que paramtre. Les objets Tub possdent une proprit
Location qui retourne lobjet Machine reprsentant lemplacement du bac. Dans la
mthode de MaterialManager, vous avez besoin de savoir si la machine est active
et disponible. Vous pourriez crire le code suivant cet effet :
if (tub.getLocation().isUp()) {
//...
}

Ce code enfreint la loi de Demeter car il envoie un message tub.getLocation().


Lobjet tub.getLocation() nest pas un paramtre, nest pas this lobjet
MaterialManager dont la mthode est excute et nest pas non plus un attribut de
this.
Exercice 26.2
Expliquez pourquoi lexpression tub.getLocation().isUp() peut tre considre comme tant de prfrence viter.

Cet exercice pourrait banaliser la valeur de cette loi sil suggrait quelle signifie
seulement que les expressions de la forme a.b.c sont viter. En fait, Lieberherr et
Holland lui donnent un sens plus large et rpondent affirmativement la question :
"Existe-t-il une formule ou une rgle permettant dcrire des programmes orients
objet de bonne qualit ?" Je vous conseille de lire larticle original expliquant cette
loi. A linstar du principe de Liskov, elle vous aidera dvelopper de meilleurs
programmes si vous connaissez les rgles, savez les suivre, et savez aussi quand
votre conception peut les enfreindre.
Vous pouvez constater quen suivant un ensemble de recommandations, vos extensions produisent automatiquement du code performant. Mais, aux yeux de
nombreux programmeurs, le dveloppement OO reste un art. Lextension efficace
dune base de code semble tre le rsultat dun ensemble de pratiques labores par
des artisans qui en sont encore formuler et codifier leur art. La refactorisation
dsigne lemploi dune collection doutils de modification de code conus pour
amliorer la qualit dune base de code sans changer sa fonction.

pattern Livre Page 273 Vendredi, 9. octobre 2009 10:31 10

Chapitre 26

Introduction aux extensions

273

Elimination des erreurs potentielles


Vous pourriez penser que le principe de substitution de Liskov et la loi de Demeter
vous empcheront dcrire du code mdiocre. En fait, vous utiliserez plutt ces
rgles pour identifier le code de mauvaise qualit et lamliorer. Cest une pratique
normale : crire du code qui tourne puis le perfectionner en identifiant et en corrigeant les problmes potentiels. Mais comment identifie-t-on ces problmes ? Au
moyen dindicateurs (code smell). Louvrage Refactoring: Improving the Design
of Existing Code [Fowler et al. 1999], en dnombre vingt-deux et dcrit les refactorisations correspondantes.
Le prsent livre a eu recours de nombreuses fois la refactorisation pour rorganiser et amliorer du code existant en appliquant un pattern. Mais vous navez pas
ncessairement besoin dappliquer un pattern de conception lors de la refactorisation. Chaque fois que le code dune mthode est susceptible de poser problme, il
devrait tre revu.
Exercice 26.3
Donnez un exemple dune mthode pouvant tre refactorise sans enfreindre
pour autant le principe de Liskov ou la loi de Demeter.

Au-del des extensions ordinaires


Lobjectif de beaucoup de patterns de conception, dont nombre dentre eux ont dj
t couverts, a trait de prs ou de loin lextension de comportements. Ces patterns
clarifient souvent le rle de deux dveloppeurs. Par exemple, dans le pattern ADAPTER,
un dveloppeur peut fournir un service utile ainsi quune interface pour les objets
qui veulent utiliser ce service.
En plus des patterns dj couverts, trois autres patterns ont comme principal objectif
dtendre du code existant.
Si vous envisagez de

Appliquez le pattern

Permettre aux dveloppeurs de composer dynamiquement le comportement dun objet

DECORATOR

pattern Livre Page 274 Vendredi, 9. octobre 2009 10:31 10

274

Partie V

Patterns dextension

Si vous envisagez de

Appliquez le pattern

Offrir un moyen daccder aux lments dune collection de faon


squentielle

ITERATOR

Permettre aux dveloppeurs de dfinir une nouvelle opration pour


une hirarchie sans changer les classes qui la composent

VISITOR

Exercice 26.4
Compltez le tableau suivant, qui donne des exemples dutilisation des patterns
dj abords pour tendre le comportement dune classe ou dun objet.
Exemple

Pattern luvre

Le concepteur dune simulation pyrotechnique tablit une interface


qui dfinit le comportement que doit prsenter votre objet pour
pouvoir prendre part la simulation.

ADAPTER

Un kit doutils permet de composer des objets excutables lors


de lexcution.

TEMPLATE METHOD

COMMAND

Un gnrateur de code insre un comportement qui donne lillusion


quun objet sexcutant sur une autre machine est local.

OBSERVER

Une conception permet de dfinir des oprations abstraites


qui dpendent dune interface spcifique et dajouter de nouveaux
drivers qui satisfont aux exigences de cette interface.

Rsum
Ecrire du code revient souvent tendre celui existant pour apporter de nouvelles
fonctionnalits, puis le rorganiser afin damliorer sa qualit. Il nexiste pas de
technique infaillible pour valuer la qualit dun programme, mais certains principes
de bonne conception OO ont nanmoins t dfinis.

pattern Livre Page 275 Vendredi, 9. octobre 2009 10:31 10

Chapitre 26

Introduction aux extensions

275

Le principe de substitution de Liskov suggre quune instance dune classe devrait


fonctionner comme une instance de sa super-classe. Vous devriez connatre et tre
capable de justifier les violations de ce principe dans votre code. La loi de Demeter
est un ensemble de rgles qui aident rduire les dpendances entre les classes et
clarifier le code.
Martin Fowler et al. [1999] ont labor une srie dindicateurs permettant didentifier le code de qualit mdiocre. Chaque indicateur donne lieu une ou plusieurs
refactorisations, certaines dentre elles visant lapplication dun pattern de conception.
Nombre de patterns servent de techniques pour clarifier, simplifier ou faciliter les
extensions.

pattern Livre Page 276 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 277 Vendredi, 9. octobre 2009 10:31 10

27
DECORATOR
Pour tendre une base de code, vous lui ajoutez normalement de nouvelles classes
ou mthodes. Parfois, vous avez besoin de composer un objet avec un nouveau
comportement lors de lexcution. Le pattern INTERPRETER, par exemple, permet
de gnrer un objet excutable dont le comportement change radicalement selon la
faon dont vous le composez. Dans certains cas, vous aurez besoin de pouvoir
combiner plusieurs variations comportementales moins importantes et utiliserez
pour cela le pattern DECORATOR.
Lobjectif du pattern DECORATOR est de vous permettre de composer de nouvelles
variations dune opration lors de lexcution.

Un exemple classique : flux dE/S et objets Writer


La conception densemble des flux dentre et de sortie dans les bibliothques de
classes Java constitue un exemple classique du pattern DECORATOR. Un flux (stream)
est une srie doctets ou de caractres, tels ceux contenus dans un document. Dans
Java, les classes dcriture, ou Writer, reprsentent une faon de supporter les flux.
Certaines de ces classes possdent un constructeur qui accepte un objet Writer, ce
qui signifie que vous pouvez crer un Writer partir dun Writer. Ce type de composition simple est la structure typique du pattern DECORATOR, qui est prsent dans
les classes dcriture Java. Mais, comme nous le verrons, il ne faut pas beaucoup
de code DECORATOR pour nous permettre dtendre grandement notre capacit
combiner des variations doprations de lecture et dcriture.

pattern Livre Page 278 Vendredi, 9. octobre 2009 10:31 10

278

Partie V

Patterns dextension

Pour un exemple de DECORATOR dans Java, considrez le code suivant qui cre un
petit fichier texte :
package app.decorator;
import java.io.*;
public class ShowDecorator {
public static void main(String[] args)
throws IOException {
FileWriter file = new FileWriter("sample.txt");
BufferedWriter writer = new BufferedWriter(file);
writer.write("un petit exemple de texte");
writer.newLine();
writer.close();
}
}

Lexcution de ce programme produit un fichier sample.txt qui contient une petite


quantit de texte. Le code utilise un objet FileWriter pour crer un fichier, en enveloppant cet objet dans un objet BufferedWriter. Ce quil importe de retenir ici, cest
que nous composons un flux, BufferedWriter, partir dun autre flux, FileWriter.
Chez Oozinoz, le personnel de vente a besoin de mettre en forme des messages
personnaliss partir du texte stock dans la base de donnes de produits. Ces
messages nutilisent pas des polices ou des styles trs varis. Nous crerons pour
cela un framework de dcorateurs. Ces classes nous permettront de composer une
grande varit de filtres de sortie.
Figure 27.1
Writer

La classe OozinozFilter est parente


des classes qui
mettent en forme
les flux de caractres
en sortie.

FilterWriter

FilterWriter(:Writer)
close()
write(:char[],offset:int,length:int)
write(ch:int)
write(:String,offset:int,length:int)
...

OozinozFilter

pattern Livre Page 279 Vendredi, 9. octobre 2009 10:31 10

Chapitre 27

DECORATOR

279

Pour dvelopper une collection de classes de filtrage, il est utile de crer une classe
abstraite qui dfinit les oprations que ces filtres doivent supporter. En slectionnant
des oprations qui existent dj dans la classe Writer, vous pouvons crer presque
sans effort une autre classe qui hrite tous ses comportements de cette clas se.
La Figure 27.1 illustre cette conception.
Notre super-classe de filtrage doit possder plusieurs attributs essentiels pour
pouvoir supporter des flux de sortie composables :
m

Elle doit accepter dans son constructeur un objet Writer.

Elle doit agir en tant que super-classe dune hirarchie de filtres.

Elle doit fournir des implmentations par dfaut de toutes les mthodes dcriture sauf write(:int).

La Figure 27.2 illustre cette conception.


Figure 27.2
Writer

Le constructeur de la
classe OozinozFilter accepte
une instance de
nimporte quelle
sous-classe de
Writer.

FilterWriter

OozinozFilter

OozinozFilter(:Writer)
write(:char[],offset:int,length:int)
write(c:int)
write(:String,offset:int,length:int)
...

LowerCaseFilter

UpperCaseFilter

RandomCaseFilter

WrapFilter

CommaListFilter

pattern Livre Page 280 Vendredi, 9. octobre 2009 10:31 10

280

Partie V

Patterns dextension

La classe OozinozFilter rpond aux exigences de conception en peu de lignes :


package com.oozinoz.filter;
import java.io.*;
public abstract class OozinozFilter extends FilterWriter {
protected OozinozFilter(Writer out) {
super(out);
}
public void write(char cbuf[], int offset, int length)
throws IOException {
for (int i = 0; i < length; i++)
write(cbuf[offset + i]);
}
public abstract void write(int c) throws IOException;
public void write(String s, int offset, int length)
throws IOException {
write(s.toCharArray(), offset, length);
}
}

Ce code est tout ce dont nous avons besoin pour faire intervenir DECORATOR. Les
sous-classes de OozinozFilter peuvent fournir de nouvelles implmentations
de write(:int) qui modifient un caractre avant de le passer la mthode
write(:int) du flux sous-jacent. Les autres mthodes de OozinozFilter fournissent le comportement typiquement requis par les sous-classes. Cette classe laisse
simplement les appels de close() et flush() sa classe parent, FilterWriter.
Elle interprte galement write(:char[]) par rapport la mthode write(:int)
quelle laisse abstraite.
A prsent, il est ais de crer et dutiliser de nouveaux filtres de flux. Par exemple,
le code suivant convertit le texte en minuscules :
package com.oozinoz.filter;
import java.io.*;
public class LowerCaseFilter extends OozinozFilter {
public LowerCaseFilter(Writer out) {
super(out);
}
public void write(int c) throws IOException {
out.write(Character.toLowerCase((char) c));
}
}

pattern Livre Page 281 Vendredi, 9. octobre 2009 10:31 10

Chapitre 27

DECORATOR

281

Voici un exemple de programme qui utilise un filtre de conversion en minuscules :


package app.decorator;
import java.io.IOException;
import java.io.Writer;
import com.oozinoz.filter.ConsoleWriter;
import com.oozinoz.filter.LowerCaseFilter;
public class ShowLowerCase {
public static void main(String[] args)
throws IOException {
Writer out = new ConsoleWriter();
out = new LowerCaseFilter(out);
out.write("Ce Texte doit tre crit TOUT en MiNusculeS !");
out.close();
}
}

Ce programme affiche "ce texte doit tre crit tout en minuscules !" sur la console.
Le code de la classe UpperCaseFilter est identique celui de LowerCaseFilter,
lexception de la mthode write(), que voici :
public void write(int c) throws IOException {
out.write(Character.toUpperCase((char) c));
}

Le code de la classe TitleCaseFilter est un peu plus complexe puisquil doit


garder trace des espaces :
package com.oozinoz.filter;
import java.io.*;
public class TitleCaseFilter extends OozinozFilter {
boolean inWhite = true;
public TitleCaseFilter(Writer out) {
super(out);
}
public void write(int c) throws IOException {
out.write(
inWhite
? Character.toUpperCase((char) c)
: Character.toLowerCase((char) c));
inWhite = Character.isWhitespace((char) c)
|| c == ";
}
}

pattern Livre Page 282 Vendredi, 9. octobre 2009 10:31 10

282

Partie V

Patterns dextension

La classe CommaListFilter insre une virgule entre des lments :


package com.oozinoz.filter;
import java.io.IOException;
import java.io.Writer;
public class CommaListFilter extends OozinozFilter {
protected boolean needComma = false;
public CommaListFilter(Writer writer) {
super(writer);
}
public void write(int c) throws IOException {
if (needComma) {
out.write(,);
out.write( );
}
out.write(c);
needComma = true;
}
public void write(String s) throws IOException {
if (needComma)
out.write(", ");
out.write(s);
needComma = true;
}
}

Le rle de ces filtres est le mme : la tche de dveloppement consiste remplacer


les mthodes write() appropries. Ces mthodes mettent en forme le flux de texte
reu puis le passent un flux subordonn.
Exercice 27.1
Ecrivez le code de RandomCaseFilter.java.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.

Le code de la classe WrapFilter est beaucoup plus complexe que les autres filtres.
Il aligne son rsultat au centre et doit donc mettre en tampon et compter les caractres avant de les passer son flux subordonn. Vous pouvez examiner ce code en le

pattern Livre Page 283 Vendredi, 9. octobre 2009 10:31 10

Chapitre 27

DECORATOR

283

tlchargeant partir de www.oozinoz.com (voir lAnnexe C pour les instructions


de tlchargement du code).
Le constructeur de WrapFilter accepte un objet Writer ainsi quun paramtre de
largeur lui indiquant quand aller la ligne. Vous pouvez combiner ce filtre et
dautres filtres pour crer une varit deffets. Par exemple, le programme suivant
aligne au centre le texte dun fichier en entre, en insrant des retours la ligne et
en mettant en capitale la premire lettre de chaque mot :
package app.decorator;
import java.io.*;
import com.oozinoz.filter.TitleCaseFilter;
import com.oozinoz.filter.WrapFilter;
public class ShowFilters {
public static void main(String args[])
throws IOException {
BufferedReader in = new BufferedReader(
new FileReader(args[0]));
Writer out = new FileWriter(args[1]);
out = new WrapFilter(new BufferedWriter(out), 40);
out = new TitleCaseFilter(out);
String line;
while ((line = in.readLine()) != null)
out.write(line + "\n");
out.close();
in.close();
}
}

Pour voir le rsultat de ce programme, supposez quun fichier adcopy.txt


contienne le texte suivant :
The "SPACESHOT" shell hovers
at 100 meters for 2 to 3
minutes, erupting star bursts every 10 seconds that
generate ABUNDANT reading-level light for a
typical stadium.

Vous pourriez excuter le programme ShowFilters partir de la ligne de commande,


comme ceci :
>ShowFilters adcopy.txt adout.txt

Le contenu du fichier adout.txt apparatrait ainsi :


The "Spaceshot" Shell Hovers At 100
Meters For 2 To 3 Minutes, Erupting Star

pattern Livre Page 284 Vendredi, 9. octobre 2009 10:31 10

284

Partie V

Patterns dextension

Bursts Every 10 Seconds That Generate


Abundant Reading-level Light For A
Typical Stadium.

Plutt que dcrire dans un fichier, il peut tre utile denvoyer les caractres vers la
console. La Figure 27.3 illustre la conception dune classe qui tend Writer et
dirige la sortie vers la console.
Figure 27.3
Writer

Un objet ConsoleWriter
peut servir dargument au
constructeur de nimporte
laquelle des sous-classes de
OozinozFilter.

FilterWriter

OozinozFilter

OozinozFilter(:Writer)
write(:char[],offset:int,length:int)
write(c:int)
write(:String,offset:int,length:int)
...

LowerCaseFilter

UpperCaseFilter

RandomCaseFilter

WrapFilter

CommaListFilter

Exercice 27.2
Ecrivez le code de ConsoleWriter.java.

pattern Livre Page 285 Vendredi, 9. octobre 2009 10:31 10

Chapitre 27

DECORATOR

285

Les flux dentre et de sortie reprsentent un exemple classique de la faon dont le


pattern DECORATOR permet dassembler le comportement dun objet au moment de
lexcution. Une autre application importante de ce pattern intervient lorsque vous
avez besoin de crer des fonctions mathmatiques lexcution.

Enveloppeurs de fonctions
Le principe de composer de nouveaux comportements lors de lexcution au moyen
du pattern DECORATOR sapplique aussi bien aux flux dentre/sortie quaux fonctions mathmatiques. La possibilit de crer des fonctions lors de lexcution est
quelque chose dont vous pouvez faire profiter les utilisateurs, en leur permettant de
spcifier de nouvelles fonctions via une interface GUI ou un petit langage. Vous
pouvez aussi simplement vouloir rduire le nombre de mthodes prsentes dans
votre code et offrir davantage de souplesse en crant des fonctions mathmatiques
en tant quobjets.
Pour crer une bibliothque de dcorateurs de fonctions, ou "enveloppeurs" de
fonctions, nous pouvons appliquer une structure semblable celle utilise pour les
flux dentre/sortie. Nous nommerons Function la super-classe enveloppeur. Pour
la conception initiale de cette classe, nous pourrions copier la conception de la
classe OozinozFilter, comme illustr Figure 27.4.
La classe OozinozFilter tend FilterWriter et son constructeur sattend recevoir un objet Writer. La conception de la classe Function est analogue, sauf quau
lieu de recevoir un seul objet IFunction, elle accepte un tableau. Certaines fonctions, telles celles arithmtiques, ncessitent plus dune fonction subordonne.
Dans le cas des enveloppeurs de fonctions, aucune classe existante telle que Writer
nimplmente lopration dont nous avons besoin. Nous pouvons donc nous passer
dune interface IFunction et dfinir plus simplement la hirarchie Function sans
cette interface, comme le montre la Figure 27.5.
A linstar de la classe OozinozFilter, la classe Function dfinit une opration
commune que ses sous-classes doivent implmenter. Un choix naturel pour le nom
de cette opration est f. Nous pourrions prvoir dimplmenter des fonctions paramtriques fondes sur un paramtre de temps qui varie de 0 1 (reportez-vous
lencadr sur les quations paramtriques du Chapitre 4 pour une prsentation du
sujet).

pattern Livre Page 286 Vendredi, 9. octobre 2009 10:31 10

286

Partie V

Patterns dextension

Figure 27.4
Writer

La conception initiale de la
hirarchie denveloppeurs
de fonctions ressemble
beaucoup la conception
des flux dentre/sortie.

FilterWriter

OozinozFilter

OozinozFilter(:Writer)
write(:char[],offset:int,length:int)
write(c:int)
write(:String,offset:int,length:int)
...

LowerCaseFilter

UpperCaseFilter

RandomCaseFilter

WrapFilter

CommaListFilter

Figure 27.5
Une conception simplifie
pour la classe Function
fonctionne sans dfinir
dinterface spare.

*
Function

Function(
sources[]:Function)
f(:double):double

...

pattern Livre Page 287 Vendredi, 9. octobre 2009 10:31 10

Chapitre 27

DECORATOR

287

Nous allons crer une sous-classe de Function pour chaque enveloppeur. La


Figure 27.6 prsente une hirarchie Function initiale.
Figure 27.6
Function

Chaque sous-classe
de Function implmente la fonction
f(t) de sorte quelle
corresponde au nom
de la classe.

#sources[]:Function
Function(source:Function)
Function(sources[]:Function)
f(t:double):double

Abs

Cos

Abs(source:Function)

Cos(source:Function)

f(t:double):double

f(t:double):double

Arithmetic

Arithmetic(
op:char,
f1:Function,
f2:Function)

Sin

Sin(source:Function)
f(t:double):double

f(t:double):double

T()
Constant

Constant(constant:double)
f(t:double):double

f(t:double):double

pattern Livre Page 288 Vendredi, 9. octobre 2009 10:31 10

288

Partie V

Patterns dextension

Le code de la super-classe Function sert principalement dclarer le tableau de


sources :
package com.oozinoz.function;
public abstract class Function {
protected Function[] sources;
public Function(Function f) {
this(new Function[] { f });
}
public Function(Function[] sources) {
this.sources = sources;
}
public abstract double f(double t);
public String toString() {
String name = this.getClass().toString();
StringBuffer buf = new StringBuffer(name);
if (sources.length > 0) {
buf.append(();
for (int i = 0; i < sources.length; i++) {
if (i > 0)
buf.append(", ");
buf.append(sources[i]);
}
buf.append());
}
return buf.toString();
}
}

Les sous-classes de Function sont gnralement simples. Voici par exemple le


code de la classe Cos:
package com.oozinoz.function;
public class Cos extends Function {
public Cos(Function f) {
super(f);
}
public double f(double t) {
return Math.cos(sources[0].f(t));
}
}

pattern Livre Page 289 Vendredi, 9. octobre 2009 10:31 10

Chapitre 27

DECORATOR

289

Le constructeur de Cos attend un argument Function et le passe ensuite au constructeur de la super-classe, o il est stock dans le tableau de sources. La mthode Cos.f()
value la fonction source lheure t, passe cette valeur la mthode Math.Cos()
et retourne le rsultat.
Les classes Abs et Sin sont presque identiques Cos. La classe Constant vous
permet de crer un objet Function contenant une valeur constante retourner en
rponse aux appels de la mthode f(). La classe Arithmetic accepte un indicateur
doprateur quelle applique sa mthode f(). Voici le code de cette classe :
package com.oozinoz.function;
public class Arithmetic extends Function {
protected char op;
public Arithmetic(char op, Function f1, Function f2) {
super(new Function[] { f1, f2 });
this.op = op;
}
public double f(double t) {
switch (op) {
case +:
return sources[0].f(t)
case -:
return sources[0].f(t)
case *:
return sources[0].f(t)
case /:
return sources[0].f(t)
default:
return 0;
}
}

+ sources[1].f(t);
- sources[1].f(t);
* sources[1].f(t);
/ sources[1].f(t);

La classe T retourne les valeurs de t qui ont t passes. Ce comportement est utile
si vous avez besoin dune variable qui varie dans le temps de manire linaire. Par
exemple, lexpression suivante cre un objet Function dont la valeur de f() varie
de 0 2 mesure que le temps varie de 0 1 :
new Arithmetic(*, new T(), new Constant(2 * Math.PI))

Vous pouvez utiliser les classes Function pour composer de nouvelles fonctions
mathmatiques sans avoir crire de nouvelles mthodes. La classe FunPanel
accepte des arguments Function pour ses fonctions x et y. Elle adapte aussi ces

pattern Livre Page 290 Vendredi, 9. octobre 2009 10:31 10

290

Partie V

Patterns dextension

fonctions la taille du canevas. Cette classe peut tre utilise par un programme
comme le suivant :
package app.decorator;
import app.decorator.brightness.FunPanel;
import com.oozinoz.function.*;
import com.oozinoz.ui.SwingFacade;
public class ShowFun {
public static void main(String[] args) {
Function theta = new Arithmetic(
*, new T(), new Constant(2 * Math.PI));
Function theta2 = new Arithmetic(
*, new T(), new Constant(2 * Math.PI * 5));
Function x = new Arithmetic(
+, new Cos(theta), new Cos(theta2));
Function y = new Arithmetic(
+, new Sin(theta), new Sin(theta2));
FunPanel panel = new FunPanel(1000);
panel.setPreferredSize(
new java.awt.Dimension(200, 200));
panel.setXY(x, y);
SwingFacade.launch(panel, "Chrysanthemum");
}
}

Ce programme utilise une fonction qui laisse un cercle sentrelacer avec un autre
plusieurs fois. Il produit le rsultat de la Figure 27.7.
Figure 27.7
Une fonction mathmatique complexe
cre sans introduire
aucune mthode
nouvelle.

pattern Livre Page 291 Vendredi, 9. octobre 2009 10:31 10

Chapitre 27

DECORATOR

291

Pour tendre votre kit denveloppeurs de fonctions, il suffit dajouter de nouvelles


fonctions mathmatiques la hirarchie Function.
Exercice 27.3
Fermez ce livre et crivez le code de la classe enveloppeur Exp.

Supposez que la luminosit dune toile soit une onde sinusodale qui dcrot exponentiellement :
luminosit = e

4t

sin ( t )

Comme prcdemment, nous pouvons composer une fonction sans avoir crire de
nouvelles classes ou mthodes :
package app.decorator.brightness;
import com.oozinoz.function.*;
import com.oozinoz.ui.SwingFacade;
public class ShowBrightness {
public static void main(String args[]) {
FunPanel panel = new FunPanel();
panel.setPreferredSize(
new java.awt.Dimension(200, 200));
Function brightness = new Arithmetic(
*,
new Exp(
new Arithmetic(
*,
new Constant(-4),
new T())),
new Sin(
new Arithmetic(
*,
new Constant(Math.PI),
new T())));
panel.setXY(new T(), brightness);
SwingFacade.launch(panel, "Brightness");
}
}

pattern Livre Page 292 Vendredi, 9. octobre 2009 10:31 10

292

Partie V

Patterns dextension

Ce code produit la courbe de la Figure 27.8.


Figure 27.8
La luminosit dune
toile connat un pic
soudain avant de
dcrotre.

Exercice 27.4
Ecrivez le code pour dfinir un objet Brightness reprsentant la fonction de
luminosit.

A mesure que vous en avez besoin, vous pouvez ajouter dautres fonctions la
hirarchie Function. Par exemple, des classes pour la racine carre et la tangente
pourraient tre utiles. Vous pouvez aussi crer de nouvelles hirarchies applicables
diffrents types, tels que des chanes, ou impliquant une dfinition diffrente
de lopration f(). Par exemple, f() pourrait tre dfinie en tant quune fonction de
temps deux ou trois dimensions. Indpendamment de la hirarchie que vous crez,
vous pouvez utiliser le pattern DECORATOR pour dvelopper un riche ensemble de
fonctions composables lors de lexcution.

DECORATOR en relation avec dautres patterns


Le pattern DECORATOR sappuie sur une opration commune implmente travers
une hirarchie. A cet gard, il ressemble STATE, STRATEGY et INTERPRETER. Dans
DECORATOR, les classes possdent aussi habituellement un constructeur qui requiert
un autre objet dcorateur subordonn. Ce pattern ressemble sur ce point COMPOSITE. DECORATOR ressemble galement PROXY en ce que les classes dcorateur
implmentent typiquement lopration commune en transmettant lappel lobjet
dcorateur subordonn.

pattern Livre Page 293 Vendredi, 9. octobre 2009 10:31 10

Chapitre 27

DECORATOR

293

Rsum
Le pattern DECORATOR permet dassembler des variations dune mme opration.
Un exemple classique apparat dans les flux dentre/sortie, o vous pouvez composer un flux partir dun autre flux. Les bibliothques de classes Java supportent
DECORATOR dans limplmentation des flux dentre/sortie. Vous pouvez tendre ce
principe en crant votre propre ensemble de filtres dentre/sortie. Vous pouvez
galement appliquer DECORATOR pour dfinir des enveloppeurs de fonctions permettant de crer un large ensemble dobjets fonction partir dun jeu fixe de classes de
fonctions. Ce pattern donne lieu des conceptions flexibles dans les situations o
vous voulez pouvoir combiner les variations dimplmentation dune opration
commune en de nouvelles variations au moment de lexcution.

pattern Livre Page 294 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 295 Vendredi, 9. octobre 2009 10:31 10

28
ITERATOR
Etendre les fonctionnalits dun code existant en ajoutant un nouveau type de
collection peut requrir lajout dun itrateur. Ce chapitre tudie le cas particulier
de litration parcourant un objet composite. Outre litration dans de nouveaux
types de collections, le cas dun environnement multithread soulve un certain
nombre de problmes intressants qui mritent dtre analyss. Simple de prime
abord, litration est en fait un problme non totalement rsolu.
Lobjectif du pattern ITERATOR est de fournir un moyen daccder de faon
squentielle aux lments dune collection.

Itration ordinaire
Java dispose de diffrentes approches pour raliser une itration :
m

les boucles for, while et repeat, se fondant gnralement sur un indice ;

la classe Enumeration (dans java.util) ;

la classe Iterator (galement dans java.util), ajoute dans le JDK 1.2 pour
grer les collections ;
les boucles for tendues (foreach), ajoutes dans le JDK 1.5.

Nous utiliserons la classe Iterator pour une grande partie de ce chapitre, cette
section prsentant une boucle for tendue.
Une classe Iterator possde trois mthodes : hasnext(), next() et remove().
Un itrateur a le droit de gnrer une exception du type UnsupportedOperationException sil ne supporte pas lopration remove().

pattern Livre Page 296 Vendredi, 9. octobre 2009 10:31 10

296

Partie V

Patterns dextension

Une boucle for tendue suit la syntaxe suivante :


for (Type element : collection)

Cette instruction cre une boucle lisant une collection, extrayant un lment la
fois (appel ici element). Il nest pas ncessaire de convertir element en un type
particulier, cela tant gr de faon implicite. Cette construction peut aussi fonctionner avec des tableaux (array). Une classe qui souhaite autoriser des boucles
for tendues doit implmenter une interface Iterable et inclure une mthode
iterator().
Voici un programme illustrant la classe Iterator et des boucles for tendues :
package app.iterator;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ShowForeach {
public static void main(String[] args) {
ShowForeach example = new ShowForeach();
example.showIterator();
System.out.println();
example.showForeach();
}
public void showIterator() {
List names = new ArrayList();
names.add("Fuser:1101");
names.add("StarPress:991");
names.add("Robot:1");
System.out.println("Itrateur style JDK 1.2 :");
for (Iterator it = names.iterator(); it.hasNext();) {
String name = (String) it.next();
System.out.println(name);
}
}
public void showForeach() {
List<String> names = new ArrayList<String>();
names.add("Fuser:1101");
names.add("StarPress:991");
names.add("Robot:1");
System.out.println(
"Boucle FOR tendue style JDK 1.5 :");
for (String name: names)

pattern Livre Page 297 Vendredi, 9. octobre 2009 10:31 10

Chapitre 28

ITERATOR

297

System.out.println(name);
}
}

Lorsque nous excutons ce programme, nous voyons les rsultats :


Itrateur style JDK 1.2 :
Fuser:1101
StarPress:991
Robot:1
Boucle FOR tendue style JDK 1.5 :
Fuser:1101
StarPress:991
Robot:1

Pour linstant, la socit Oozinoz doit continuer utiliser lancien style de classes
Iterator. Elle ne peut augmenter la version du code avant dtre absolument sre
que ses clients disposent des nouveaux compilateurs. Vous pouvez nanmoins
essayer aujourdhui les boucles for gnriques et tendues.

Itration avec scurit inter-threads


Les applications riches en fonctionnalits emploient souvent des threads pour raliser des tches sexcutant avec une apparence de simultanit. En particulier, il est
frquent dexcuter en arrire-plan les tches coteuses en temps pour ne pas ralentir la ractivit de la GUI. Lutilisation de threads est utile, mais elle comporte ses
risques. De nombreuses applications plantent en raison de tches sexcutant dans
des threads qui ne collaborent pas efficacement. Des mthodes parcourant une
collection peuvent par exemple tre en cause.
Les classes de collection dans java.util.Collections offrent une certaine
mesure de scurit de thread en fournissant une mthode synchronized(). En
essence, elle retourne une version de la collection sous-jacente, vitant ainsi que
deux threads la modifient en mme temps.
Une collection et son itrateur cooprent pour dtecter si une liste change durant
litration, cest--dire si la liste est synchronise. Pour observer ce comportement
en action, supposez que le singleton Factory dOozinoz puisse nous indiquer les
machines qui sont actives un certain moment et que nous souhaitions en afficher
la liste. Lexemple de code dans le package app.iterator.concurrent implmente cette liste dans la mthode upMachineNames().

pattern Livre Page 298 Vendredi, 9. octobre 2009 10:31 10

298

Partie V

Patterns dextension

Le programme suivant affiche une liste des machines qui sont actuellement actives,
mais simule la condition que de nouvelles machines puissent entrer en action alors
que le programme est en train dafficher la liste :
package app.iterator.concurrent;
import java.util.*;
public class ShowConcurrentIterator implements Runnable {
private List list;
protected static List upMachineNames() {
return new ArrayList(Arrays.asList(new String[] {
"Mixer1201", "ShellAssembler1301",
"StarPress1401", "UnloadBuffer1501" }));
}
public static void main(String[] args) {
new ShowConcurrentIterator().go();
}
protected void go() {
list = Collections.synchronizedList(
upMachineNames());
Iterator iter = list.iterator();
int i = 0;
while (iter.hasNext()) {
i++;
if (i == 2) { // simule lactivation dune machine
new Thread(this).start();
try { Thread.sleep(100); }
catch (InterruptedException ignored) {}
}
System.out.println(iter.next());
}
}
/**
** Insre un lment dans la liste, dans un thread distinct.
*/
public void run() {
list.add(0, "Fuser1101");
}
}

La mthode main() dans ce code construit une instance de la classe et appelle la


mthode go(). Cette mthode parcourt par itration la liste des machines actives, en
prenant soin den construire une version synchronise. Ce code simule la situation
o une nouvelle machine devient active alors que la mthode lit la liste. La mthode
run() modifie la liste, sexcutant dans un thread spar.

pattern Livre Page 299 Vendredi, 9. octobre 2009 10:31 10

Chapitre 28

ITERATOR

299

Le programme ShowConcurrentIterator affiche une ou deux machines puis plante :


Mixer1201
java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
at java.util.AbstractList$Itr.next(Unknown Source)
at
com.oozinoz.app.ShowConcurrent.ShowConcurrentIterator.go(ShowConcurrent
Iterator.java:49)
at
com.oozinoz.app.ShowConcurrent.ShowConcurrentIterator.main(ShowConcurre
ntIterator.java:29)
Exception in thread "main" .

Le programme plante car la liste et les objets itrateurs dtectent que la liste a
chang durant litration. Vous navez pas besoin de crer un nouveau thread pour
illustrer ce comportement. Vous pouvez crire un programme qui plante simplement en modifiant une collection partir dune boucle dnumration. Dans la
pratique, cest plus vraisemblablement par accident quune application multithread
modifie une liste pendant quun itrateur la parcourt.
Nous pouvons laborer une approche par thread scurise pour lire une liste. Toutefois, il est important de noter que le programme ShowConcurrentIterator plante
seulement parce quil utilise un itrateur. Lnumration par boucle for des
lments dune liste, mme synchronise, ne dclenchera pas lexception dmontre dans le programme prcdent, mais elle pourra nanmoins rencontrer des
problmes. Considrez cette version du programme :
package app.iterator.concurrent;
import java.util.*;
public class ShowConcurrentFor implements Runnable {
private List list;
protected static List upMachineNames() {
return new ArrayList(Arrays.asList(new String[] {
"Mixer1201", "ShellAssembler1301",
"StarPress1401", "UnloadBuffer1501" }));
}
public static void main(String[] args) {
new ShowConcurrentFor().go();
}
protected void go() {
System.out.println(
"Cette version permet lajout concurrent"
+ "de nouveaux lments :");

pattern Livre Page 300 Vendredi, 9. octobre 2009 10:31 10

300

Partie V

Patterns dextension

list = Collections.synchronizedList(
upMachineNames());
display();
}
private void display() {
for (int i = 0; i < list.size(); i++) {
if (i == 1) { // simule lactivation dune machine
new Thread(this).start();
try { Thread.sleep(100); }
catch (InterruptedException ignored) {}
}
System.out.println(list.get(i));
}
}
/**
** Insre un lment dans la liste, dans un thread distinct.
*/
public void run() {
list.add(0, "Fuser1101");
}
}

Lexcution de ce programme affiche :


Cette version permet lajout concurrent de nouveaux lments :
Mixer1201
Mixer1201
ShellAssembler1301
StarPress1401
UnloadBuffer1501

Exercice 28.1
Expliquez le rsultat en sortie du programme ShowConcurrentFor.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.


Nous avons examin deux versions de lexemple de programme : une qui plante et
une qui produit une sortie incorrecte. Aucun de ces rsultats ntant acceptable,
nous devons recourir une autre approche pour protger une liste pendant lnumration de ses lments.
Il y a deux approches courantes pour coder une itration sur une collection dans une
application multithread. Elles impliquent toutes deux lemploi dun objet appel

pattern Livre Page 301 Vendredi, 9. octobre 2009 10:31 10

Chapitre 28

ITERATOR

301

mutex, qui est partag par des threads rivalisant pour obtenir le contrle du verrou
sur lobjet. La premire possibilit de conception est de forcer tous les threads
obtenir le contrle du verrou du mutex avant de pouvoir accder la collection.
Voici un exemple de cette approche :
package app.iterator.concurrent;
import java.util.*;
public class ShowConcurrentMutex implements Runnable {
private List list;
protected static List upMachineNames() {
return new ArrayList(Arrays.asList(new String[] {
"Mixer1201", "ShellAssembler1301",
"StarPress1401", "UnloadBuffer1501" }));
}
public static void main(String[] args) {
new ShowConcurrentMutex().go();
}
protected void go() {
System.out.println(
"Cette version synchronise correctement :");
list = Collections.synchronizedList(upMachineNames());
synchronized (list) {
display();
}
}
private void display() {
for (int i = 0; i < list.size(); i++) {
if (i == 1) { // simule lactivation dune machine
new Thread(this).start();
try { Thread.sleep(100);
} catch (InterruptedException ignored) {}
}
System.out.println(list.get(i));
}
}
/**
** Insre un lment dans la liste, dans un thread distinct.
*/
public void run() {
synchronized (list) {
list.add(0, "Fuser1101");
}
}
}

pattern Livre Page 302 Vendredi, 9. octobre 2009 10:31 10

302

Partie V

Patterns dextension

Ce programme affiche la liste originale :


Cette version synchronise correctement :
Mixer1201
ShellAssembler1301
StarPress1401
UnloadBuffer1501

La sortie du programme montre la liste telle quelle existait avant linsertion dun
nouvel objet par la mthode run(). Le programme retourne un rsultat cohrent,
sans duplication, car la logique du programme requiert que la mthode run()
attende la fin de lnumration des lments dans la mthode display(). Bien que
les rsultats soient corrects, la conception peut se rvler impossible implmenter.
En effet, il est probable que vous nayez pas le luxe davoir des threads bloqus
pendant quun thread excute son itration.
La deuxime solution consiste cloner la collection dans une opration avec un
mutex puis de lire le contenu du clone. Lavantage du clonage dune liste avant de
la parcourir est la vitesse. Cest une opration souvent plus rapide que dattendre
quune mthode finisse son travail sur le contenu dune collection. Cette approche
peut toutefois donner lieu des problmes.
La mthode clone() pour ArrayList produit une copie "partielle", cest--dire
une nouvelle collection qui se rfre aux mmes objets que la collection originale.
Cette approche chouerait donc si dautres threads pouvaient modifier les objets
sous-jacents dune manire qui interfre avec votre mthode. Dans certains cas
toutefois, ce risque est faible. Par exemple, si vous souhaitez simplement afficher
une liste de noms de machines, il est peu probable que des noms changent au
moment o votre mthode lit le clone de la liste.
Pour rsumer, nous avons vu quatre approches diffrentes permettant dnumrer
par itration les lments dune liste dans un environnement multithread. Deux
dentre elles emploient la mthode synchronized() et sont fautives, pouvant soit
planter soit produire des rsultats incorrects. Les deux dernires que nous avons
tudies emploient le verrouillage et le clonage pour produire des rsultats corrects,
mais elles ont aussi leurs inconvnients.
Java et ses bibliothques fournissent un support substantiel pour litration dans un
environnement multithread, mais ce support ne vous affranchit pas des difficults
de la conception avec des processus concurrents. Les bibliothques Java offrent de

pattern Livre Page 303 Vendredi, 9. octobre 2009 10:31 10

Chapitre 28

ITERATOR

303

bonnes fonctionnalits pour grer la lecture des nombreuses collections quelles


proposent, mais si vous introduisez votre propre type de collection, il vous faudra
aussi introduire un itrateur qui lui soit associ.

Exercice 28.2
Donnez un argument contre lemploi de la mthode synchronized(), ou
justifiez le fait quune approche par verrouillage nest pas non plus toujours la
rponse.

Itration sur un objet composite


Il est gnralement facile de concevoir des algorithmes qui parcourent une structure
composite, visitant chaque nud et ralisant certaines tches. LExercice 5.3 du
Chapitre 5 vous avait demand de concevoir plusieurs algorithmes sexcutant de
manire rcursive pour parcourir une structure composite. La cration dun itrateur peut tre beaucoup plus complexe que la conception dun algorithme rcursif.
La difficult rside dans le transfert en retour du contrle une autre partie du
programme et la conservation dun genre de signet permettant litrateur de reprendre
l o il avait suspendu son travail.
Les composites fournissent un bon exemple ditrateur difficile dvelopper.
Vous pouvez penser que vous aurez besoin dune nouvelle classe ditrateur pour
chaque composite spcifique que vous crerez. Vous pouvez en fait concevoir un
itrateur composite relativement rutilisable, hors le fait quil vous faudra modifier
vos classes de composite pour retourner le bon type ditrateur.
La conception dun itrateur composite est aussi naturellement rcursive que les
composites le sont eux-mmes. Pour lire par itration un composite, il faut lire ses
enfants, bien que ce soit un peu plus complexe que cela puisse paratre de prime
abord. Nous avons le choix entre retourner un nud avant ou aprs ses descendants
(techniques appeles respectivement traverse pr-ordonne ou post-ordonne).
Si nous choisissons une traverse pr-ordonne, nous devons numrer les enfants
aprs avoir retourn la tte, en prenant soin de noter que chaque enfant peut luimme tre un composite. Une subtilit demande ici que nous grions deux itrateurs. Un itrateur, nomm 1 dans la Figure 28.1, garde trace de son enfant actuel.

pattern Livre Page 304 Vendredi, 9. octobre 2009 10:31 10

304

Partie V

Patterns dextension

Cest un simple itrateur de liste qui parcourt la liste denfants. Un second itrateur
(nomm 2) parcourt lenfant actuel en tant que composite. La Figure 28.1 illustre les
trois aspects de la dtermination du nud actuel dans une itration sur un composite.

ComponentIterator

interface
Iterator

head:Object
visited:Set

hasNext():boolean
next():Object

ComponentEnumerator(
head:Object,visited:Set)

remove()

remove()

CompositeIterator
peek:Object
children:Iterator
subIterator:
ComponentIterator

LeafIterator

LeafEnumerator(
head:Object,visited:Set)
hasNext():bool
next():Object

ComponentEnumerator(
head:Object,
components:List,
visited:Set)
hasNext():bool
next():Object

Figure 28.1
La lecture par itration dun composite ncessite : de signaler la tte(0) ; de parcourir
squentiellement les enfants (1), et de parcourir un enfant composite.

Pour notre travail de conception dun itrateur composite, nous pouvons supposer
quune itration sur un nud simple sera une chose banale alors quune itration sur
un nud composite sera plus difficile. En premire approximation, nous pouvons
imaginer une conception ditrateurs ressemblant lexemple illustr Figure 28.2.

pattern Livre Page 305 Vendredi, 9. octobre 2009 10:31 10

Chapitre 28

ITERATOR

ComponentIterator

305

interface
Iterator

head:Object
visited:Set

hasNext():boolean
next():Object

ComponentEnumerator(
head:Object,visited:Set)

remove()

remove()

CompositeIterator
peek:Object
children:Iterator
subIterator:
ComponentIterator

LeafIterator

LeafEnumerator(
head:Object,visited:Set)
hasNext():bool
next():Object

ComponentEnumerator(
head:Object,
components:List,
visited:Set)
hasNext():bool
next():Object

Figure 28.2
Une premire conception pour une famille dnumrateurs par itration.

Les classes emploient les noms de mthodes hasNext() et next() pour que la
classe ComponentIterator implmente linterface Iterator du package
java.util.
La conception montre que les constructeurs de classe dnumration acceptent un
objet parcourir par itration. Dans la pratique, cet objet sera un composite, de
machines ou de processus, par exemple. La conception utilise aussi une variable
visited pour garder trace des nuds dj numrs. Cela nous vite dentrer dans

pattern Livre Page 306 Vendredi, 9. octobre 2009 10:31 10

306

Partie V

Patterns dextension

une boucle infinie lorsquun composite a des cycles. Le code pour ComponentIterator au sommet de la hirarchie aura finalement lapparence suivante :
package com.oozinoz.iterator;
import java.util.*;
public abstract class ComponentIterator
implements Iterator {
protected Object head;
protected Set visited;
protected boolean returnInterior = true;
public ComponentIterator(Object head, Set visited) {
this.head = head;
this.visited = visited;
}
public void remove() {
throw new UnsupportedOperationException(
"ComponentIterator.Remove");
}
}

Cette classe laisse la plus grande part du travail difficile ses sous-classes.
Dans la sous-classe CompositeIterator, nous pouvons anticiper le besoin pour un
itrateur de liste dnumrer les enfants dun nud composite. Cest lnumration
note 1 dans la Figure 28.1, reprsente par la variable children dans la
Figure 28.2. Les composites ont galement besoin dun numrateur pour lnumration note 2 dans la figure. La variable subIterator dans la Figure 28.2 rpond
ce besoin. Le constructeur de la classe CompositeEnumerator peut initialiser
lnumrateur denfants de la manire suivante :
public CompositeIterator(
Object head, List components, Set visited) {
super(head, visited);
children = components.iterator();
}

Lorsque nous commenons la traverse dun composite, nous savons que le premier
nud retourner est le nud de tte (marqu H dans la Figure 28.1). Ainsi, le code
pour la mthode next() dune classe CompositeIterator pourrait tre comme
suit :
public Object next() {
if (peek != null) {
Object result = peek;

pattern Livre Page 307 Vendredi, 9. octobre 2009 10:31 10

Chapitre 28

ITERATOR

307

peek = null;
return result;
}
if (!visited.contains(head)) {
visited.add(head);
return head;
}
return nextDescendant();
}

La mthode next() utilise lensemble visit (Set visited) pour enregistrer si


lnumrateur a dj retourn le nud de tte. Sil a retourn la tte dun composite,
la mthode nextDescendant() doit trouver le nud suivant.
A tout moment, la variable subIterator peut se trouver en cours de processus
dnumration dun enfant qui est lui-mme un nud composite. Si cet numrateur est actif, la mthode next() de la classe CompositeIterator peut "dplacer"
le sous-itrateur. Si celui-ci ne peut se dplacer, le code doit mettre le prochain
lment dans la liste denfants, obtenir un nouveau sous-itrateur pour lui, et
dplacer cet numrateur. Le code de la mthode nextDescendant() montre cette
logique :
protected Object nextDescendant() {
while (true) {
if (subiterator != null) {
if (subiterator.hasNext())
return subiterator.next();
}
if (!children.hasNext()) return null;
ProcessComponent pc =
(ProcessComponent) children.next();
if (!visited.contains(pc)) {
subiterator = pc.iterator(visited);
}
}

Cette mthode introduit la premire contrainte que nous avons rencontre concernant le type dobjets que nous pouvons numrer : le code requiert que les enfants
dans un composite implmentent une mthode iterator(:Set). Considrez un
exemple dune structure composite, telle que la hirarchie ProcessComponent que
le Chapitre 5 a introduite. La Figure 28.3 illustre la hirarchie de composites de
processus quOozinoz utilise pour modliser le flux de processus de fabrication qui
produit les divers types dartifices.

pattern Livre Page 308 Vendredi, 9. octobre 2009 10:31 10

308

Partie V

Patterns dextension

Figure 28.3
ProcessComponent

Le flux des processus


de fabrication
dOozinoz est constitu de composites.

ProcessComposite

ProcessAlternation

ProcessStep

ProcessSequence

La mthode next() de la classe CompositeIterator doit numrer les nuds de


chaque enfant qui appartient un objet composite. Nous devons faire en sorte que la
classe de lenfant implmente une mthode iterator(:Set) que le code de
next() puisse utiliser. La Figure 28.2 illustre la relation qui unit les classes et les
interfaces.
Pour actualiser la hirarchie de ProcessComponent afin de pouvoir la parcourir,
nous devons prvoir une mthode iterator():
public ComponentIterator iterator() {
return iterator(new HashSet());
}
public abstract ComponentIterator iterator(Set visited);

La classe ProcessComponent est abstraite et la mthode iterator(:Set) doit tre


implmente par les sous-classes. Pour la classe ProcessComposite, le code se
prsenterait comme suit :
public ComponentIterator iterator(Set visited) {
return new CompositeIterator(this, subprocesses, visited);
}

Voici limplmentation de iterator() dans la classe ProcessStep:


public ComponentIterator iterator(Set visited) {
return new LeafIterator(this, visited);
}

Avec ces petits ajouts en place dans la hirarchie ProcessComponent, nous


pouvons maintenant crire du code pour numrer les nuds dun composite de
processus.

pattern Livre Page 309 Vendredi, 9. octobre 2009 10:31 10

Chapitre 28

ITERATOR

309

Exercice 28.3
Quel pattern appliquez-vous pour permettre aux classes dune hirarchie
ProcessComponent dimplmenter iterator() afin de crer des instances
dune classe ditrateur approprie ?

buildInnerShell

make:ProcessSequence

inspect

reworkOrFinish:Alternation

:ProcessSequence

disassemble

finish

Figure 28.4
Le flux de processus de fabrication de bombes ariennes est un composite cyclique.
Chaque nud feuille dans ce diagramme est une instance de ProcessStep. Les autres
nuds sont des instances de ProcessComposite.

La Figure 28.4 illustre le modle objet dun flux de processus typique chez Oozinoz.
Le court programme qui suit numre tous les nuds de ce modle :
package app.iterator.process;
import com.oozinoz.iterator.ComponentIterator;
import com.oozinoz.process.ProcessComponent;
import com.oozinoz.process.ShellProcess;
public class ShowProcessIteration {

pattern Livre Page 310 Vendredi, 9. octobre 2009 10:31 10

310

Partie V

Patterns dextension

public static void main(String[] args) {


ProcessComponent pc = ShellProcess.make();
ComponentIterator iter = pc.iterator();
while (iter.hasNext())
System.out.println(iter.next());
}
}

Lexcution de ce programme affiche les informations suivantes :


Fabriquer une bombe arienne
Crer la coque interne
Contrler
Retraiter la coque interne, ou finir la bombe
Retraiter
Dsassembler
Terminer: ajouter la charge de propulsion, insrer le dispositif
dallumage,
et emballer

Les tapes numres sont celles dfinies par la classe ShellProcess. Notez que,
dans le modle objet, ltape qui suit "Dsassembler" est "Fabriquer". Le rsultat en
sortie omet cela car lnumration voit quelle a dj numr cette tape dans la
premire ligne du rsultat.
Ajout dun niveau de profondeur un numrateur

La sortie de ce programme pourrait tre plus claire si nous prvoyions chaque tape
conformment sa profondeur dans le modle. Nous pouvons dfinir la profondeur
dun numrateur de feuille comme tant 0 et celle dun numrateur de composite
comme tant 1 plus la profondeur de son sous-itrateur. Nous pouvons dclarer
getDepth() abstraite dans la super-classe ComponentIterator de la manire suivante :
public abstract int getDepth();

Le code pour la mthode getDepth() dans la classe LeafIterator se prsente comme


suit :
public int getDepth() {
return 0;
}

Le code pour CompositeIterator.getDepth() est :


public int getDepth() {
if (subiterator != null)
return subiterator.getDepth() + 1;
return 0;
}

pattern Livre Page 311 Vendredi, 9. octobre 2009 10:31 10

Chapitre 28

ITERATOR

311

Le programme suivant produit une sortie plus lisible :


package app.iterator.process;
import com.oozinoz.iterator.ComponentIterator;
import com.oozinoz.process.ProcessComponent;
import com.oozinoz.process.ShellProcess;
public class ShowProcessIteration2 {
public static void main(String[] args) {
ProcessComponent pc = ShellProcess.make();
ComponentIterator iter = pc.iterator();
while (iter.hasNext()) {
for (int i = 0; i < 4 * iter.getDepth(); i++)
System.out.print( );
System.out.println(iter.next());
}
}
}

La sortie du programme est :


Fabriquer une bombe arienne
Crer la coque interne
Contrler
Retraiter la coque interne, ou finir la bombe
Retraiter
Dsassembler
Terminer: ajouter la charge de propulsion, insrer le dispositif
dallumage, et emballer

Une autre amlioration que nous pouvons apporter la hirarchie ComponentIterator est de ne permettre que lnumration des feuilles dun composite.
Enumration des feuilles

Supposez que nous voulions permettre une numration de ne retourner que les
feuilles. Cela pourrait tre utile si nous tions intresss par des attributs ne sappliquant qu cette catgorie de nuds, tel le temps requis par une tape procdurale.
Nous pourrions ajouter un champ returnInterior la classe ComponentIterator
afin denregistrer si les nuds intrieurs (non feuilles) devraient ou non tre retourns
par lnumration :
protected boolean returnInterior = true;
public boolean shouldShowInterior() {
return returnInterior;
}
public void setShowInterior(boolean value) {
returnInterior = value;
}

pattern Livre Page 312 Vendredi, 9. octobre 2009 10:31 10

312

Partie V

Patterns dextension

Dans la mthode nextDescendant() de la classe CompositeIterator, nous


aurons besoin de retransmettre cet attribut lorsque nous crerons une nouvelle
numration pour un enfant de nud composite :
protected Object nextDescendant() {
while (true) {
if (subiterator != null) {
if (subiterator.hasNext())
return subiterator.next();
}
if (!children.hasNext()) return null;
ProcessComponent pc =
(ProcessComponent) children.next();
if (!visited.contains(pc)) {
subiterator = pc.iterator(visited);
subiterator.setShowInterior(
shouldShowInterior());
}
}
}

Il nous faudra aussi modifier la mthode next() de la classe CompositeEnumerator.


Le code actuel est le suivant :
public Object next() {
if (peek != null) {
Object result = peek;
peek = null;
return result;
}
if (!visited.contains(head)) {
visited.add(head);
}
return nextDescendant();
}

Exercice 28.4
Modifiez la mthode next() de la classe CompositeIterator pour respecter la
valeur du champ returnInterior.
La cration dun itrateur, ou dun numrateur, pour un nouveau type de collection
peut reprsenter une certaine somme de travail. Lavantage qui en rsultera sera
quil sera aussi simple de travailler avec votre collection quavec les bibliothques
de classes Java.

pattern Livre Page 313 Vendredi, 9. octobre 2009 10:31 10

Chapitre 28

ITERATOR

313

Rsum
Lobjectif du pattern ITERATOR est de permettre un client daccder en squence
aux lments dune collection. Les classes de collection dans les bibliothques Java
offrent un support avanc pour travailler avec des collections, dont la gestion
ditrations, ou lnumration. Lorsque lon cre un nouveau type de collection, on
lui associe souvent un itrateur. Un composite spcifique un domaine est un exemple courant de nouveau type de collection. Le support de la boucle for, gnrique
comme tendu, amliorera la visibilit de votre code. Vous pouvez concevoir un
itrateur relativement gnrique que vous pourrez ensuite appliquer une varit de
hirarchies de composites.
Lorsque vous instanciez un itrateur, vous devez vous demander si la collection peut
changer pendant que vous en numrez les lments. Dans une application monothread, il y a peu de chances que cela se produise, mais dans une application
multithread, vous devrez vous assurer que laccs une collection est bien synchronis. Pour numrer des lments en toute scurit dans un tel environnement,
vous pouvez synchroniser laccs aux collections en verrouillant un objet mutex. Vous
pouvez bloquer tous les accs au cours de lnumration, ou brivement pendant le
clonage dune collection. Avec une conception correcte, vous pouvez assurer une
scurit inter-threads aux clients de votre code ditrateur.

pattern Livre Page 314 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 315 Vendredi, 9. octobre 2009 10:31 10

29
VISITOR
Pour tendre une hirarchie de classes, normalement vous ajoutez simplement des
mthodes qui fournissent le comportement souhait. Il peut arriver nanmoins que
ce comportement ne soit pas cohrent avec la logique du modle objet existant. Il se
peut aussi que vous nayez pas accs au code existant. Dans de telles situations, il
peut tre impossible dtendre le comportement de la hirarchie sans modifier ses
classes. Le pattern VISITOR permet justement au dveloppeur dune hirarchie
dintgrer un support pour les cas o dautres dveloppeurs voudraient tendre son
comportement.
A linstar de INTERPRETER, VISITOR est le plus souvent plac au-dessus de COMPOSITE. Vous pourriez donc vouloir rviser COMPOSITE car nous nous y rfrerons
dans ce chapitre.
Lobjectif du pattern VISITOR est de vous permettre de dfinir une nouvelle
opration pour une hirarchie sans changer ses classes.

Application de VISITOR
Le pattern VISITOR permet, avec un peu de prvoyance lors du dveloppement
dune hirarchie de classes, douvrir la voie une varit illimite dextensions
pouvant tre apportes par un dveloppeur nayant pas accs au code source. Voici
comment lappliquer :
m

Ajoutez une opration accept() certaines ou toutes les classes dune hirarchie.
Chaque implmentation de cette mthode acceptera un argument dont le type
sera une interface que vous crerez.

pattern Livre Page 316 Vendredi, 9. octobre 2009 10:31 10

316

Partie V

Patterns dextension

Crez une interface avec un ensemble doprations partageant un nom commun,


habituellement visit, mais possdant des types darguments diffrents. Dclarez une de ces oprations pour chaque classe de la hirarchie dont vous autorisez
lextension.

La Figure 29.1 illustre le diagramme de classes de la hirarchie MachineComponent


modifie pour supporter VISITOR.

interface
MachineVisitor
MachineComponent
visit(:Machine)
accept(:MachineVisitor)

visit(:MachineComposite)

MachineComposite

accept(:MachineVisitor)

Fuser

Machine

accept(:MachineVisitor)

Mixer

...

Figure 29.1
Pour intgrer le support de VISITOR la hirarchie MachineComponent, ajoutez les mthodes
accept() et linterface MachineVisitor prsentes dans ce diagramme.

Ce diagramme nexplique pas comment VISITOR fonctionne, ce qui est lobjet de la


prochaine section. Il montre simplement certains des principes vous permettant
dappliquer ce pattern.

pattern Livre Page 317 Vendredi, 9. octobre 2009 10:31 10

Chapitre 29

VISITOR

317

Notez que toutes les classes du diagramme de MachineComponent implmentent


une mthode accept(). VISITOR nimpose pas toutes les classes de la hirarchie de possder leur propre implmentation de cette mthode. Nanmoins,
comme nous le verrons, toutes celles qui limplmentent doivent apparatre sous la
forme dun argument dans une mthode visit() dclare dans linterface MachineVisitor.
La mthode accept() de la classe MachineComponent est abstraite. Les deux sousclasses implmentent cette mthode en utilisant exactement le mme code :
public void accept(MachineVisitor v) {
v.visit(this);
}

Cette mthode tant identique dans les classes Machine et MachineComposite,


vous pourriez vouloir la remonter dans la classe abstraite MachineComponent.
Sachez toutefois que le compilateur voit une diffrence.
Exercice 29.1
Quelle diffrence un compilateur Java discerne-t-il entre les mthodes accept()
des classes Machine et MachineComposite? Ne regardez pas la solution avant
davoir bien rflchi car cette diffrence est essentielle pour comprendre
VISITOR.

b Les solutions des exercices de ce chapitre sont donnes dans lAnnexe B.

Linterface MachineVisitor impose aux implmenteurs de dfinir des mthodes


pour "visiter" les machines et les composites de machines :
package com.oozinoz.machine;
public interface MachineVisitor {
void visit(Machine m);
void visit(MachineComposite mc);
}

Les mthodes accept() de MachineComponent combines linterface MachineVisitor invitent les dveloppeurs ajouter de nouvelles oprations la hirarchie.

pattern Livre Page 318 Vendredi, 9. octobre 2009 10:31 10

318

Partie V

Patterns dextension

Un VISITOR ordinaire
Imaginez que vous participiez au dveloppement de la toute dernire unit de
production dOozinoz Dublin en Irlande. Les dveloppeurs qui sont sur place ont
cr un modle objet pour la composition des machines et lont rendu accessible
sous la forme de la mthode dublin() statique de la classe OozinozFactory. Pour
afficher ce composite, ils ont cr une classe MachineTreeModel afin dadapter les
informations du modle aux exigences dun objet JTree (le code de MachineTreeModel se trouve dans le package com.oozinoz.dublin).
Laffichage des machines de lunit de production demande de crer une instance
de MachineTreeModel partir du composite de lunit et denvelopper ce modle
dans des composants Swing :
package app.visitor;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import com.oozinoz.machine.OozinozFactory;
import com.oozinoz.ui.SwingFacade;
public class ShowMachineTreeModel {
public ShowMachineTreeModel() {
MachineTreeModel model = new MachineTreeModel(
OozinozFactory.dublin());
JTree tree = new JTree(model);
tree.setFont(SwingFacade.getStandardFont());
SwingFacade.launch(
new JScrollPane(tree),
" Une nouvelle unit de production Oozinoz");
}
public static void main(String[] args) {
new ShowMachineTreeModel();
}
}

Ce programme affiche le rsultat illustr Figure 29.2. Nombre de comportements


utiles sont possibles pour un composite de machines. Par exemple, supposez que
vous ayez besoin de trouver une certaine machine dans le modle. Pour ajouter cette
possibilit sans modifier la hirarchie MachineComponent, vous pouvez crer une
classe FindVisitor, comme le montre la Figure 29.3.

pattern Livre Page 319 Vendredi, 9. octobre 2009 10:31 10

Chapitre 29

VISITOR

Figure 29.2
Lapplication GUI prsente
la composition des machines
de la nouvelle unit de
production irlandaise.

Figure 29.3
La classe FindVisitor
ajoute une opration find()
la hirarchie MachineComponent.

interface
MachineVisitor

visit(:Machine)
visit(:MachineComposite)

FindVisitor
soughtId:int
found:MachineComponent
find(
mc:MachineComponent,
id:int):MachineComponent
visit(:Machine)
visit(:MachineComposite)

319

pattern Livre Page 320 Vendredi, 9. octobre 2009 10:31 10

320

Partie V

Patterns dextension

Les mthodes visit() ne retournent pas dobjet, aussi la classe FindVisitor


enregistre-t-elle ltat dune recherche dans sa variable dinstance found:
package app.visitor;
import com.oozinoz.machine.*;
import java.util.*;
public class FindVisitor implements MachineVisitor {
private int soughtId;
private MachineComponent found;
public MachineComponent find(
MachineComponent mc, int id) {
found = null;
soughtId = id;
mc.accept(this);
return found;
}
public void visit(Machine m) {
if (found == null && m.getId() == soughtId)
found = m;
}
public void visit(MachineComposite mc) {
if (found == null && mc.getId() == soughtId) {
found = mc;
return;
}
Iterator iter = mc.getComponents().iterator();
while (found == null && iter.hasNext())
((MachineComponent) iter.next()).accept(this);
}
}

Les mthodes visit() examinent la variable found pour que la traverse de larbre
se termine aussitt que le composant recherch a t trouv.
Exercice 29.2
Ecrivez un programme qui trouve et affiche lobjet StarPress:3404 dans
linstance de MachineComponent retourne par OozinozFactory.dublin().

pattern Livre Page 321 Vendredi, 9. octobre 2009 10:31 10

Chapitre 29

VISITOR

321

La mthode find() ne se proccupe pas de savoir si lobjet MachineComponent


quelle reoit est une instance de Machine ou de MachineComposite. Elle invoque
simplement accept() qui invoque son tour visit().
Notez que la boucle dans la mthode visit(:MachineComposite) ne se soucie
pas non plus de savoir si un composant enfant est une instance de Machine ou de
MachineComposite. La mthode visit() invoque simplement lopration
accept() de chaque composant. La mthode qui sexcute comme rsultat de cette
invocation dpend du type de lenfant. La Figure 29.4 prsente une squence typique
dappels de mthodes.

:FindVisitor

:MachineComposite

:Machine

visit(dublin)

accept(this)

visit(this)

accept(this)

visit(this)

Figure 29.4
Un objet FindVisitor invoque une opration accept() pour dterminer quelle mthode
visit() excuter.

Lorsque la mthode visit(:MachineComposite) sexcute, elle invoque donc


lopration accept() de chaque enfant du composite. Un enfant rpond en invoquant lopration visit() de lobjet Visitor. Comme le montre la Figure 29.4, cet
aller-retour entre lobjet Visitor et lobjet qui reoit linvocation de accept()
permet de rcuprer le type de ce dernier. Cette technique, qualifie de double

pattern Livre Page 322 Vendredi, 9. octobre 2009 10:31 10

322

Partie V

Patterns dextension

dispatching, garantit que la mthode visit() approprie de la classe Visitor est


excute.
Le double dispatching dans VISITOR permet de crer des classes visiteur avec des
mthodes qui sont spcifiques aux divers types de la hirarchie visite. Vous
pouvez ajouter pratiquement nimporte quel comportement au moyen de ce
pattern, comme si vous contrliez le code source. Comme autre exemple, considrez un visiteur qui trouve toutes les machines les nuds feuille dun
composant-machine :
package app.visitor;
import com.oozinoz.machine.*;
import java.util.*;
public class RakeVisitor implements MachineVisitor {
private Set leaves;
public Set getLeaves(MachineComponent mc) {
leaves = new HashSet();
mc.accept(this);
return leaves;
}
public void visit(Machine m) {
// Exercice !
}
public void visit(MachineComposite mc) {
// Exercice !
}
}

Exercice 29.3
Compltez le code de la classe RakeVisitor pour collecter les feuilles (leave)
dun composant-machine.

Un court programme peut trouver les feuilles dun composant-machine et les


afficher :
package app.visitor;

pattern Livre Page 323 Vendredi, 9. octobre 2009 10:31 10

Chapitre 29

VISITOR

323

import com.oozinoz.machine.*;
import java.io.*;
import com.oozinoz.filter.WrapFilter;
public class ShowRakeVisitor {
public static void main(String[] args)
throws IOException {
MachineComponent f = OozinozFactory.dublin();
Writer out = new PrintWriter(System.out);
out = new WrapFilter(new BufferedWriter(out), 60);
out.write(
new RakeVisitor().getLeaves(f).toString());
out.close();
}
}

Ce programme utilise un filtre de passage la ligne pour produire son rsultat :


[StarPress:3401, Fuser:3102, StarPress:3402, Mixer:3202,
Fuser:3101, StarPress:3403, ShellAssembler:1301,
ShellAssembler:2301, Mixer:1201, StarPress:2401, Mixer:3204,
Mixer:3201, Fuser:1101, Fuser:2101, ShellAssembler:3301,
ShellAssembler:3302, StarPress:1401, Mixer:3203, Mixer:2202,
StarPress:3404, Mixer:2201, StarPress:2402]

Les classes FindVisitor et RakeVisitor ajoutent toutes deux un nouveau


comportement la hirarchie MachineComponent et semblent fonctionner correctement. Cependant, le risque dutiliser des visiteurs est quils demandent de
comprendre la hirarchie qui est tendue. Un changement dans cette hirarchie
pourrait rendre votre visiteur inoprant, ou vous pourriez ne pas saisir correctement
le fonctionnement de la hirarchie. En particulier, vous pourriez avoir grer des
cycles si le composite que vous visitez ne les empche pas.

Cycles et VISITOR
La hirarchie ProcessComponent quutilise Oozinoz pour modliser ses flux de
processus est une autre structure composite laquelle il peut tre utile dintgrer un
support de VISITOR. Contrairement aux composites de machines, il est naturel pour
les flux de processus de contenir des cycles, et les objets visiteurs doivent viter de
gnrer des boucles infinies lorsquils parcourent des composites de processus.
La Figure 29.5 prsente la hirarchie ProcessComponent.

pattern Livre Page 324 Vendredi, 9. octobre 2009 10:31 10

324

Partie V

Patterns dextension

ProcessComponent

interface
ProcessVisitor

ProcessComponent(name:String)
accept(:ProcessVisitor)
getName():String

visit(:ProcessAlternation)
visit(:ProcessSequence)
visit(:ProcessStep)

ProcessComposite
getChildren():List

ProcessAlternation

accept(:ProcessVisitor)

ProcessStep
accept(:ProcessVisitor)

ProcessSequence

accept(:ProcessVisitor)

Figure 29.5
A linstar de la hirarchie MachineComponent, la hirarchie ProcessComponent peut intgrer
un support de VISITOR.

Supposez que vous vouliez afficher un composant-processus dans un format


indent. Dans le Chapitre 28, consacr au pattern ITERATOR, nous avons utilis un
itrateur pour afficher les tapes dun flux de processus, que voici :
Fabriquer une bombe arienne
Crer une coque interne
Contrler
Retraiter la coque interne, ou finir la bombe
Retraiter
Dsassembler
Terminer : ajouter la charge de propulsion, insrer le dispositif
dallumage, et emballer

Retravailler une bombe implique de la dsassembler et de la rassembler. Ltape


qui suit Dsassembler est Fabriquer une bombe arienne. Laffichage prcdent ne
rpte pas cette tape car litrateur voit quelle est dj apparue une fois. Il serait
nanmoins plus informatif de la faire apparatre de nouveau en indiquant que le

pattern Livre Page 325 Vendredi, 9. octobre 2009 10:31 10

Chapitre 29

VISITOR

325

processus entre ici dans un cycle. Il serait galement utile dindiquer quels composites constituent des alternances par opposition des squences.
Pour crer un programme daffichage des processus, vous pourriez crer une classe
visiteur qui initialise un objet StringBuilder et ajoute ce tampon les nuds
dun composant-processus mesure quils sont visits. Pour indiquer quune tape dun
composite est une alternance, le visiteur pourrait faire prcder son nom dun point
dinterrogation (?). Pour indiquer quune tape est dj apparue, il pourrait la faire
suivre de trois points de suspension (). Avec ces changements, le processus de
fabrication dune bombe arienne ressemblerait ce qui suit :
Fabriquer une bombe arienne
Crer une coque interne
Contrler
?Retraiter la coque interne, ou finir la bombe
Retraiter
Dsassembler
Fabriquer une bombe arienne...
Terminer : ajouter la charge de propulsion, insrer le dispositif
dallumage, et emballer

Un visiteur de composant-processus doit tenir compte des cycles, mais cela est
facile mettre en uvre en utilisant un objet Set qui garde trace des nuds visits.
Le code de cette classe dbuterait ainsi :
package app.visitor;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
import com.oozinoz.process.*;
public class PrettyVisitor implements ProcessVisitor {
public static final String INDENT_STRING = " ";
private StringBuffer buf;
private int depth;
private Set visited;
public StringBuffer getPretty(ProcessComponent pc) {
buf = new StringBuffer();
visited = new HashSet();
depth = 0;
pc.accept(this);
return buf;
}
protected void printIndentedString(String s) {
for (int i = 0; i < depth; i++)
buf.append(INDENT_STRING);

pattern Livre Page 326 Vendredi, 9. octobre 2009 10:31 10

326

Partie V

Patterns dextension

buf.append(s);
buf.append("\n");
}
// ... mthodes visit()...
}

Cette classe utilise une mthode getPretty() pour initialiser les variables dune
instance et lancer lalgorithme visiteur. La mthode printIndentedString() gre
lindentation des tapes mesure que lalgorithme explore plus avant un composite.
Lorsquil visite un objet ProcessStep, le code affiche simplement le nom de
ltape :
public void visit(ProcessStep s) {
printIndentedString(s.getName());
}

Vous avez peut-tre remarqu dans la Figure 29.5 que la classe ProcessComposite
nimplmente pas de mthode accept() mais que ses sous-classes le font. Visiter
une alternance ou une squence de processus requiert une logique identique, que
voici :
public void visit(ProcessAlternation a) {
visitComposite("?", a);
}
public void visit(ProcessSequence s) {
visitComposite("", s);
}
protected void visitComposite(
String prefix, ProcessComposite c) {
if (visited.contains(c)) {
printIndentedString(prefix + c.getName() + "");
} else {
visited.add(c);
printIndentedString(prefix + c.getName());
depth++;
List children = c.getChildren();
for (int i = 0; i < children.size(); i++) {
ProcessComponent child =
(ProcessComponent) children.get(i);
child.accept(this);
}
depth--;
}
}

pattern Livre Page 327 Vendredi, 9. octobre 2009 10:31 10

Chapitre 29

VISITOR

327

La diffrence entre visiter une alternance et visiter une squence est que la premire
utilise un point dinterrogation comme prfixe. Dans les deux cas, si lalgorithme a
dj visit le nud, nous affichons son nom suivi de trois points de suspension.
Sinon, nous lajoutons une collection de nuds visits, affichons son prfixe
un point dinterrogation ou rien et "acceptons" ses enfants. Chose courante avec
le pattern VISITOR, le code recourt au polymorphisme pour dterminer si les nuds
enfant sont des instances des classes ProcessStep, ProcessAlternation ou
ProcessSequence.
Un court programme peut maintenant afficher le flux de processus :
package app.visitor;
import com.oozinoz.process.ProcessComponent;
import com.oozinoz.process.ShellProcess;
public class ShowPrettyVisitor {
public static void main(String[] args) {
ProcessComponent p = ShellProcess.make();
PrettyVisitor v = new PrettyVisitor();
System.out.println(v.getPretty(p));
}
}

Ce programme affiche le rsultat suivant :


Fabriquer une bombe arienne
Crer la coque interne
Contrler
?Retraiter la coque interne, ou finir la bombe
Retraiter
Dsassembler
Fabriquer une bombe arienne
Terminer : ajouter la charge de propulsion, insrer le dispositif
dallumage, et emballer

Ce rsultat est plus informatif que celui obtenu en parcourant simplement le modle
de processus. Le point dinterrogation qui apparat signale que les tapes de ce
composite sont des alternances. De plus, le fait dafficher ltape Fabriquer une
bombe arienne une seconde fois, suivie de points de suspension, est plus clair que
de simplement omettre une tape qui se rpte.
Les dveloppeurs de la hirarchie ProcessComponent intgrent le support de
VISITOR en y incluant des mthodes accept() et en dfinissant linterface
ProcessVisitor. Ils ont tous conscience de la ncessit dviter les boucles infinies lors de litration sur des flux de processus. Comme le montre la classe

pattern Livre Page 328 Vendredi, 9. octobre 2009 10:31 10

328

Partie V

Patterns dextension

PrettyVisitor, ils doivent aussi avoir conscience de lventualit de cycles dans


les composants-processus. Ils pourraient rduire le risque derreur en prvoyant un
support des cycles dans le cadre de leur support de VISITOR.
Exercice 29.4
Comment les dveloppeurs de ProcessComponent peuvent-ils intgrer la
hirarchie la fois un support des cycles et un support de VISITOR?

Risques de VISITOR
VISITOR est un pattern sujet controverse. Certains dveloppeurs vitent systmatiquement de lappliquer, tandis que dautres dfendent son utilisation et suggrent
des moyens de le renforcer, bien que ces suggestions augmentent gnralement la
complexit du code. Le fait est que ce pattern peut donner lieu de nombreux
problmes de conception.
La fragilit de VISITOR est perceptible dans les exemples de ce chapitre. Par exemple, les dveloppeurs de la hirarchie MachineComponent ont choisi de faire une
distinction entre les nuds Machine et les nuds MachineComposite, mais ils ne
diffrencient pas les sous-classes de Machine. Si vous aviez besoin de distinguer
diffrents types de machines dans votre visiteur, il vous faudrait procder une
vrification du type ou employer dautres techniques pour dterminer quel type de
machine une mthode visit() a reue. Vous pensez peut-tre que les dveloppeurs
auraient d inclure tous les types de machines ainsi quune mthode
visit(:Machine) gnrique dans linterface visiteur. Mais de nouveaux types de
machines apparaissent rgulirement et cette solution ne serait donc pas plus
robuste.
VISITOR ne reprsente pas toujours une bonne option selon les changements
susceptibles dintervenir. Si la hirarchie est stable et que les comportements associs changent, ce choix peut convenir. Mais si les comportements sont stables et que
la hirarchie change, cela vous obligera actualiser les visiteurs existants pour
quils puissent supporter les nouveaux types de nuds.
Un autre exemple de faiblesse apparat dans la hirarchie ProcessComponent. Les
dveloppeurs savent que les cycles reprsentent un danger li aux modles de flux

pattern Livre Page 329 Vendredi, 9. octobre 2009 10:31 10

Chapitre 29

VISITOR

329

de processus. Comment peuvent-ils communiquer ce risque aux dveloppeurs de


visiteurs ?
Ces difficults sont rvlatrices du problme fondamental de ce pattern, savoir
que lextension des comportements dune hirarchie demande normalement une
connaissance experte de sa conception. Si vous ne possdez pas cette expertise,
vous risquez de tomber dans un pige, comme celui de ne pas viter les cycles dun
flux de processus. Vous risquez de crer des dpendances dangereuses qui ne rsisteront pas aux changements de la hirarchie. La distribution de lexpertise et du
contrle du code source peut rendre VISITOR dangereux appliquer.
Un cas classique o ce pattern semble bien fonctionner sans causer de problmes
subsquents est celui des analyseurs syntaxiques des langages informatiques. Leurs
dveloppeurs sarrangent souvent pour que lanalyseur (parser) cre un arbre
syntaxique abstrait, cest--dire une structure qui organise le texte en entre en
fonction de la grammaire du langage. Vous pourriez dvelopper une varit de
comportements pour accompagner ces arbres, et le pattern VISITOR reprsente une
approche efficace permettant cela. Dans ce cas classique, il ny a quasiment pas de
comportements, voire aucun, dans la hirarchie visite. La responsabilit de concevoir
les comportements revient aux visiteurs, vitant ainsi la distribution de responsabilit observe dans les exemples de ce chapitre.
Comme nimporte quel autre pattern, VISITOR nest jamais ncessaire, sinon il
apparatrait partout o il le serait. En outre, il existe souvent des alternatives offrant
des conceptions plus robustes.
Sachez quil est le plus performant dans les conditions suivantes :
m
m

Lensemble des types de nuds est stable.


Un changement courant est lajout de nouvelles fonctions qui sappliquent aux
diffrents nuds.

A noter que les nouvelles fonctions devraient toucher tous les types de nuds.
Exercice 29.5
Enumrez deux alternatives lemploi de VISITOR dans les hirarchies de
machines et de processus dOozinoz.

pattern Livre Page 330 Vendredi, 9. octobre 2009 10:31 10

330

Partie V

Patterns dextension

Rsum
Le pattern VISITOR permet de dfinir une nouvelle opration pour une hirarchie
sans avoir modifier ses classes. Son application implique de dfinir une interface
pour les visiteurs et dajouter des mthodes accept() la hirarchie qui sera appele par un visiteur. Ces mthodes renvoient leur appel au visiteur en utilisant une
technique de double dispatching, laquelle fait quune mthode visit() sappliquera au type dobjet correspondant dans la hirarchie.
Le dveloppeur dun visiteur doit connatre certaines, voire toutes les subtilits de
conception de la hirarchie visite. En particulier, les visiteurs doivent avoir
connaissance des cycles susceptibles de survenir dans le modle objet visit. Ce
genre de difficults pousse certains dveloppeurs renoncer utiliser VISITOR, et
lui prfrer dautres alternatives. La dcision demployer ce pattern devrait dpendre de votre philosophie de conception, de votre quipe et des spcificit de votre
application.

pattern Livre Page 331 Vendredi, 9. octobre 2009 10:31 10

VI
Annexes

pattern Livre Page 332 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 333 Vendredi, 9. octobre 2009 10:31 10

A
Recommandations
Si vous avez lu ce livre jusquici, flicitations ! Si vous avez effectu tous les exercices, bravo ! Vous avez dvelopp une bonne connaissance pratique des patterns de
conception. Cette annexe vous donne des recommandations pour pousser plus loin
votre apprentissage.

Tirer le meilleur parti du livre


Si vous navez pas accompli les exercices du livre, vous ntes pas le seul ! Nous
sommes tous trs occups, et il est tentant de simplement rflchir au problme puis
de consulter la solution. Cette dmarche est assez commune, mais ce qui est
dommage, cest que vous avez la possibilit de devenir un dveloppeur hors norme.
Refaites les exercices un un et srieusement, en vous reportant la solution
uniquement lorsque vous pensez avoir trouv une bonne rponse ou si vous tes
bloqu. Faites-le maintenant. Ne vous dites pas que vous aurez davantage de temps
plus tard. En exerant vos connaissances frachement acquises sur les patterns, vous
acquerrez lassurance dont vous avez besoin pour commencer les appliquer dans
votre travail.
Nous vous suggrons galement de tlcharger le code disponible ladresse
www.oozinoz.com et de reproduire les exemples du livre sur votre systme. Parvenir excuter ce code vous apportera plus de confiance que si vous vous contentez
de travailler sur papier. Vous pouvez aussi laborer de nouveaux exercices. Vous
voudrez peut-tre trouver de nouvelles faons de combiner des filtres DECORATOR,
ou implmenter un ADAPTER de donnes qui affiche des informations dun domaine
familier.

pattern Livre Page 334 Vendredi, 9. octobre 2009 10:31 10

334

Partie VI

Annexes

A mesure que vous vous familiarisez avec les patterns, vous devriez commencer
comprendre les exemples classiques de leur utilisation. Vous commencerez galement identifier les endroits o il peut tre appropri dappliquer des patterns dans
votre code.

Connatre ses classiques


Les patterns rendent souvent une conception plus robuste. Cette ide nest pas
nouvelle, aussi nest-il pas surprenant que de nombreux patterns soient intgrs aux
bibliothques de classes Java. Si vous pouvez reprer un pattern dans le corps dun
programme, vous pouvez saisir la conception et la communiquer dautres dveloppeurs qui comprennent galement les patterns. Par exemple, si un dveloppeur
comprend comment fonctionne DECORATOR, cela a du sens dexpliquer que les flux
Java sont des dcorateurs.
Voici un test pour contrler votre comprhension des exemples classiques dutilisation
des patterns, qui apparaissent dans Java et ses bibliothques .
m

Comment les GUI contrlent-elles lemploi du pattern OBSERVER?

Pourquoi les menus emploient-ils souvent le pattern COMMAND?

En quoi les drivers constituent-ils un bon exemple du pattern BRIDGE? Chaque


driver est-il une instance du pattern ADAPTER?

Que signifie le fait de dire que les flux Java utilisent le pattern DECORATOR?

Pourquoi le pattern PROXY est-il essentiel pour la conception de RMI ?

Si le tri est un bon exemple du pattern TEMPLATE METHOD, quelle tape de lalgorithme nest pas spcifie initialement ?

Lidal est de rpondre ces questions sans vous aider du livre. Un bon exercice est
de mettre vos rponses par crit et de les partager avec des collgues.

Appliquer les patterns


Devenir un meilleur dveloppeur est ce qui incite le plus souvent apprendre les
patterns de conception. Leur application peut se faire de deux manires : lorsque
vous ajoutez du code ou via une refactorisation. Si une partie de votre code est
complexe et difficile grer, vous pourriez lamliorer en le refactorisant et en

pattern Livre Page 335 Vendredi, 9. octobre 2009 10:31 10

Annexe A

Recommandations

335

utilisant un pattern. Avant de vous lancer dans un tel projet, assurez-vous que cela
en vaille la peine. Veillez aussi crer une suite de tests automatiss pour le code
que vous rorganisez.
Supposez maintenant que vous compreniez bien les patterns tudis et soyez dtermin les utiliser prudemment et de manire approprie. Comment trouvez-vous
des opportunits ? En fait, des occasions se prsentent assez frquemment. Pour les
identifier, considrez les points suivants .
m

Votre base de code comporte-t-elle des parties complexes lies ltat dun
systme ou de lutilisateur de lapplication ? Si oui, vous pourriez lamliorer en
appliquant le pattern STATE.
Votre code combine-t-il la slection dune stratgie et lexcution de cette stratgie ? Si oui, vous pourriez lamliorer en appliquant le pattern STRATEGY.
Votre client ou analyste vous remet-il des organigrammes qui donnent lieu du
code difficile comprendre ? Si oui, vous pouvez appliquer le pattern INTERPRETER, en faisant de chaque nud de lorganigramme une instance dune
classe de la hirarchie interprteur. Vous obtiendrez ainsi une traduction directe
de lorganigramme en code.
Votre code comporte-t-il un composite faible qui nautorise pas ses enfants
tre eux-mmes des composites ? Vous pourriez renforcer ce code laide du
pattern COMPOSITE.
Avez-vous rencontr des erreurs dintgrit relationnelle dans votre modle
objet ? Vous pourriez les viter en appliquant le pattern MEDIATOR pour centraliser
la modlisation des relations entre les objets.
Votre code comporte-t-il des endroits o des clients utilisent les informations
dun service pour dcider quelle classe instancier ? Vous pourriez amliorer et
simplifier ce code en appliquant le pattern FACTORY METHOD.

Connatre les patterns permet de dvelopper un vocabulaire riche dides de


conception. Si vous recherchez des opportunits, vous naurez probablement pas
attendre longtemps avant de trouver une conception qui peut tre amliore par
lapplication dun pattern. Mais ne forcez pas les choses.

pattern Livre Page 336 Vendredi, 9. octobre 2009 10:31 10

336

Partie VI

Annexes

Continuer dapprendre
Cest dj une bonne chose que vous ayez eu lopportunit, la volont et lambition
dacqurir et de lire ce livre. Continuez ainsi ! Dterminez combien dheures par
semaine vous voulez consacrer votre carrire. Si vous arrivez vous en mnager
cinq, cest trs bien. Passez ce temps en dehors de votre bureau, lire des livres et
des magazines ou crire des programmes en rapport avec un sujet qui vous intresse. Que cela devienne une habitude aussi rgulire que daller au bureau. Traitez
srieusement cet aspect de votre carrire et vous deviendrez un bien meilleur dveloppeur. Vous nen apprcierez ensuite que mieux votre travail.
Vous pouvez prendre de nombreuses directions, lessentiel tant de continuer
progresser. Considrez lacte dapprendre comme faisant partie de votre plan de
carrire, et adonnez-vous aux sujets qui vous passionnent le plus. Pensez aux
comptences que vous pouvez acqurir avec le temps, et entreprenez ce quil faut
pour cela.
Steve.Metsker@acm.org
William.Wake@acm.org

pattern Livre Page 337 Vendredi, 9. octobre 2009 10:31 10

B
Solutions
Introduction aux interfaces
Solution 2.1

Une classe abstraite ne contenant aucune mthode non abstraite est semblable une
interface du point de vue de son utilit. Notez toutefois les nuances suivantes :
m

Une classe peut implmenter autant dinterfaces que ncessaire mais elle ne
peut tendre au plus quune classe abstraite.
Une classe abstraite peut avoir des mthodes non abstraites, mais toutes les
mthodes dune interface sont abstraites.
Une classe abstraite peut dclarer et utiliser des champs alors quune interface
ne le peut pas, bien quelle puisse crer des constantes static final.
Une classe abstraite peut avoir des mthodes dclares public, protected,
private ou sans accs (package). Les mthodes dune interface ont un accs
implicitement public.
Une classe abstraite peut dfinir des constructeurs alors quune interface ne le
peut pas.

pattern Livre Page 338 Vendredi, 9. octobre 2009 10:31 10

338

Partie VI

Annexes

Solution 2.2

A. Vrai. Les mthodes dune interface sont toujours abstraites mme sans dclaration
explicite.
B. Vrai. Les mthodes dune interface sont publiques mme sans dclaration explicite.
C. Faux. La visibilit dune interface peut se limiter au package dans lequel elle rside.
Dans ce cas, elle est marque public afin que les classes externes com.oozinoz.simulation puissent y accder.
D. Vrai. Par exemple, les interfaces List et Set tendent toutes deux linterface
Collection dans java.util.
E. Faux. Une interface sans mthode est appele interface de marquage. Parfois, une
mthode haut dans la hirarchie de classe, telle que Object.clone(), nest pas
approprie pour toutes les sous-classes. Vous pouvez crer une interface de
marquage qui demande aux sous-classes dopter ou non pour une telle stratgie.
La mthode clone() sur Object requiert que les sous-classes optent pour la stratgie, en dclarant quelles implmentent linterface de marquage Cloneable.
F. Faux. Une interface ne peut dclarer des champs dinstance, bien quelle puisse
crer des constantes en dclarant des champs qui sont static et final.
G. Faux. Ce peut tre une bonne ide mais une interface ne dispose daucun moyen
pour exiger que les classes limplmentant fournissent un certain constructeur.
Solution 2.3

Un tel exemple se produit lorsque des classes peuvent tre enregistres en tant que
listeners dvnements. Les classes reoivent des notifications pour leur propre
compte et non de celui de lappelant. Par exemple, nous pourrions ragir avec
MouseListener.mouseDragged() mais avoir un corps vide pour Listener.mouseMoved() pour le mme listener.

ADAPTER
Solution 3.1

Votre solution devrait ressembler au diagramme illustr la Figure B.1.

pattern Livre Page 339 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

339

Figure B.1
La classe OozinozRocket adapte la
classe PhysicalRocket pour
rpondre aux
besoins dclars
dans linterface
RocketSim.

PhysicalRocket
interface
PhysicalRocket(
burnArea:double,
burnRate:double,
fuelMass:double,
totalMass:double)
getBurnTime():double

RocketSim
getMass():double
getThrust():double
setSimTime(t:double)

getMass(t:double):double
getThrust(t:double):double

OozinozRocket
time:double
OozinozRocket(
burnArea:double,
burnRate:double,
fuelMass:double,
totalMass:double)
getMass():double
getThrust():double
setSimTime(t:double)

Les instances de la classe OozinozRocket peuvent fonctionner en tant quobjets


PhysicalRocket ou RocketSim. Le pattern ADAPTER vous permet dadapter les
mthodes que vous avez celles dont un client a besoin.
Solution 3.2

Le code pour complter la classe devrait tre :


package com.oozinoz.firework;
import com.oozinoz.simulation.*;
public class OozinozRocket
extends PhysicalRocket implements RocketSim {
private double time;
public OozinozRocket(
double burnArea,
double burnRate,
double fuelMass,
double totalMass) {
super(burnArea, burnRate, fuelMass, totalMass);
}

pattern Livre Page 340 Vendredi, 9. octobre 2009 10:31 10

340

Partie VI

Annexes

public double getMass() {


return getMass(time);
}
public double getThrust() {
return getThrust(time);
}
public void setSimTime(double time) {
this.time = time;
}
}

Vous trouverez ce code dans le package com.oozinoz.firework du code source


compagnon de ce livre.
Solution 3.3

La Figure B.2 illustre une solution.


Skyrocket
#simTime:double
...

PhysicalRocket

Skyrocket(
mass:double,
thrust:double
burnTime:double)

PhysicalRocket(
burnArea:double,
burnRate:double,
fuelMass:double,
totalMass:double)
getBurnTime():double

getMass():double

getMass(t:double):double

getThrust():double

getThrust(t:double):double

setSimTime(t:double)

OozinozSkyrocket
rocket:PhysicalRocket
OozinozRocket(
r:PhysicalRocket)
getMass():double
getThrust():double

Figure B.2
Un objet OozinozSkyrocket est un objet Skyrocket, mais son travail est ralis
par transmission des appels un objet PhysicalRocket.

pattern Livre Page 341 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

341

La classe OozinozSkyrocket est un adaptateur dobjet. Elle tend Skyrocket pour


quun objet OozinozSkyrocket puisse fonctionner o un objet Skyrocket est requis.
Solution 3.4

La conception avec adaptateur dobjet utilise par la classe OozinozSkyrocket


peut se rvler plus fragile quune approche par adaptateur de classe pour les
raisons suivantes :
m

Il ny a aucune spcification de linterface que la classe OozinozSkyrocket


fournit. Par consquent, mme si cela na pas t dtect la compilation,
Skyrocket pourrait changer dune certaine faon qui pourrait tre source de
problmes lors de lexcution.
La classe OozinozSkyrocket compte sur la capacit accder la variable
simTime de sa classe parent, bien quil ny ait aucune garantie que cette
variable sera toujours dclare comme tant protge et aucune certitude quant
la signification de ce champ dans la classe Skyrocket. Nous nattendons pas
de la part des fournisseurs du code quils scartent de leur conception pour
changer le code Skyrocket sur lequel nous nous appuyons, mais nous disposons
toutefois dun contrle limit sur ce quils font.

Solution 3.5

Votre code pourrait ressembler lexemple suivant :


package app.adapter;
import javax.swing.table.*;
import com.oozinoz.firework.Rocket;
public class RocketTable extends AbstractTableModel {
protected Rocket[] rockets;
protected String[] columnNames = new String[] {
"Name", "Price", "Apogee" };
public RocketTable(Rocket[] rockets) {
this.rockets = rockets;
}
public int getColumnCount() {
return columnNames.length;
}
public String getColumnName(int i) {
return columnNames[i];
}
public int getRowCount() {
return rockets.length;
}

pattern Livre Page 342 Vendredi, 9. octobre 2009 10:31 10

342

Partie VI

Annexes

public Object getValueAt(int row, int col) {


switch (col) {
case 0:
return rockets[row].getName();
case 1:
return rockets[row].getPrice();
case 2:
return new Double(rockets[row].getApogee());
default:
return null;
}
}
}

Linterface TableModel est un bon exemple de lefficacit de prvoir lavance une


adaptation. Cette interface, ainsi que son implmentation partielle AbstractTableModel, limite le travail ncessaire pour montrer les objets spcifiques dans
une table de GUI. Votre solution devrait permettre de constater comme il est simple
de recourir ladaptation lorsquelle est supporte par une interface.
Solution 3.6
m

Un argument pour. Lorsque lutilisateur clique avec la souris, je dois traduire,


ou adapter, lappel Swing rsultant en une action approprie. En dautres
termes, lorsque je dois adapter des vnements de GUI linterface de mon
application, jemploie les classes dadaptation de Swing. Je ralise une conversion
dune interface une autre, cest--dire lobjectif du pattern ADAPTER.
Un argument contre. Les classes dadaptation dans Swing sont des stubs : elles
nadaptent rien. Vous drivez des sous-classes de ces classes, o vous implmentez alors les mthodes qui doivent raliser quelque chose. Ce sont vos
mthodes et votre classe qui appliqueraient ventuellement le pattern ADAPTER.
Si "ladaptateur" de Swing avait t nomm disons DefaultMouseListener, cet
argument ne pourrait tre avanc.

FACADE
Solution 4.1

Voici quelques diffrences notables entre une dmo et une faade :


m

Une dmo est gnralement une application autonome alors quune faade ne
lest gnralement pas.

pattern Livre Page 343 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

Une dmo inclut gnralement un chantillon, ce quune faade ne fait pas.

Une faade est gnralement configurable, pas une dmo.

Une faade est prvue pour tre rutilise, pas une dmo.

Une faade est prvue pour tre utilise en production, pas une dmo.

343

Solution 4.2

La classe JOptionPane est lun des exemples peu nombreux dune faade dans les
bibliothques de classes Java. Elle est utilisable en production, configurable et est
prvue pour tre rutilise. Avant toute chose, JOptionPane ralise lobjectif du
pattern FACADE en fournissant une interface simple qui facilite lemploi dune
classe JDialog. Vous pouvez soutenir quune faade simplifie un "sous-systme" et
que la classe JDialog seule ne remplit pas les conditions pour tre un soussystme. Cest toutefois la richesse des fonctionnalits de cette classe qui donne
toute sa valeur une faade.
Sun Microsystems incorpore de nombreuses dmos dans son JDK. Ces classes ne
font toutefois jamais partie des bibliothques de classes Java. Cest--dire quelles
napparaissent pas dans les packages nomms avec le prfixe java. Une faade peut
appartenir aux bibliothques Java, mais pas les dmos.
La classe JOptionPane possde des dizaines de mthodes statiques qui font effectivement delle un utilitaire ainsi quune application de FACADE. Toutefois, strictement parler, elle ne remplit pas les conditions de la dfinition dans UML dun
utilitaire, laquelle exige que toutes les mthodes soient statiques.
Solution 4.3

Voici quelques points de vue acceptables mais opposs concernant la pnurie de


faades dans les bibliothques de classes Java.
m

En tant que dveloppeur Java, vous tes bien avis de dvelopper une profonde
connaissance des outils de la bibliothque. Les faades limitent ncessairement
la faon dont vous pouvez appliquer nimporte quel systme. Elles seraient une
distraction et un lment ventuel de mprise dans les bibliothques dans
lesquelles elles apparatraient.
Les caractristiques dune faade se situent quelque part entre la richesse dun
kit doutils et la spcificit dune application. Crer une faade requiert davoir

pattern Livre Page 344 Vendredi, 9. octobre 2009 10:31 10

344

Partie VI

Annexes

une certaine ide du type dapplications quelle supportera. Il est impossible de


prvoir cela compte tenu de limmense diversit du public utilisateur des bibliothques Java.
m

La prsence rare de faades dans les bibliothques Java est un point faible.
Lajout de davantage de faades serait fortement utile.

Solution 4.4

Votre rponse devrait sapparenter au diagramme de la Figure B.3.


Figure B.3
Ce diagramme illustre
lapplication de calcul
de trajectoire restructure en classes se
chargeant chacune
dune tche.

JPanel

ShowFlight2

main(:String[])

UI

PlotPanel

PlotPanel(nPoint:int,
xFunc:Function,
yFunc:Function)
paintComponent(:Graphics)

NORMAL:UI
createTitledPanel(
title:String,
panel:JPanel)

Function
f(t:double):double

Notez que createTitledPanel() nest pas une mthode statique. Votre solution at-elle prvu que ces mthodes soient statiques ? Pourquoi ou pourquoi pas ?
Le code prsent dans ce livre choisit les mthodes de UI comme tant non statiques
pour quune sous-classe de UI puisse les redfinir, crant ainsi un kit diffrent pour
laborer des interfaces graphiques utilisateur. Pour permettre la disponibilit dune
interface utilisateur standard, cette conception se rfre lobjet singleton NORMAL.
Voici le code appropri pour la classe UI:
public class UI {
public static final UI NORMAL = new UI();

pattern Livre Page 345 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

345

protected Font font =


new Font("Book Antiqua", Font.PLAIN, 18);
// beaucoup de choses omises
public Font getFont() {
return font;
}
public TitledBorder createTitledBorder(String title) {
TitledBorder border =
BorderFactory.createTitledBorder(
BorderFactory.createBevelBorder(
BevelBorder.RAISED),
title,
TitledBorder.LEFT,
TitledBorder.TOP);
border.setTitleColor(Color.black);
border.setTitleFont(getFont());
return border;
}
public JPanel createTitledPanel(
String title, JPanel in) {
JPanel out = new JPanel();
out.add(in);
out.setBorder(createTitledBorder(title));
return out;
}
}

Voyez le Chapitre 17 pour plus dinformations sur la conception de kits de GUI, et


le Chapitre 8 qui tudie les singletons.

COMPOSITE
Solution 5.1

Prvoir la classe Composite de faon pouvoir conserver une collection dobjets


Component demande quun objet Composite puisse contenir des objets feuilles ou
dautres objets composites.
En dautres termes, cette conception nous permet de modliser des groupes en tant
que collections dautres groupes. Par exemple, nous pouvons dfinir les privilges
systme dun utilisateur sous forme dune collection de privilges spcifiques ou
dautres groupes de privilges. Nous pourrions aussi dfinir une procdure en tant
que collection dtapes procdurales et dautres procdures. De telles dfinitions
sont beaucoup plus souples que la dfinition dun composite comme devant tre une
collection de feuilles uniquement.

pattern Livre Page 346 Vendredi, 9. octobre 2009 10:31 10

346

Partie VI

Annexes

Si vous nautorisez que des collections de feuilles, les composites ne pourront avoir
quun niveau de profondeur.
Solution 5.2

Pour la classe Machine, getMachineCount() devrait ressembler lexemple


suivant :
public int getMachineCount() {
return 1;
}

Le diagramme de classes montre que MachineComposite utilise un objet List pour


garder trace de ses composants. Pour compter les machines dans un composite,
vous pourriez crire le code suivant :
public int getMachineCount() {
int count = 0;
Iterator i = components.iterator();
while (i.hasNext()) {
MachineComponent mc = (MachineComponent) i.next();
count += mc.getMachineCount();
}
return count;
}

Si vous utilisez le JDK 1.5, vous pourriez utiliser une boucle for tendue.
Solution 5.3
Mthode

Classe

Dfinition

getMachineCount()

MachineComposite

Retourne la somme des comptes pour


chaque composant de Component

Machine

Retourne 1

MachineComposite

Retourne true si tous les composants


sont actifs

Machine

Retourne true si cette machine est active

MachineComposite

Indique tous les composants de tout


arrter

Machine

Arrte cette machine

isCompletelyUp()

stopAll()

pattern Livre Page 347 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

347

Mthode

Classe

Dfinition

getOwners()

MachineComposite

Cre un set (ensemble), pas une liste ;


ajoute les propritaires de tous les
composants, puis retourne le set

Machine

Retourne les propritaires de cette


machine

MachineComposite

Retourne une collection de tous les


produits sur les composants-machine

Machine

Retourne les produits se trouvant sur cette


machine

getMaterial()

Solution 5.4

Le programme affiche :
Nombre de machines: 4

Il ny a en fait que trois machines dans plant, mais la machine mixer est compte
par plant et bay. Ces deux objets contiennent une liste de composants-machine qui
rfrencent mixer.
Les rsultats pourraient tre pires. Supposez quun ingnieur ajoute lobjet plant
en tant que composant du composite bay, un appel de getMachineCount() entrerait
dans une boucle infinie.
Solution 5.5

Voici une implmentation raisonnable de MachineComposite.isTree():


protected boolean isTree(Set visited) {
visited.add(this);
Iterator i = components.iterator();
while (i.hasNext()) {
MachineComponent c = (MachineComponent) i.next();
if (visited.contains(c) || !c.isTree(visited))
return false;
}
return true;
}

pattern Livre Page 348 Vendredi, 9. octobre 2009 10:31 10

348

Partie VI

Annexes

Solution 5.6

Votre solution devrait montrer les liens de la Figure B.4.


Figure B.4
Les lignes paisses
dans ce diagramme
objet signalent
le cycle inhrent
au processus de
fabrication.

buildInnerShell:
ProcessStep

inspect:
ProcessStep

make:
ProcessSequence

reworkOrFinish:
ProcessAlternation

:ProcessSequence

disassemble:
ProcessStep

finish:
ProcessStep

BRIDGE
Solution 6.1

Pour contrler diverses machines avec une interface commune, vous pouvez appliquer le pattern ADAPTER crant une classe dadaptation pour chaque contrleur.
Chaque classe peut traduire les appels dinterface standard en appels grs par les
contrleurs existants.
Solution 6.2

Votre code pourrait tre comme suit :


public void shutdown() {
stopProcess();
conveyOut();
stopMachine();
}

pattern Livre Page 349 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

Solution 6.3

La Figure B.5 illustre une solution.


Figure B.5
Ce diagramme reprsente une abstraction
une hirarchie de types
de gestionnaires de
machine distincte des
implmentations de
lobjet abstrait driver
utilis par labstraction.

interface
MachineDriver

MachineManager2
driver:MachineDriver

startMachine()
stopMachine()

shutdown()

startProcess()
stopProcess()
conveyIn()
conveyOut()

HskMachineManager2

setTimeout(:double)

FuserDriver

StarPressDriver

Solution 6.4

La Figure B.6 suggre une solution.


:Client

:Connection
createStatement()
<<create>>
:Statement

executeQuery()

<<create>>
:ResultSet

next()

Figure B.6
Ce diagramme illustre le flux de messages principal intervenant dans une application JDBC.

349

pattern Livre Page 350 Vendredi, 9. octobre 2009 10:31 10

350

Partie VI

Annexes

Solution 6.5

Voici deux arguments en faveur de lcriture de code spcifique SQL Server :


1. Nous ne pouvons prvoir le futur. Aussi, investir de largent maintenant en prvision
dventualits qui pourraient ne jamais se produire est une erreur classique. Nous
disposons de SQL Server maintenant, et davantage de vitesse signifie de meilleurs
temps de rponse, ce qui signifie des fonds en banque aujourdhui.
2. En nous engageant exclusivement pour SQL Server, nous pouvons utiliser toutes les
fonctionnalits offertes par la base de donnes, sans avoir nous inquiter de savoir
si dautres drivers de base de donnes les supporteront.

Voici deux arguments en faveur de lemploi de drivers SQL gnriques :


1. Si nous utilisons des drivers SQL gnriques pour crire du code, il sera plus facile
de le modifier si nous changeons de fournisseur de base de donnes et commenons
utiliser, par exemple, Oracle. En verrouillant le code pour SQL Server, nous limitons
notre capacit tirer parti de loffre varie du march des bases de donnes.
2. Lemploi de drivers gnriques nous permet dcrire du code exprimental pouvant
sexcuter avec des bases de donnes peu onreuses, telles que MySql, sans
sappuyer sur un test spcifique dans SQL Server.

Introduction la responsabilit
Solution 7.1

Voici quelques problmes que posent le diagramme :


m

La mthode Rocket.thrust() retourne un objet Rocket la place dun type


quelconque de nombre ou de quantit physique.
La classe LiquidRocket possde une mthode getLocation() bien que rien
dans le diagramme ou le domaine de problmes ne suggre que les fuses
doivent avoir un emplacement. Mme si nous lavions fait, il ny a aucune raison
pour que les fuses combustible liquide pas les autres objets Rocket
aient un emplacement.
La mthode isLiquid() peut constituer une alternative acceptable lemploi
de loprateur instanceof, mais nous nous attendrions alors aussi ce que la
super-classe ait une mthode isLiquid() retournant false.

pattern Livre Page 351 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

351

CheapRockets (fuses bon march) est un nom pluriel bien que les noms de
classes soient par convention au singulier.
La classe CheapRockets implmente Runnable, bien que cette interface nait
pas affaire avec les objets CheapRockets du domaine de problmes.
Nous pourrions modliser laspect bon march avec seulement des attributs.
Il ny a donc aucune justification la cration dune classe simplement pour des
fuses bon march.
La classe CheapRockets introduit une structuration du code qui entre en conflit
avec la structuration du modle de fuse, lequel peut tre combustible liquide
ou solide. Par exemple, comment modliser une fuse combustible liquide bon
march ?
Le modle montre que Firework est une sous-classe de LiquidRocket impliquant que tous les artifices sont des fuses combustible liquide, ce qui est faux.
Le modle montre une relation directe entre les rservations et les types dartifices,
bien quaucune relation nexiste dans le domaine de problmes.
La classe Reservation possde sa propre copie de city, quelle devrait obtenir
par dlgation un objet Location.
CheapRockets se compose dobjets Runnable, ce qui est tout simplement
trange.

Solution 7.2

Lintrt de cet exercice est surtout de vous amener rflchir sur ce qui constitue
une bonne classe. Voyez si votre dfinition considre les points suivants :
m

Voici une description basique dune classe : "Un ensemble nomm de champs
contenant des valeurs de donnes, et de mthodes qui oprent sur ces valeurs"
[Flanagan 2005, p. 71].
Une classe tablit un ensemble de champs, cest--dire les attributs dun objet.
Le type de chaque attribut peut tre une classe, un type de donnes primitif, tel
que boolean et int, ou une interface.
Un concepteur de classes devrait tre en mesure dexpliquer de quelle faon les
attributs dune classe sont lis.

pattern Livre Page 352 Vendredi, 9. octobre 2009 10:31 10

352

m
m

m
m

Partie VI

Annexes

Une classe devrait remplir un objectif logique.


Le nom dune classe devrait reflter la signification de la classe, la fois en tant
que collection dattributs et en ce qui concerne son comportement.
Une classe doit supporter tous les comportements quelle dfinit, ainsi que ceux
des super-classes et toutes les mthodes des interfaces quelle implmente la
dcision de ne pas supporter une mthode dune super-classe ou dune interface
peut occasionnellement se justifier.
La relation dune classe vis--vis de sa super-classe doit tre justifiable.
Le nom de chaque mthode dune classe devrait constituer un bon commentaire
de ce quelle accomplit.

Solution 7.3

Deux bonnes remarques sont que le rsultat dinvoquer une opration peut dpendre de ltat de lobjet rcepteur ou de la classe de lobjet rcepteur. A dautres
moments, vous utilisez un nom impos par quelquun dautre.
Un exemple de mthode dont leffet dpend de ltat de lobjet concern apparat au
Chapitre 6, o la classe MachineManager2 possde une mthode stopMachine().
Le rsultat de lappel de cette mthode dpend du driver qui est en place pour
lobjet MachineManager2.
Lorsque le polymorphisme fait partie de la conception, leffet dinvoquer une
opration peut partiellement ou totalement dpendre de la classe de lobjet rcepteur. Ce principe apparat dans de nombreux patterns, surtout avec FACTORY
METHOD, STATE, STRATEGY, COMMAND et INTERPRETER. Par exemple, les classes de
stratgie dune hirarchie peuvent toutes implmenter une mthode getRecommended(), en utilisant diffrentes stratgies pour recommander un artifice particulier.
Il est facile de comprendre que getRecommended() recommandera un artifice ;
mais sans connatre la classe de lobjet qui reoit lappel de la mthode, il est
impossible de connatre la stratgie sous-jacente qui sera utilise.
Une troisime situation se produit lorsquune autre personne dfinit le nom. Supposez que votre mthode agisse en tant que callback : vous avez redfini la mthode
mouseDown() de la classe MouseListener. Vous devez utiliser mouseDown()
comme nom de mthode, mme sil nindique rien sur lintention de votre mthode.

pattern Livre Page 353 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

353

Solution 7.4

Le code compile sans problme. Laccs est dfini au niveau classe et non au niveau
objet. Aussi un objet Firework peut-il accder aux variables et mthodes prives
dun autre objet Firework, par exemple.

SINGLETON
Solution 8.1

Pour empcher dautres dveloppeurs dinstancier votre classe, crez un seul constructeur avec un accs private. Notez que si vous crez dautres constructeurs non
privs, ou ne crez pas de constructeurs du tout, dautres objets seront en mesure
dinstancier votre classe.
Solution 8.2

Voici deux raisons de recourir linitialisation tardive, ou paresseuse (lazy) :


1. Vous pourriez ne pas disposer de suffisamment dinformations pour instancier un singleton au moment voulu. Par exemple, un singleton Factory pourrait tre forc dattendre
que les machines relles tablissent des canaux de communication entre elles.
2. Vous pourriez choisir dinitialiser tardivement un singleton qui requiert des ressources, telles quune connexion une base de donnes, surtout sil y a une possibilit
que lapplication conteneur ne require pas le singleton durant une certaine session.
Solution 8.3

Votre solution devrait liminer toute possibilit de confusion pouvant se produire lorsque deux threads appellent la mthode recordWipMove() peu prs au mme moment :
public void recordWipMove() {
synchronized (classLock) {
wipMoves++;
}
}

Est-il possible quun thread puisse devenir actif au beau milieu dune opration
dincrmentation ? Absolument : toutes les machines nont pas quune seule
instruction pouvant incrmenter une variable, et mme celles qui sont dans ce cas
ne peuvent pas garantir que le compilateur les utilisera dans toutes les situations.
Cest une bonne stratgie que de restreindre laccs aux donnes dun singleton
dans une application multithread. Voir Concurrent Programming in Java [Lea
2000] pour plus dinformations sur de telles applications.

pattern Livre Page 354 Vendredi, 9. octobre 2009 10:31 10

354

Partie VI

Annexes

Solution 8.4
OurBiggestRocket:

Cette classe a un nom appropri. Vous devriez normalement modliser des proprits, telles que "biggest" par exemple, par des attributs
et non par des noms de classes. Si un dveloppeur devait grer cette
classe, ce serait peut-tre en tant que singleton.

topSalesAssociate:

Cette classe prsente le mme problme que OurBiggestRocket.

Math:

Cest une classe utilitaire, avec uniquement des mthodes statiques et


aucune instance. Ce nest pas un singleton. Notez quelle possde
toutefois un constructeur priv.

System:

Cest galement une classe utilitaire.

PrintStream:

Bien que lobjet System.out soit un objet PrintStream avec des


responsabilits uniques, il ne constitue pas une instance unique de
PrintStream, qui nest pas un singleton.

PrintSpooler:

Un PrintSpooler est associ une imprimante ou quelques imprimantes. Il est peu vraisemblable que ce soit un singleton.

PrinterManager:

Chez Oozinoz, il y a plusieurs imprimantes et vous pouvez rechercher


leurs adresses respectives par lintermdiaire du singleton PrinterManager.

OBSERVER
Solution 9.1

Une solution possible est :


public JSlider slider() {
if (slider == null) {
slider = new JSlider();
sliderMax = slider.getMaximum();
sliderMin = slider.getMinimum();
slider.addChangeListener(this);
slider.setValue(slider.getMinimum());
}
return slider;
}
public void stateChanged(ChangeEvent e) {
double val = slider.getValue();

pattern Livre Page 355 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

355

double tp = (val - sliderMin) / (sliderMax - sliderMin);


burnPanel().setTPeak(tp);
thrustPanel().setTPeak(tp);
valueLabel().setText(Format.formatToNPlaces(tp, 2));
}

Ce code suppose quune classe auxiliaire Format existe pour formater le champ de
valeur. Vous pourriez avoir utilis la place une expression telle que ""+tp ou
Double.toString(tp) lemploi dun nombre de chiffres constant produit une
animation plus fluide.
Solution 9.2

Une solution est montre la Figure B.7. Pour permettre un champ de valeur de
senregistrer pour tre notifi des vnements de curseur, la conception dans la
Figure B.7 cre une sous-classe JLabel qui implmente ChangeListener.
Figure B.7
Dans cette conception, les composants
qui dpendent du
curseur implmentent ChangeListener
pour pouvoir senregistrer et tre notifis
des vnements
de dplacement
du curseur.

JPanel

ChangeListener

2
ShowBallistics2

burnPanel():
BallisticsPanel2

BallisticsPanel2

stateChanged(
e:ChangeEvent)

slider():JSlider
thrustPanel():
BallisticsPanel2

JLabel

ChangeListener

valueLabel():
BallisticsLabel2
BallisticsLabel2

stateChanged(
e:ChangeEvent)

La nouvelle conception permet aux composants dpendants du curseur de senregistrer pour tre notifis et de sactualiser ainsi eux-mmes. Cest une amlioration
discutable, mais nous restructurerons nouveau la conception vers une architecture
Modle-Vue-Contrleur.

pattern Livre Page 356 Vendredi, 9. octobre 2009 10:31 10

356

Partie VI

Annexes

Solution 9.3

La Figure B.8 illustre une solution.


Figure B.8
Cette conception
prvoit que lapplication
surveille le curseur.
Le champ de valeur
et les panneaux daffichage restent attentifs
aux changements
dun objet contenant
la valeur tPeak.

JPanel

ChangeListener

2
ShowBallistics2

burnPanel():
BallisticsPanel2

BallisticsPanel2
stateChanged(
e:ChangeEvent)

slider():JSlider
thrustPanel():
BallisticsPanel2

JLabel

ChangeListener

valueLabel():
BallisticsLabel2
BallisticsLabel2
stateChanged(
e:ChangeEvent)

Un objet Tpeak qui contient une valeur de temps crte joue un rle central dans
cette conception. Lapplication ShowBallistics3 cre lobjet Tpeak, et le curseur
lactualise lorsque sa position change. Les composants daffichage (le champ de
valeur et les panneaux de trac) restent " lcoute" de lobjet Tpeak en senregistrant
en tant quobservateurs de lobjet.
Solution 9.4

Voici une solution possible :


package app.observer.ballistics3;
import javax.swing.*;
import java.util.*;
public class BallisticsLabel extends JLabel
implements Observer {
public BallisticsLabel(Tpeak tPeak) {
tPeak.addObserver(this);
}
public void update(Observable o, Object arg) {
setText("" + ((Tpeak) o).getValue());
repaint();
}
}

pattern Livre Page 357 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

357

Solution 9.5

Voici une solution possible :


protected JSlider slider() {
if (slider == null) {
slider = new JSlider();
sliderMax = slider.getMaximum();
sliderMin = slider.getMinimum();
slider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
if (sliderMax == sliderMin) return;
tPeak.setValue(
(slider.getValue() - sliderMin)
/ (sliderMax - sliderMin));
}
});
slider.setValue(slider.getMinimum());
}
return slider;
}

Solution 9.6

La Figure B.9 illustre le flux dappels qui se produit lorsquun utilisateur dplace le
curseur de lapplication de calculs balistiques.
:JSlider

:BallisticsLabel

:ChangeListener
Couche GUI
Couche
mtier
:Tpeak
stateChanged()
setValue()

update()

Figure B.9
La conception MVC force un cheminement des messages par une couche mtier.

pattern Livre Page 358 Vendredi, 9. octobre 2009 10:31 10

358

Partie VI

Annexes

Solution 9.7

Votre diagramme devrait ressembler lexemple de la Figure B.10. Notez que vous
pouvez appliquer la mme conception avec Observer et Observable. La cl de
cette conception rside dans le fait que la classe Tpeak intressante se rend ellemme observable en grant un objet avec des capacits dcoute.
PropertyChangeListener

BallisticsPanel

Tpeak

PropertyChangeListener

BallisticsLabel

PropertyChangeSupport

getValue()
setValue(:double)
addPropertyChangeListener(
:PropertyChangeListener)
firePropertyChange(
propertyName:String,
oldValue:Object,
newValue:Object)
removePropertyChangeListener(
:PropertyChangeListener)

Figure B.10
La classe Tpeak peut ajouter des fonctionnalits dcoute en dlguant les appels orients
coute un objet PropertyChangeSupport.

pattern Livre Page 359 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

359

MEDIATOR
Solution 10.1

La Figure B.11 illustre une solution.

MoveATub2

MoveATubMediator

MoveATubMediator(
gui:MoveATub,data:NameBase)

MoveATub()
assignButton():Button

actionPerformed(:ActionEvent)
valueChange(
:ListSelectionEvent)
updateTubList(:String)

boxList():JList
machineList():JList
tubList():JList
main(:String[])

ActionListener

ListSelectionListener

NameBase

boxes():Object[]
tubNames():Object[]
getMachineContaining(tubName:String):String
put(tubName:String,toMachineName:String)

Figure B.11
La classe MoveATub gre la construction de composants et la classe MoveATubMediator
gre les vnements.

Dans cette conception, la classe de mdiation rvle ce qui est cit dans Fowler et
al. [1999] comme tant un symptme de "Feature Envy", o elle semble plus intresse par la classe de GUI que par elle-mme, comme le montre la mthode
valueChanged():
public void valueChanged(ListSelectionEvent e) {
//

pattern Livre Page 360 Vendredi, 9. octobre 2009 10:31 10

360

Partie VI

Annexes

gui.assignButton().setEnabled(
! gui.tubList().isSelectionEmpty()
&& ! gui.machineList().isSelectionEmpty());
}

Le rejet par certains dveloppeurs de cet aspect "dsir de fonctionnalit" les carte
de ce genre de conception. Vous pourrez toutefois prouver le besoin davoir une
classe pour la construction et le placement dun composant de GUI et une classe
spare pour grer linteraction avec le composant.
Solution 10.2

La Figure B.12 illustre une solution possible.


Figure B.12
Ce diagramme
illustre le rle central
du mdiateur.

assignButton

data

mediator

tubList

actionPerformed()
getMachineContaining()
put()

updateTubList()

setEnabled(false)

La solution fournie met en valeur le rle du mdiateur en tant que dispatcher,


recevant un vnement et assumant la responsabilit dactualiser tous les objets
concerns.

pattern Livre Page 361 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

361

Solution 10.3

La Figure B.13 prsente un diagramme objet actualis.


Figure B.13
Deux machines pensent
quelles contiennent
un bac T308. Le modle
objet accepte une situation que ni une table
relationnelle ni la ralit
naccepteraient les
traits pais soulignent la
situation problmatique.

:RocketImpl

ShowRocketClient

:RocketImpl_Stub

getApogee()
getApogee()

Le problme que le code du dveloppeur introduit est que la machine StarPress2402 pense toujours quelle possde un bac T308. Dans une table relationnelle,
le changement des attributs de machine dune ligne retire automatiquement le bac
de la machine prcdente. Cette suppression automatique ne se produit pas lorsque
la relation est disperse travers un modle objet rparti. La modlisation
correcte de la relation bac/machine ncessite une logique spciale que vous pouvez
dplacer vers un objet mdiateur distinct.
Solution 10.4

Le code complet pour la classe TubMediator devrait ressembler lexemple


suivant :
package com.oozinoz.machine;
import java.util.*;
public class TubMediator {
protected Map tubToMachine = new HashMap();
public Machine getMachine(Tub t) {
return (Machine) tubToMachine.get(t);
}
public Set getTubs(Machine m) {
Set set = new HashSet();

pattern Livre Page 362 Vendredi, 9. octobre 2009 10:31 10

362

Partie VI

Annexes

Iterator i = tubToMachine.entrySet().iterator();
while (i.hasNext()) {
Map.Entry e = (Map.Entry) i.next();
if (e.getValue().equals(m))
set.add(e.getKey());
}
return set;
}
public void set(Tub t, Machine m) {
tubToMachine.put(t, m);
}
}

Solution 10.5
m

Le pattern FACADE peut aider la restructuration dune grande application.

Le pattern BRIDGE peut placer les oprations abstraites dans une interface.

Le pattern OBSERVER peut intervenir pour restructurer du code pour supporter


une architecture MVC.
Le pattern FLYWEIGHT extrait la partie immuable dun objet pour quelle puisse
tre partage.
Le pattern BUILDER place la logique de construction dun objet en dehors de la
classe instancier.
Le pattern FACTORYMETHOD permet de rduire les responsabilits dune hirarchie de classes en plaant un aspect des comportements dans une hirarchie
parallle.
Les patterns STATE et STRATEGY permettent de placer les comportements spcifiques un tat ou une stratgie dans des classes distinctes.

PROXY
Solution 11.1

Voici une solution possible :


public int getIconHeight() {
return current.getIconHeight();
}
public int getIconWidth() {
return current.getIconWidth();
}

pattern Livre Page 363 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

363

public synchronized void paintIcon(


Component c, Graphics g, int x, int y) {
current.paintIcon(c, g, x, y);
}

Solution 11.2

Voici quelques-uns des problmes que prsente cette conception :


m

La transmission dun sous-ensemble dappels seulement un objet ImageIcon


sous-jacent est prilleuse. La classe ImageIconProxy hrite dune dizaine de
champs et dau moins 25 mthodes de la classe ImageIcon. Pour tre un vritable proxy, lobjet ImageIconProxy devrait transmettre la totalit ou la plupart des
appels. Une retransmission dtaille ncessiterait beaucoup de mthodes potentiellement fautives, et ce code demanderait de la maintenance supplmentaire
mesure que la classe ImageIcon et ses super-classes changeraient dans le temps.
Vous pouvez vous demander si limage "absente" et limage souhaite se trouvent au bon endroit dans la conception. Il pourrait tre plus logique que les
images soient passes plutt que de rendre la classe responsable de leur obtention.

Solution 11.3

La mthode load() dfinit comme image "Loading", alors que la mthode


run(), qui sexcute dans un thread distinct, charge limage dsire :
public void load(JFrame callbackFrame) {
this.callbackFrame = callbackFrame;
setImage(LOADING.getImage());
callbackFrame.repaint();
new Thread(this).start();
}
public void run() {
setImage(new ImageIcon(
ClassLoader.getSystemResource(filename))
.getImage());
callbackFrame.pack();
}

Solution 11.4

Comme le montre le diagramme de classes, un constructeur RocketImpl accepte


une valeur de prix et dapoge.
Rocket biggie = new rocketImpl(29.95, 820);

pattern Livre Page 364 Vendredi, 9. octobre 2009 10:31 10

364

Partie VI

Annexes

Vous pourriez dclarer biggie de type RocketImpl. Toutefois, limportant


propos de biggie est quil remplisse lobjectif de linterface Rocket quun client
rechercherait.
Solution 11.5

Le diagramme complt devrait ressembler lillustration de la Figure B.14. Une


autre dcision lgitime, toutefois moins explicite, pourrait dclarer les deux
rcepteurs de getApogee() comme tant de type Rocket. En fait, les programmes
serveur et client se rfrent tous deux ces objets en tant quinstances de linterface Rocket.
Figure B.14
Un proxy transmet
les appels dun client
de sorte que lobjet
distant apparaisse
local au client.

:RocketImpl

ShowRocketClient

:RocketImpl_Stub

getApogee()
getApogee()

CHAIN OF RESPONSABILITY
Solution 12.1

Voici quelques-uns des inconvnients possibles de la conception CHAIN OF


RESPONSABILITY utilise par Oozinoz pour trouver lingnieur responsable dune
machine :
m

Nous navons pas spcifi comment la chane sera mise en place pour que les
machines connaissent leur parent. Dans la pratique, il peut se rvler difficile de
garantir que les parents ne soient jamais null.
Il est concevable que le processus de recherche dun parent entre dans une
boucle infinie, selon la faon dont les parents sont implments.

pattern Livre Page 365 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

365

Tous les objets noffrent pas tous les comportements impliqus par ces nouvelles
mthodes. Par exemple, llment du niveau suprieur na pas de parent.
La conception prsente est insuffisante quant aux dtails affrents la faon
dont le systme sait quels ingnieurs sont actuellement prsents dans lusine et
disponibles. On ne sait pas clairement quelle devrait tre, en temps rel, la
porte de cette responsabilit.

Solution 12.2

Votre diagramme devrait ressembler lexemple prsent dans la Figure B.15.


Figure B.15
Chaque objet VisualizationItem peut
indiquer son ingnieur
responsable.
En interne, un tel objet
peut transmettre la
requte un autre
objet parent.

interface
VisualizationItem

getResponsible():Engineer

MachineComponent

getResponsible():Engineer

Tool

getResponsible():Engineer

ToolCart

getResponsible():Engineer

pattern Livre Page 366 Vendredi, 9. octobre 2009 10:31 10

366

Partie VI

Annexes

Avec cette conception, nimporte quel client de nimporte quel lment simul peut
simplement demander llment son ingnieur responsable. Cette approche libre
le client de la tche de dtermination des objets capables de comprendre la responsabilit et place cette charge au niveau des objets qui implmentent linterface
VisualizationItem.
Solution 12.3

A. Un objet MachineComponent peut avoir un responsable assign. Si ce nest pas le


cas, il passe la requte son parent :
public Engineer getResponsible() {
if (responsible != null)
return responsible;
if (parent != null)
return parent.getResponsible();
return null;
}

B. Le code de Tool.Responsible reflte la rgle stipulant que "les outils sont


toujours assigns aux chariots doutils" :
public Engineer getResponsible() {
return toolCart.getResponsible();
}

C. Le code de ToolCart reflte la rgle stipulant que "les chariots doutils ont un
ingnieur responsable" :
public Engineer getResponsible() {
return responsible;
}

Solution 12.4

Votre solution devrait ressembler au diagramme de la Figure B.16.


Vos constructeurs devraient autoriser les objets Machine et MachineComposite
tre instancis avec ou sans responsable assign. Chaque fois quun objet MachineComponent ne possde pas dingnieur assign, il peut obtenir un ingnieur de son
parent.

pattern Livre Page 367 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

367

Figure B.16
MachineComponent

Les constructeurs
dans la hirarchie

MachineComponent

parent:MachineComponent

supportent les rgles


stipulant quun objet
MachineRoot doit
avoir un ingnieur
responsable et que
chaque objet MachineComponent, except
lobjet racine, doit avoir
un parent.

responsible:Engineer
MachineComponent(
parent:MachineComponent)
responsible:Engineer)
MachineComponent(
parent:MachineComponent)

Machine

MachineComposite

Machine(
parent:MachineComponent)

MachineComposite(
parent:MachineComponent)

Machine(
parent:MachineComponent,
responsible:Engineer)

MachineComposite(
parent:MachineComponent,
responsible:Engineer)

MachineRoot

MachineRoot(
responsible:Engineer)

Solution 12.5

Le pattern CHAIN OF RESPONSABILITY pourrait sappliquer aux objets non composites dans les situations suivantes :
m

Une chane dingnieurs de permanence suivent une rotation standard. Si lingnieur de permanence principal ne rpond pas un appel par pageur du service de

pattern Livre Page 368 Vendredi, 9. octobre 2009 10:31 10

368

Partie VI

Annexes

production dans un certain intervalle de temps, le systme de notification


appelle le prochain ingnieur dans la chane.
m

Des utilisateurs saisissent des informations, telles que la date dun vnement, et
une chane danalyseurs syntaxiques tentent tour de rle de dcoder le texte
saisi.

FLYWEIGHT
Solution 13.1

Un argument en faveur de limmuabilit des chanes. Dans la pratique, les chanes


sont frquemment partages entre plusieurs clients, ce qui constitue lessentiel des
dfauts qui apparaissent lorsquun client a accidentellement un impact sur un autre.
Par exemple, une mthode qui retourne le nom dun client sous forme dune
chane conservera gnralement une rfrence au nom. Si le client convertit la chane
en lettre majuscules pour lutiliser dans une table indexe par hachage, le nom
de lobjet Customer changera galement, si ce nest pour limmuabilit des chanes.
Dans Java, vous pouvez produire une version dune chane en majuscules, mais il
faut que cela soit un nouvel objet, et non une version modifie de la chane initiale.
Limmuabilit des chanes permet de les partager en toute scurit. De plus, cela
aide le systme viter certains risques pour la scurit.
Un argument contre limmuabilit des chanes. Limmuabilit des chanes nous
vite de commettre certaines erreurs mais le prix payer est lourd. Premirement, le dveloppeur est priv de la possibilit de modifier une chane, indpendamment de la faon dont il pourrait justifier ce besoin. Deuximement, lajout
de certaines rgles spciales un langage le rend plus difficile apprendre et
utiliser. Java est bien plus difficile apprendre que le langage Smalltalk, qui est
aussi puissant. Finalement, aucun langage informatique ne peut mempcher de
commettre des erreurs. Je prfre pouvoir apprendre un langage rapidement et
avoir ainsi le temps dapprendre mettre en place et utiliser un environnement
de test.

pattern Livre Page 369 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

369

Solution 13.2

Vous pouvez dplacer les aspects immuables de Substance nom, symbole


et poids atomique inclus vers une classe Chemical, comme le montre la
Figure B.17.

Substance2

Chemical

grams:double

name:String

chemical:Chemical

symbol:String
atomicWeight:double

getName():String
getSymbol():String

getName():String

getAtomicWeight():double

getSymbol():String

getGrams():double

getAtomicWeight():double

getMoles():double

Figure B.17
Ce diagramme montre la portion immuable de la classe Substance place dans
une classe Chemical.

La classe Substance2 conserve maintenant une rfrence un objet Chemical.


Par consquent, la classe Substance2 peut toujours offrir les mmes mthodes
accesseurs que la classe Substance originale. En interne, ces accesseurs
sappuient sur la classe Chemical, comme le montrent les mthodes de Substance2
suivantes :
public double getAtomicWeight() {
return chemical.getAtomicWeight();
}
public double getGrams() {
return grams;
}
public double getMoles() {
return grams / getAtomicWeight();
}

pattern Livre Page 370 Vendredi, 9. octobre 2009 10:31 10

370

Partie VI

Annexes

Solution 13.3

Un moyen qui ne fonctionnera pas est de dclarer private le constructeur de Chemical. Cela empcherait la classe ChemicalFactory dinstancier la classe Chemical.
Pour contribuer empcher les dveloppeurs dinstancier eux-mmes la classe
Chemical, vous pouvez placer les classes Chemical et ChemicalFactory dans le
mme package et offrir un accs par dfaut ("package") au constructeur de la classe
Chemical.
Solution 13.4

Lapproche par classe imbrique est bien plus complexe mais plus complte pour
sassurer que seule la classe ChemicalFactory2 peut instancier de nouveaux objets
flyweight. Le code rsultant devrait ressembler lexemple suivant :
package com.oozinoz.chemical2;
import java.util.*;
public class ChemicalFactory2 {
private static Map chemicals = new HashMap();
class ChemicalImpl implements Chemical {
private String name;
private String symbol;
private double atomicWeight;
ChemicalImpl(
String name,
String symbol,
double atomicWeight) {
this.name = name;
this.symbol = symbol;
this.atomicWeight = atomicWeight;
}
public String getName() {
return name;
}
public String getSymbol() {
return symbol;
}
public double getAtomicWeight() {
return atomicWeight;
}
public String toString() {
return name + "(" + symbol + ")[" +
atomicWeight + "]";
}
}
static {
ChemicalFactory2 factory = new ChemicalFactory2();

pattern Livre Page 371 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

371

chemicals.put("carbon",
factory.new ChemicalImpl("Carbon", "C", 12));
chemicals.put("sulfur",
factory.new ChemicalImpl("Sulfur", "S", 32));
chemicals.put("saltpeter",
factory.new ChemicalImpl(
"Saltpeter", "KN03", 101));
//...
}
public static Chemical getChemical(String name) {
return (Chemical) chemicals.get(
name.toLowerCase());
}
}

Ce code rpond aux trois exercices de la faon suivante :


1. La classe ChemicalImpl imbrique devrait tre prive pour que seule la classe
ChemicalFactory2 puisse lutiliser. Notez que laccs la classe imbrique devrait
tre de niveau package ou public pour que la classe conteneur puisse linstancier.
Mme si vous dclariez le constructeur public, aucune autre classe ne pourrait utiliser
le constructeur si la classe imbrique elle-mme est dclare private.
2. Le constructeur de ChemicalFactory2 utilise un initialisateur statique pour garantir
que la classe ne construira quune seule fois la liste de substances chimiques.
3. La mthode getChemical() devrait rechercher les substances chimiques par nom
dans la table indexe par hachage. Lexemple de code stocke et recherche les substances
en utilisant la version en minuscules de leur nom.

Introduction la construction
Solution 14.1

Voici quelques-unes des rgles spciales concernant les constructeurs :


m

Si vous ne fournissez pas de constructeur pour une classe, Java en fournira un


par dfaut.
Les noms de constructeurs doivent correspondre aux noms de leurs classes
respectives pour cette raison, ils commencent gnralement par une capitale,
la diffrence de la plupart des noms de mthodes.
Les constructeurs peuvent invoquer dautres constructeurs avec this() et
super() tant que cette invocation reprsente la premire instruction dans le
constructeur.

pattern Livre Page 372 Vendredi, 9. octobre 2009 10:31 10

372

Partie VI

Annexes

Le "rsultat" de laction dun constructeur est une instance de la classe, alors


que le type de retour dune mthode ordinaire peut tre nimporte quoi.
Vous pouvez utiliser new ou la rflexion pour invoquer un constructeur.

Solution 14.2

Le code suivant chouera la compilation une fois plac dans Fuse.java et QuickFuse.java.
package app.construction;
public class Fuse {
private String name;
public Fuse(String name) { this.name = name; }
}

et
package app.construction;
public class QuickFuse extends Fuse { }

Le compilateur gnrera un message derreur dont la teneur sera peu prs la


suivante :
Constructeur Fuse() super implicite indfini pour un
constructeur par dfaut. Constructeur explicite
obligatoire.

Cette erreur se produit lorsque le compilateur rencontre la classe QuickFuse et


fournit un constructeur par dfaut. Celui-ci nattend pas dargument et invoque,
nouveau par dfaut, le constructeur de la super-classe sans argument. Toutefois, la
prsence dun constructeur Fuse() qui accepte un paramtre String signifie que le
compilateur ne fournira plus de constructeur par dfaut pour Fuse. Le constructeur
par dfaut de QuickFuse ne peut alors plus invoquer le constructeur de la superclasse sans argument car il nexiste plus.
Solution 14.3

Le programme affiche :
java.awt.Point[x=3,y=4]

Le programme a russi trouver un constructeur acceptant deux arguments et a cr


un nouveau point avec les arguments donns.

pattern Livre Page 373 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

373

BUILDER
Solution 15.1

Une faon de rendre lanalyseur plus flexible est de lui permettre daccepter
plusieurs espaces aprs une virgule. Pour cela, changez la construction de lappel de
split() de la manire suivante :
s.split(", *");

Ou bien, au lieu daccepter des espaces aprs une virgule, vous pouvez autoriser
nimporte quel type despace en initialisant lobjet Regex comme suit :
s.split(",\\s*")

La combinaison de caractres \s signifie la "catgorie" de caractres blancs dans


les expressions rgulires. Notez que ces solutions supposent quil ny aura pas de
virgule insre dans les champs.
Plutt que de rendre lanalyseur plus souple, vous pouvez remettre en question
lapproche. En particulier, vous pouvez solliciter des agences de voyages quelles
commencent envoyer les rservations au format XML. Vous pouvez tablir un
ensemble de balises de marquage utiliser et les lire au moyen dun analyseur XML.
Solution 15.2

La mthode build() de UnforgivingBuilder gnre une exception si un attribut


est invalide et retourne sinon un objet Reservation valide. Voici une implmentation :
public Reservation build() throws BuilderException {
if (date == null)
throw new BuilderException("Valid date not found");
if (city == null)
throw new BuilderException("Valid city not found");
if (headcount < MINHEAD)
throw new BuilderException(
"Minimum headcount is " + MINHEAD);
if (dollarsPerHead.times(headcount)
.isLessThan(MINTOTAL))
throw new BuilderException(
"Minimum total cost is " + MINTOTAL);
return new Reservation(
date,
headcount,
city,
dollarsPerHead,
hasSite);
}

pattern Livre Page 374 Vendredi, 9. octobre 2009 10:31 10

374

Partie VI

Annexes

Le code vrifie que les valeurs de date et de ville sont dfinies et que les valeurs de
nombre de personnes et de prix par tte sont acceptables. La super-classe ReservationBuilder dfinit les constantes MINHEAD et MINTOTAL.
Si le constructeur ne rencontre pas de problmes, il retourne un objet Reservation
valide.
Solution 15.3

Comme vu prcdemment, le code doit gnrer une exception si la rservation omet


de spcifier une ville ou une date car il ny a aucun moyen de deviner ces valeurs.
Pour ce qui est de lomission des valeurs de nombre de personne et de prix par tte,
notez les points suivants :
m

Si la requte de rservation ne spcifie ni le nombre de personnes ni le prix par


tte, dfinissez la quantit de personnes la valeur minimale, et assignez au prix
par tte le rsultat de la division du prix total acceptable par la quantit minimale
de personnes.
Si le nombre de personnes est omis mais pas le prix par tte, dfinissez la quantit de personnes la valeur minimale tout en veillant ce quelle soit suffisante
pour atteindre la recette totale minimale acceptable pour un spectacle.
Si le nombre de personnes est indiqu mais pas le prix par tte, dfinissez celuici suffisamment haut pour gnrer la recette minimale.

Solution 15.4

Voici une solution possible :


public Reservation build() throws BuilderException {
boolean noHeadcount = (headcount == 0);
boolean noDollarsPerHead = (dollarsPerHead.isZero());
if (noHeadcount && noDollarsPerHead) {
headcount = MINHEAD;
dollarsPerHead = sufficientDollars(headcount);
} else if (noHeadcount) {
headcount = (int) Math.ceil(
MINTOTAL.dividedBy(dollarsPerHead));
headcount = Math.max(headcount, MINHEAD);
} else if (noDollarsPerHead) {
dollarsPerHead = sufficientDollars(headcount);
}

pattern Livre Page 375 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

375

check();
return new Reservation(
date,
headcount,
city,
dollarsPerHead,
hasSite);
}

Ce code sappuie sur une mthode check() qui est similaire la mthode build()
de la classe InforgivingBuilder:
protected void check() throws BuilderException {
if (date == null)
throw new BuilderException("Valid date not found");
if (city == null)
throw new BuilderException("Valid city not found");
if (headcount < MINHEAD)
throw new BuilderException(
"Minimum headcount is " + MINHEAD);
if (dollarsPerHead.times(headcount)
.isLessThan(MINTOTAL))
throw new BuilderException(
"Minimum total cost is " + MINTOTAL);
}

FACTORY METHOD
Solution 16.1

Une bonne rponse serait peut-tre de dire que vous navez pas besoin didentifier
la classe sous-jacente lobjet retourn par une mthode iterator(). Ce qui est
important, cest que vous connaissiez linterface supporte par litrateur, ce qui
vous permet dnumrer les lments dune collection. Toutefois, si vous devez
connatre la classe, vous pouvez afficher son nom avec une ligne dinstruction du
genre :
System.out.println(iter.getClass().getName());

Cette instruction affiche :


java.util.AbstractList$Itr

La classe Itr est une classe interne de AbstractList. Vous ne devriez probablement
jamais voir cette classe en travaillant avec Java.

pattern Livre Page 376 Vendredi, 9. octobre 2009 10:31 10

376

Partie VI

Annexes

Solution 16.2

Il y a de nombreuses rponses possibles, mais toString() est probablement la


mthode la plus frquemment utilise pour crer un nouvel objet. Par exemple, le
code suivant cre un nouvel objet String:
String s = new Date().toString();

La cration de chanes se produit souvent en coulisses. Considrez la ligne


suivante :
System.out.println(new Date());

Ce code cre un objet String partir de lobjet Date par lintermdiaire de la


mthode toString() de lobjet Date.
Une autre mthode couramment utilise pour crer un nouvel objet est clone(),
une mthode qui retourne gnralement une copie de lobjet rcepteur.
Solution 16.3

Lobjectif du pattern FACTORY METHOD est de permettre un fournisseur dobjets de


dterminer quelle classe instancier lors de la cration dun objet. Par comparaison,
les clients de BorderFactory savent exactement quels types dobjets ils obtiennent. Le pattern appliqu avec BorderFactory est FLYWEIGHT dans ce sens que
BorderFactory emploie le partage pour supporter efficacement un grand nombre
de bordures. La classe BorderFactory dispense les clients de devoir grer la rutilisabilit des objets alors que FACTORY METHOD vite que les clients aient savoir
quelle classe instancier.
Solution 16.4

La Figure B.18 montre que les deux classes de vrification de limite de crdit
implmentent linterface CreditCheck. La classe de factory fournit une mthode
qui retourne un objet CreditCheck. Le client qui appelle createCreditCheck()
ne sait pas prcisment quelle est la classe des objets quil obtient.
La mthode createCreditCheck() est statique. Aussi les clients nont-ils pas
besoin dinstancier la classe CreditCheckFactory pour obtenir un objet CreditCheck. Vous pouvez dfinir cette classe abstraite ou prvoir un constructeur priv si
vous voulez empcher activement dautres dveloppeurs de linstancier.

pattern Livre Page 377 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

377

Figure B.18
Deux classes implmentent linterface
CreditCheck. Le choix
de la classe instancier
dpend du fournisseur
de services plutt que
du client concern par
la vrification de crdit.

CreditCheckFactory

createCreditCheck():CreditCheck

interface
CreditCheck

creditLimit(id:int):double

CreditCheckOnline

creditLimit(id:int):double

CreditCheckOffline

creditLimit(id:int):double

Solution 16.5

En admettant que la mthode isAgencyUp() reflte prcisment la ralit, le code


pour createCreditCheck() est simple :
public static CreditCheck createCreditCheck() {
if (isAgencyUp()) return new CreditCheckOnline();
return new CreditCheckOffline();
}

pattern Livre Page 378 Vendredi, 9. octobre 2009 10:31 10

378

Partie VI

Annexes

Solution 16.6

La Figure B.19 illustre un diagramme acceptable pour la hirarchie parallle


Machine/MachinePlanner.
Figure B.19
La logique de planification se trouve
maintenant dans une
hirarchie distincte.
Chaque sous-classe
de Machine sait
quel planificateur
instancier en rponse
un appel de
createPlanner().

Machine
MachinePlanner
createPlanner():
MachinePlanner

#machine:Machine
MachinePlanner(
m:Machine)
getAvailable():Date

Fuser

Mixer

BasicPlanner

ShellAssembler

ShellPlanner

StarPress

StarPressPlanner

Ce diagramme montre que les sous-classes de MachinePlanner doivent implmenter la mthode getAvailable(). Il indique aussi que les classes de la hirarchie
MachinePlanner acceptent un objet Machine dans leur constructeur. Cela permet
au planificateur dinterroger lobjet bnficiaire de la planification, relativement
des critres tels que lemplacement de la machine et la quantit de produits quelle
traite actuellement.
Solution 16.7

Une mthode createPlanner() pour la classe Machine pourrait ressembler


lexemple suivant :
public MachinePlanner createPlanner() {
return new BasicPlanner(this);
}

pattern Livre Page 379 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

379

Les classes Fuser et Mixer peuvent compter sur le fait dhriter cette mthode,
alors que les classes ShellAssembler et StarPress devront la redfinir. Pour la
classe StarPress, la mthode createPlanner() pourrait se prsenter comme
suit :
public MachinePlanner createPlanner() {
return new StarPressPlanner(this);
}

Ces mthodes illustrent le pattern FACTORY METHOD. Lorsque nous avons besoin
dun objet de planification, nous appelons la mthode createPlanner() de la
machine concerne par la planification. Le planificateur spcifique que nous obtenons
dpend de la machine.

ABSTRACT FACTORY
Solution 17.1

Voici une solution possible :


public class BetaUI extends UI {
public BetaUI () {
Font oldFont = getFont();
font = new Font(
oldFont.getName(),
oldFont.getStyle() | Font.ITALIC,
oldFont.getSize());
}
public JButton createButtonOk() {
JButton b = super.createButtonOk();
b.setIcon(getIcon("images/cherry-large.gif"));
return b;
}
public JButton createButtonCancel() {
JButton b = super.createButtonCancel();
b.setIcon(getIcon("images/cherry-large-down.gif"));
return b;
}
}

Ce code adopte comme approche dutiliser autant que possible les mthodes de la
classe de base.

pattern Livre Page 380 Vendredi, 9. octobre 2009 10:31 10

380

Partie VI

Annexes

Solution 17.2

Une solution apportant une conception plus robuste serait de spcifier les mthodes
de cration attendues et les proprits standard de GUI dans une interface, comme
illustr Figure B.20.
Figure B.20
Cette conception
de classes de factory
abstraites pour les
composants de GUI
rduit la dpendance
des sous-classes vis--vis
des modificateurs des
mthodes de la classe UI.

UI

interface

NORMAL:UI
createButton():Button
createList(
:Object[]):JList

BetaUI

createPaddedPanel(
c:Component):JPanel
getFont():Font

Solution 17.3

La Figure B.21 prsente une solution possible pour fournir dans Credit.Canada des
classes concrtes qui implmentent les interfaces et les classes abstraites de Credit.
Figure B.21
Le package

com.oozinoz.credit.ca fournit
une famille de
classes concrtes
qui oprent une
varit de vrifications lors dun
appel provenant
du Canada.

com.oozinoz.credit.ca

CheckFactoryCanada

BillingCheckCanada

com.oozinoz.credit

CreditCheckFactory

interface
BillingCheck

ShippingCheckCanada

interface
ShippingCheck

CreditCheckCanadaOnline

interface
CreditCheck

CreditCheckOffline

pattern Livre Page 381 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

381

Une subtilit est que vous navez besoin que dune classe concrte pour une vrification de crdit hors ligne car, chez Oozinoz, ce contrle est le mme quelle que
soit la provenance des appels.
Solution 17.4

Voici une solution possible :


package com.oozinoz.credit.ca;
import com.oozinoz.check.*;
public class CheckFactoryCanada extends CheckFactory {
public BillingCheck createBillingCheck() {
return new BillingCheckCanada();
}
public CreditCheck createCreditCheck() {
if (isAgencyUp())
return new CreditCheckCanadaOnline();
return new CreditCheckOffline();
}
public ShippingCheck createShippingCheck() {
return new ShippingCheckCanada();
}
}

Votre solution devrait :


m

m
m

implmenter les mthodes create pour les mthodes hrites de la classe


abstraite CreditCheckFactory;
avoir linterface approprie pour le type de retour de chaque mthode create;
retourner un objet CreditCheckOffline si lorganisme de crdit nest pas
disponible.

Solution 17.5

Voici une justification possible. Le placement de classes spcifiques un pays dans


des packages distincts permet aux dveloppeurs de la socit Oozinoz dorganiser
les applications et le travail de dveloppement. De plus, les packages propres
chaque pays sont indpendants. Nous pouvons tre srs que, par exemple, les classes propres aux Etats-Unis nont aucun impact sur les classes spcifiques au
Canada. Nous pouvons aussi facilement ajouter des fonctionnalits pour de
nouveaux pays. Par exemple, si nous commenons travailler avec Mexico, nous
pouvons crer un nouveau package qui fournira les services de contrle dont
nous avons besoin dune manire cohrente pour ce pays.

pattern Livre Page 382 Vendredi, 9. octobre 2009 10:31 10

382

Partie VI

Annexes

Cela apporte lavantage supplmentaire de nous permettre dassigner le package


credit.mx un dveloppeur possdant une exprience de travail avec des services
et des donnes du Mexique.
Voici un argument contre. Bien que cette sparation soit valable en thorie, elle est
excessive dans la pratique. Je prfrerais avoir un package avec toutes les classes,
du moins jusqu ce nous ayons neuf ou dix pays grer. La rpartition de ces
classes dans plusieurs packages multiplie par trois, si ce nest plus, le travail de
gestion de la configuration lorsque je dois implmenter un changement qui couvre
tous ces packages.

PROTOTYPE
Solution 18.1

Voici quelques avantages de cette conception :


m

Nous pouvons crer de nouveaux objets factory sans avoir crer une nouvelle
classe. Nous pourrions mme crer un nouveau kit de GUI lors de lexcution.
Nous pouvons produire un nouveau factory par copie en apportant de lgres
modifications. Par exemple, nous pouvons faire en sorte que le kit de GUI dune
version soit identique au kit normal, except pour la police. Lapproche avec
PROTOTYPE permet aux boutons et autres composants dun nouveau factory
dhriter de valeurs, telles que des couleurs, dun prcdent factory.

Voici quelques inconvnients :


m

Lapproche avec PROTOTYPE permet de changer des valeurs, par exemple pour
les couleurs et les polices, pour chaque factory, mais elle ne permet pas de
produire de nouveaux kits ayant dautres comportements.
La raison de stopper la prolifration des classes de kit de GUI nest pas claire.
En quoi est-elle un problme ? Nous devons placer le code dinitialisation du kit
quelque part, probablement dans des mthodes statiques de la classe UIKit
propose. Cette approche ne diminue pas rellement la quantit de code grer.

Quelle est la rponse correcte ? Dans une telle situation, il peut tre utile dexprimenter. Ecrivez du code qui suive les deux conceptions et valuez leur comportement dans la pratique. Il y aura des situations o les membres dune quipe seront
en dsaccord quant la direction suivre. Cest une bonne chose : cest le signe
que vous mettez le doigt sur certains problmes et que vous discutez conception.

pattern Livre Page 383 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

383

Si vous ntes jamais en dsaccord, il est probable que vous ne soyez pas en train
dlaborer la meilleure conception pour les cas o vous ntes pas daccord,
mme aprs une analyse mrement rflchie, vous pouvez recourir un architecte,
un chef concepteur ou une tierce partie neutre pour prendre une dcision.
Solution 18.2

Une faon de rsumer la fonction de clone() est "nouvel objet, mmes champs".
La mthode clone() cre un nouvel objet en utilisant la mme classe et les mmes
types dattributs que pour lobjet original. Le nouvel objet reoit aussi les mmes
valeurs de champs que loriginal. Si ces champs sont des types de base, tels que des
entiers, les valeurs sont copies. Si toutefois ce sont des rfrences, ce sont les rfrences qui sont copies.
Lobjet que la mthode clone() cre est une copie partielle (shallow copy), elle
partage avec loriginal tout objet subordonn. Une copie complte (deep copy)
inclurait des copies entires de tous les attributs de lobjet parent. Par exemple, si
vous clonez un objet A qui pointe vers un objet B, une copie partielle crera un
nouveau A qui pointe vers lobjet B original ; une copie complte crera un
nouvel A pointant vers un nouveau B. Si vous voulez une copie complte, vous
devrez implmenter votre propre mthode qui effectuera ce que vous souhaitez.
Notez que, pour utiliser clone(), vous devez dclarer votre classe comme implmentant Cloneable. Cette interface de marquage ne possde aucune mthode mais
sert dindicateur pour signaler que vous supportez clone() de manire intentionnelle.
Solution 18.3

Le code suggr ne gardera que trois objets, comme le montre la Figure B.22.
Figure B.22
Une conception
insuffisante pour
le clonage peut crer
une copie incomplte
qui partage certains
objets avec son prototype.

m1:Machine

m2:Machine

:Location

pattern Livre Page 384 Vendredi, 9. octobre 2009 10:31 10

384

Partie VI

Annexes

La version actuelle de la mthode clone() pour MachineSimulator appelle


super.clone(), que la classe Object implmente. Cette mthode cre un nouvel
objet avec les mmes champs. Des types primitifs, tels que les champs dinstance
int dans MachineSimulator, sont copis. De plus, les rfrences dobjets, telles
que le champ Location dans MachineSimulator, sont copies. Notez que cest
la rfrence qui est copie, pas lobjet. Cela signifie que Object.clone() cre la
situation illustre Figure B.22.
Supposez que vous changiez la trave et les coordonnes demplacement de la
deuxime machine. Etant donn quil ny a quun objet Location, cette modification
change lemplacement des deux machines simules !
Solution 18.4

Voici une solution possible :


public OzPanel copy2() {
OzPanel result = new OzPanel();
result.setBackground(this.getBackground());
result.setForeground(this.getForeground());
result.setFont(this.getFont());
return result;
}

Les deux mthodes copy() et copy2() exonrent les clients de OzPanel davoir
invoquer un constructeur et supporter le concept de PROTOTYPE. Toutefois,
lapproche manuelle de copy2() offre peut-tre une meilleure scurit. Elle sappuie
sur le fait de savoir quels sont les attributs importants copier, mais vite davoir
copier des attributs dont vous ignorez tout.

MEMENTO
Solution 19.1

Voici une implmentation possible de undo() pour FactoryModel:


public boolean canUndo() {
return mementos.size() > 1;
}
public void undo() {
if (!canUndo()) return;
mementos.pop();
notifyListeners();
}

pattern Livre Page 385 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

385

Ce code veille ignorer les requtes undo() si la pile se retrouve dans son tat
initial avec un seul mmento. Le sommet de la pile est toujours ltat courant, aussi
le code undo() doit-il seulement effectuer un pop() pour exposer le mmento
prcdent.
Lorsque vous crivez une mthode createMemento(), vous devriez faire en sorte
quelle retourne toutes les informations ncessaires pour reconstruire lobjet rcepteur. Dans cet exemple, un simulateur de machine peut se reconstruire partir dun
clone, et un simulateur dusine peut se reconstruire partir dune liste de clones de
simulateurs de machines.
Solution 19.2

Une solution possible est :


public void stateChanged(ChangeEvent e) {
machinePanel().removeAll();
List locations = factoryModel.getLocations();
for (int i = 0; i < locations.size(); i++) {
Point p = (Point) locations.get(i);
machinePanel().add(createPictureBox(p));
}
undoButton().setEnabled(factoryModel.canUndo());
repaint();
}

Chaque fois que ltat change, ce code reconstruit entirement la liste de machines
dans machinePanel().
Solution 19.3

Stocker un mmento en tant quobjet suppose que lapplication soit toujours en


train de sexcuter lorsque lutilisateur souhaite restaurer lobjet original. Voici les
raisons qui pourraient vous inciter stocker un mmento sous forme persistante :
m
m

pouvoir restaurer ltat dun objet si le systme plante ;


permettre lutilisateur de quitter le systme et de poursuivre son travail ultrieurement ;
pouvoir reconstruire un objet sur un autre ordinateur.

pattern Livre Page 386 Vendredi, 9. octobre 2009 10:31 10

386

Partie VI

Annexes

Solution 19.4

Une solution possible est :


public void restore(Component source) throws Exception {
JFileChooser dialog = new JFileChooser();
dialog.showOpenDialog(source);
if (dialog.getSelectedFile() == null)
return;
FileInputStream out = null;
ObjectInputStream s = null;
try {
out = new FileInputStream(dialog.getSelectedFile());
s = new ObjectInputStream(out);
ArrayList list = (ArrayList) s.readObject();
factoryModel.setLocations(list);
} finally {
if (s != null)
s.close();
}
}

Ce code est presque identique la mthode save(), sauf que la mthode


restore() doit demander au modle dusine de lui transmettre par pouss (push) la
liste de machines rcupre.
Solution 19.5

Encapsuler limite laccs ltat et aux oprations dun objet. Le fait denregistrer
un objet, tel quune collection demplacements, sous forme textuelle expose les
donnes de lobjet et permet nimporte qui disposant dune diteur de texte de
changer ltat de lobjet. Par consquent, enregistrer un objet au format XML viole
la rgle dencapsulation, dans une certaine mesure en tout cas.
Selon votre application, une violation de la rgle dencapsulation par un stockage
persistant peut poser un problme dans la pratique. Pour y remdier, vous pourriez
limiter laccs aux donnes, ce qui est courant dans une base de donnes relationnelle. Vous pourriez sinon chiffrer les donnes, ce qui est courant lors de la transmission de texte HTML sensible. Limportant ici nest pas de savoir si les concepts
dencapsulation et de mmento sappliquent une conception mais plutt de garantir lintgrit des donnes tout en supportant le stockage et la transmission de
donnes.

pattern Livre Page 387 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

387

Introduction aux oprations


Solution 20.1

Le pattern CHAIN OF RESPONSIBILITY distribue une opration travers une chane


dobjets. Chaque mthode implmente le service de lopration directement ou
transmet les appels lobjet suivant dans la chane.
Solution 20.2

Voici une liste complte des modificateurs de mthodes Java avec une dfinition
informelle de chacun :
m
m

public : accs permis tous les clients.


protected : accs permis au sein du package de dclaration et aux sous-classes
de la classe.

private : accs permis uniquement au sein de la classe.

abstract : pas dimplmentation fournie.

static : associe la classe dans son ensemble, pas des objets individuels.

final : ne peut tre remplace.

m
m

synchronized : la mthode acquiert un accs au moniteur de lobjet ou de la


classe, si elle est statique.
native : implmente ailleurs dans du code dpendant dune plate-forme.
strictfp : les expressions double et float values strictement daprs les rgles
FP, ncessitant que les rsultats intermdiaires soient valides selon les standards
IEEE.

Bien que certains dveloppeurs puissent tre tents dutiliser tous ces modificateurs
dans une mme dfinition de mthode, plusieurs rgles limitent les possibilits de
combinaison.

pattern Livre Page 388 Vendredi, 9. octobre 2009 10:31 10

388

Partie VI

Annexes

Solution 20.3

Cela dpend.
Avec les versions antrieures Java 5, si vous changez la valeur de retour de
Bitmap.clone(), le code ne sera pas compil. La signature de clone() correspond
la signature de Object.clone(), et les types de retour doivent donc galement
correspondre.
Depuis Java 5, la dfinition du langage a chang pour supporter des types de
retour covariants, permettant une sous-classe de dclarer un type de retour
plus spcifique.
Solution 20.4

Voici un argument pour le fait domettre les dclarations dexceptions dans les enttes de mthodes. Tout dabord, il convient de noter que Java nimpose pas que les
mthodes dclarent toutes les exceptions qui pourraient tre gnres. Nimporte
quelle mthode pourrait, par exemple, rencontrer un pointeur null et gnrer une
exception non dclare. Il ne serait donc pas pratique dobliger les programmeurs
dclarer toutes les exceptions possibles. Les applications requirent une stratgie
pour pouvoir grer toutes les exceptions, laquelle ne peut tre remplace en demandant
aux dveloppeurs de dclarer certains types dexceptions.
Un argument contre est que les programmeurs ont besoin daide. Il est vrai que
larchitecture dune application doit disposer dune stratgie efficace de gestion des
exceptions. De toute vidence, il ne serait pas commode de forcer les dveloppeurs
dclarer dans chaque mthode lventualit dun problme courant, tel que des
pointeurs null. Mais, pour certaines erreurs, telles quun problme douverture de
fichier, demander linvocateur dune mthode de grer les exceptions possibles
pourrait tre utile. C# rsout ce dilemme en liminant toute dclaration dexception
de len-tte des mthodes.
Solution 20.5

La figure illustre un algorithme la procdure dterminant si un modle objet est


un arbre , deux oprations apparaissant en tant que signatures dans la classe
MachineComponent, et quatre mthodes.

pattern Livre Page 389 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

389

TEMPLATE METHOD
Solution 21.1

Votre programme complt devrait ressembler ce qui suit :


package app.templateMethod;
import java.util.Comparator;
import com.oozinoz.firework.Rocket;
public class ApogeeComparator implements Comparator {
public int compare(Object o1, Object o2) {
Rocket r1 = (Rocket) o1;
Rocket r2 = (Rocket) o2;
return Double.compare(r1.getApogee(), r2.getApogee());
}
}

et
package app.templateMethod;
import java.util.Comparator;
import com.oozinoz.firework.Rocket;
public class NameComparator implements Comparator {
public int compare(Object o1, Object o2) {
Rocket r1 = (Rocket) o1;
Rocket r2 = (Rocket) o2;
return r1.toString().compareTo(r2.toString());
}
}

Solution 21.2

Le code de markMoldIncomplete() passe des informations relatives au moule


incomplet au gestionnaire du matriel. Voici une solution possible :
package com.oozinoz.ozAster;
import aster.*;
import com.oozinoz.businessCore.*;
public class OzAsterStarPress extends AsterStarPress {
public MaterialManager getManager() {
return MaterialManager.getManager();
}
public void markMoldIncomplete(int id) {
getManager().setMoldIncomplete(id);
}
}

pattern Livre Page 390 Vendredi, 9. octobre 2009 10:31 10

390

Partie VI

Annexes

Solution 21.3

Vous avez besoin ici dun hook. Vous pourriez formuler votre demande comme
ceci : "Vous serait-il possible dinsrer un appel dans votre mthode shutDown(),
aprs le dchargement de la pte et avant le rinage de la machine ? Si vous le nommiez
quelque chose comme collectPaste(), je pourrais lutiliser pour rcuprer la
pte que nous rutilisons chez Oozinoz."
Les dveloppeurs de chez Aster discuteraient probablement avec vous du nom
donner la mthode. Lintrt de demander un hook dans un TEMPLATE METHOD est
que cela permet votre code dtre beaucoup plus robuste que si vous deviez vous
accommoder dun code existant inadquat.
Solution 21.4

La mthode getPlanner() de la classe Machine devrait tirer parti de la mthode


abstraite createPlanner():
public MachinePlanner getPlanner() {
if (planner == null)
planner = createPlanner();
return planner;
}

Ce code implique que vous ajoutiez un champ planner la classe Machine. Aprs
avoir ajout cet attribut et la mthode getPlanner(), vous pouvez les supprimer
dans les sous-classes.
Cette refactorisation cre un TEMPLATE METHOD. La mthode getPlanner()
procde une initialisation paresseuse de la variable planner, sappuyant uniquement
sur ltape createPlanner() fournie par les sous-classes.

STATE
Solution 22.1

Comme le montre la machine tats, lorsque la porte est ouverte, le fait de toucher
le bouton la place dans ltat StayOpen, et le fait de toucher le bouton une seconde
fois la fait se fermer.
Solution 22.2

Votre code devrait ressembler ce qui suit :


public void complete() {
if (state == OPENING)

pattern Livre Page 391 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

setState(OPEN);
else if (state == CLOSING)
setState(CLOSED);
}
public void timeout() {
setState(CLOSING);
}

Solution 22.3

Votre code devrait ressembler ce qui suit :


package com.oozinoz.carousel;
public class DoorClosing extends DoorState {
public DoorClosing(Door2 door) {
super(door);
}
public void touch() {
door.setState(door.OPENING);
}
public void complete() {
door.setState(door.CLOSED);
}
}

Solution 22.4

La Figure B.23 illustre le diagramme complt.


Figure B.23
Cette conception
rend les objets
DoorState constants.
Les mthodes de
transition dtat de
DoorState actualisent
ltat dun objet Door
quelles reoivent
comme paramtre.

Door

DoorState
CLOSED:DoorClosed

complete()

OPENING:DoorOpening

setState(
state:DoorState)

OPEN:DoorOpen

timeout()
touch()

STAYOPEN:DoorStayOpen
CLOSING:DoorClosing

status()
complete(d:Door)
timeout(d:Door)
touch(d:Door)
status()

391

pattern Livre Page 392 Vendredi, 9. octobre 2009 10:31 10

392

Partie VI

Annexes

STRATEGY
Solution 23.1

La Figure B.24 prsente une solution.


Customer

interface
Advisor

BIG_SPENDER_DOLLARS:int
getAdvisor():Advisor

recommend(c:Customer):Firework

isRegistered():boolean
isBigSpender():boolean
getRecommended():Firework

GroupAdvisor

spendingSince(d:Date):
double
ItemAdvisor

PromotionAdvisor

RandomAdvisor

Figure B.24
La politique publicitaire dOozinoz inclut quatre stratgies qui apparaissent sous la forme
de quatre implmentations de linterface Advisor.

Solution 23.2

Les classes GroupAdvisor et ItemAdvisor sont des instances de ADAPTER, fournissant linterface quun client attend en utilisant les services dune classe dont linterface est diffrente.
Solution 23.3

Votre code pourrait ressembler ceci :


public Firework getRecommended() {
return getAdvisor().recommend(this);
}

Une fois que lattribut advisor est connu, le polymorphisme fait le reste.

pattern Livre Page 393 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

393

Solution 23.4

Une routine de tri rutilisable est-elle un exemple de TEMPLATE METHOD ou de


STRATEGY?
Daprs Design Patterns, TEMPLATE METHOD laisse les sous-classes redfinir certaines tapes dun algorithme. Mais la mthode Collections.sort() ne sappuie
pas sur les sous-classes. Elle utilise une instance de Comparator. Chaque instance
de Comparator fournit une nouvelle mthode et donc un nouvel algorithme et une
nouvelle stratgie. Cette mthode est donc un bon exemple de STRATEGY.
Il existe de nombreux algorithmes de tri, mais Collections. sort() en utilise un
seul, le tri rapide (quick sort). Changer dalgorithme signifierait utiliser la place le
tri par tas (heap sort) ou le tri bulles (bubble sort). Lobjectif de STRATEGY est de
vous permettre dutiliser diffrents algorithmes, ce qui ne se produit pas ici.
Lobjectif de TEMPLATE METHOD est de vous permettre dinsrer une tape dans un
algorithme, ce qui correspond prcisment au fonctionnement de la mthode
sort().

COMMAND
Solution 24.1

Nombre dapplications Java Swing appliquent le pattern MEDIATOR enregistrant un


seul objet pour recevoir tous les vnements GUI. Cet objet sert de mdiateur aux
composants qui interagissent et traduit lentre utilisateur en commandes dobjets
du domaine.
Solution 24.2

Voici quoi pourrait ressembler votre code :


package com.oozinoz.visualization;
import java.awt.event.*;
import javax.swing.*;
import com.oozinoz.ui.*;
public class Visualization2 extends Visualization {
public static void main(String[] args) {
Visualization2 panel = new Visualization2(UI.NORMAL);
JFrame frame = SwingFacade.launch(
panel,
"Operational Model");
frame.setJMenuBar(panel.menus());
frame.setVisible(true);
}

pattern Livre Page 394 Vendredi, 9. octobre 2009 10:31 10

394

Partie VI

Annexes

public Visualization2(UI ui) {


super(ui);
}
public JMenuBar menus() {
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("File");
menuBar.add(menu);
JMenuItem menuItem = new JMenuItem("Save As...");
menuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
save();
}
});
menu.add(menuItem);
menuItem = new JMenuItem("Restore From...");
menuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
restore();
}
});
menu.add(menuItem);
return menuBar;
}
public void save() {
try {
mediator.save(this);
} catch (Exception ex) {
System.out.println("Echec de sauvegarde : " +
ex.getMessage());
}
}
public void restore() {
try {
mediator.restore(this);
} catch (Exception ex) {
System.out.println("Echec de restauration : " +
ex.getMessage());
}
}
}

Bien que la mthode actionPerformed() ncessite un argument ActionEvent,


vous pouvez lignorer sans problme. La mthode menus() enregistre une seule
instance dune classe anonyme avec loption de menu Save et une seule instance
dune autre classe anonyme avec loption de menu Load. Lorsque ces mthodes
sont appeles, il ny a aucun doute possible quant la source de lvnement.

pattern Livre Page 395 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

395

Solution 24.3

La mthode testSleep() passe la commande doze la mthode time():


package app.command;
import com.oozinoz.robotInterpreter.Command;
import com.oozinoz.utility.CommandTimer;
import junit.framework.TestCase;
public class TestCommandTimer extends TestCase {
public void testSleep() {
Command doze = new Command() {
public void execute() {
try {
Thread.sleep(
2000 + Math.round(10 * Math.random()));
} catch (InterruptedException ignored) {
}
}
};
long actual = CommandTimer.time(doze);
long expected = 2000;
long delta = 5;
assertTrue(
"Devrait tre " + expected + " +/- " + delta + " ms",
expected - delta <= actual
&& actual <= expected + delta);
}
}

Solution 24.4

Votre code pourrait ressembler ceci :


public void shutDown() {
if (inProcess()) {
stopProcessing();
moldIncompleteHook.execute(this);
}
usherInputMolds();
dischargePaste();
flush();
}

Notez que ce code ne soccupe pas de vrifier si moldIncompleteHook est null,


puisque cet argument est toujours dfini avec un vritable objet Hook (initialement,
il est dfini avec un objet NullHook qui ne fait rien, mais un utilisateur peut installer
un hook diffrent).

pattern Livre Page 396 Vendredi, 9. octobre 2009 10:31 10

396

Partie VI

Annexes

Vous pourriez lutiliser comme suit :


package app.templateMethod;
import com.oozinoz.businessCore.*;
import aster2.*;
public class ShowHook {
public static void main(String[] args) {
AsterStarPress p = new AsterStarPress();
Hook h = new Hook() {
public void execute(AsterStarPress p) {
MaterialManager m = MaterialManager.getManager();
m.setMoldIncomplete(p.getCurrentMoldID());
}
};
p.setMoldIncompleteHook(h);
}
}

Solution 24.5

Dans FACTORY METHOD, un client sait quand crer un nouvel objet mais ne sait pas
quel type dobjet. Ce pattern place la cration dun objet dans une mthode permettant au client de ne pas connatre la classe instancier. Ce principe est galement
prsent dans ABSTRACT FACTORY.
Solution 24.6

Lobjectif du pattern MEMENTO est dassurer le stockage et la restauration de ltat


dun objet. Typiquement, vous pouvez ajouter un nouveau mmento une pile pour
chaque excution dune commande, puis les rcuprer et les appliquer chaque fois
quun utilisateur a besoin de dfaire des commandes.

INTERPRETER
Solution 25.1

La mthode execute() de la classe ForCommand devrait ressembler au code


suivant :
private void execute(MachineComponent mc) {
if (mc instanceof Machine) {
Machine m = (Machine) mc;
variable.assign(new Constant(m));
body.execute();
return;
}

pattern Livre Page 397 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

397

MachineComposite comp = (MachineComposite) mc;


List children = comp.getComponents();
for (int i = 0; i < children.size(); i++) {
MachineComponent child =
(MachineComponent) children.get(i);
execute(child);
}
}

Le code de execute() parcourt un composite de machines. Lorsquil rencontre un


nud feuille une machine , il assigne la variable la machine et excute la
commande body de lobjet ForMachine.
Solution 25.2

Une solution possible est :


public void execute() {
if (term.eval() != null)
body.execute();
else
elseBody.execute();
}

Solution 25.3

Voici une manire dcrire la classe WhileCommand.java:


package com.oozinoz.robotInterpreter2;
public class WhileCommand extends Command {
protected Term term;
protected Command body;
public WhileCommand(Term term, Command body) {
this.term = term;
this.body = body;
}
public void execute() {
while (term.eval() != null)
body.execute();
}
}

Solution 25.4

Voici un exemple de rponse. Lobjectif du pattern INTERPRETER est de vous


permettre de composer des objets excutables partir dune hirarchie de classes
fournissant diffrentes interprtations dune opration commune. Lobjectif du
pattern COMMAND est simplement dencapsuler une requte dans un objet.

pattern Livre Page 398 Vendredi, 9. octobre 2009 10:31 10

398

Partie VI

Annexes

Un objet interprteur peut-il fonctionner comme une commande ? Bien entendu !


Le choix du pattern utiliser dpend de votre but. Etes-vous en train de crer un kit
doutils pour composer des objets excutables ou dencapsuler une requte dans un
objet ?

Introduction aux extensions


Solution 26.1

En mathmatique, un cercle est un cas spcial dellipse. Mais en programmation


OO, une ellipse ne possde pas le mme comportement quun cercle. Par exemple,
sa largeur peut faire le double de sa hauteur, ce qui est impossible pour un cercle. Si
ce comportement est important pour votre programme, un objet Circle ne fonctionnera pas comme un objet Ellipse et constituera donc une violation de LSP.
Notez quil peut en aller autrement pour les objets immuables. Cest simplement
un domaine dans lequel les mathmatiques naves saccordent mal avec la smantique
des hirarchies de types standard.
Solution 26.2

Lexpression tub.getLocation().isUp() peut donner lieu des erreurs de


programmation si certaines subtilits entourent la valeur de la proprit Location
dun objet Tub. Par exemple, cette proprit pourrait tre null ou tre un objet
Robot si le bac est en transit. Dans le premier cas, lvaluation de tub.getLocation().isUp() gnre une exception ; dans le second, le problme peut mme tre
pire puisque nous tentons dutiliser un robot pour rcuprer un bac partir de luimme. Ces problmes potentiels sont grables, mais voulons-nous quun tel code
de gestion soit plac dans la mthode contenant lexpression tub.getLocation().isUp()? Non. Le code ncessaire se trouve peut-tre dj dans la classe
Tub. Si ce nest pas le cas, il devrait en tout cas sy trouver pour nous viter davoir
coder les mmes subtilits dans dautres mthodes qui interagissent avec les bacs.
Solution 26.3

Voici un exemple :
public static String getZip(String address) {
return address.substring(address.length() - 5);
}

pattern Livre Page 399 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

399

Cette mthode comporte quelques indicateurs, dont un indicateur obsession primitive (utilisation dune chane pour contenir plusieurs attributs).
Solution 26.4

Voici un exemple de solution :


Exemple

Pattern luvre

Le concepteur dune simulation pyrotechnique tablit une interface


qui dfinit le comportement que doit prsenter votre objet pour
pouvoir prendre part la simulation

ADAPTER

Un kit doutils permet de composer des objets excutables lors de


lexcution

INTERPRETER

Une super-classe possde une mthode qui dpend de sous-classes


pour fournir une tape manquante

TEMPLATE METHOD

Un objet vous permet dtendre son comportement en acceptant une


mthode encapsule dans un autre objet et en invoquant cette
mthode au moment appropri

COMMAND

Un gnrateur de code insre un comportement qui donne lillusion


quun objet sexcutant sur une autre machine est local

PROXY

Une conception vous permet denregistrer des listeners pour des


rappels qui auront lieu lorsquun objet change

OBSERVER

Une conception vous permet de dfinir des oprations abstraites qui


dpendent dune interface spcifique et dajouter de nouveaux drivers
qui satisfont aux exigences de cette interface

BRIDGE

DECORATOR
Solution 27.1

Voici une solution possible :


package com.oozinoz.filter;
import java.io.*;
public class RandomCaseFilter extends OozinozFilter {
public RandomCaseFilter(Writer out) {
super(out);
}
public void write(int c) throws IOException {

pattern Livre Page 400 Vendredi, 9. octobre 2009 10:31 10

400

Partie VI

Annexes

out.write(Math.random() < .5
? Character.toLowerCase((char) c)
: Character.toUpperCase((char) c));
}
}

Une casse alatoire accroche lattention. Considrez le programme suivant :


package app.decorator;
import java.io.BufferedWriter;
import java.io.IOException;
import com.oozinoz.filter.ConsoleWriter;
import com.oozinoz.filter.RandomCaseFilter;
public class ShowRandom {
public static void main(String[] args)
throws IOException {
BufferedWriter w =
new BufferedWriter(
new RandomCaseFilter(new ConsoleWriter()));
w.write("buy two packs now and get a "
+ "zippie pocket rocket -- free!");
w.newLine();
w.close();
}
}

Ce programme produit quelque chose comme ceci :


bUy tWO pAcks NOw ANd geT A ZiPpIE PoCkEt RocKeT -- frEe!

Solution 27.2

Voici une solution possible :


package com.oozinoz.filter;
import java.io.Writer;
public class ConsoleWriter extends Writer {
public void close() {}
public void flush() {}
public void write(
char[] buffer, int offset, int length) {
for (int i = 0; i < length; i++)
System.out.print(buffer[offset + i]);
}
}

pattern Livre Page 401 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

401

Solution 27.3

Voici une solution possible :


package com.oozinoz.function;
public class Exp extends Function {
public Exp(Function f) {
super(f);
}
public double f(double t) {
return Math.exp(sources[0].f(t));
}
}

Solution 27.4

Voici une solution possible :


package app.decorator.brightness;
import com.oozinoz.function.Function;
public class Brightness extends Function {
public Brightness(Function f) {
super(f);
}
public double f(double t) {
return Math.exp(-4 * sources[0].f(t))
* Math.sin(Math.PI * sources[0].f(t));
}
}

ITERATOR
Solution 28.1

La routine display() lance un nouveau thread qui peut se rveiller nimporte


quel moment, mais lappel sleep() veille ce que run() sexcute pendant que
display() est inactive. Le rsultat indique que lorsquelle sexcute, la
mthode display() garde le contrle pendant une itration, affichant la liste
partir de lindice 0 :
Mixer1201

Ensuite, le second thread devient actif et place Fuser1101 au dbut de la liste, dcalant toutes les autres machines dun indice. En particulier, Mixer1201 passe de
lindice 0 lindice 1.

pattern Livre Page 402 Vendredi, 9. octobre 2009 10:31 10

402

Partie VI

Annexes

Lorsque le thread principal reprend le contrle, display() affiche le reste de la


liste depuis lindice 1 jusqu la fin :
Mixer1201
ShellAssembler1301
StarPress1401
UnloadBuffer1501

Solution 28.2

Un argument contre lutilisation des mthodes synchronized() est quelles


peuvent donner lieu des rsultats errons si litration utilise une boucle for
ou faire planter le programme, moins de prvoir une logique qui intercepte
lexception InvalidOperationException gnre.
Un argument contre lapproche avec verrouillage est que les conceptions qui assurent une itration avec scurit inter-thread sappuient sur la coopration entre les
threads pouvant accder la collection. Les mthodes synchronized() servent
justement dans le cas o les threads ne cooprent pas.
Ni les mthodes synchronized() ni le support du verrouillage intgrs Java ne
peuvent rendre le dveloppement multithread ais et infaillible.
Solution 28.3

Comme dcrit au Chapitre 16, les itrateurs constituent un exemple classique du


pattern FACTORY METHOD. Un client qui souhaite un numrateur pour une instance
de ProcessComponent sait quand crer litrateur, et la classe rceptrice sait quelle
classe instancier.
Solution 28.4

Voici une solution possible :


public Object next() {
if (peek != null) {
Object result = peek;
peek = null;
return result;
}
if (!visited.contains(head)) {
visited.add(head);
if (shouldShowInterior()) return head;
}
return nextDescendant();
}

pattern Livre Page 403 Vendredi, 9. octobre 2009 10:31 10

Annexe B

Solutions

403

VISITOR
Solution 29.1

La diffrence se situe au niveau du type de lobjet this. La mthode accept()


invoque la mthode visit() dun objet MachineVisitor. La mthode accept() de
la classe Machine recherche une mthode visit() possdant la signature
visit(:Machine), tandis que la mthode accept() de la classe MachineComposite recherche une mthode visit() possdant la signature visit(:MachineComposite).
Solution 29.2

Voici une solution possible :


package app.visitor;
import com.oozinoz.machine.MachineComponent;
import com.oozinoz.machine.OozinozFactory;
public class ShowFindVisitor {
public static void main(String[] args) {
MachineComponent factory = OozinozFactory.dublin();
MachineComponent machine = new FindVisitor().find(
factory, 3404);
System.out.println(machine != null ?
machine.toString() : "Introuvable");
}
}

Solution 29.3

Voici une solution possible :


package app.visitor;
import com.oozinoz.machine.*;
import java.util.*;
public class RakeVisitor implements MachineVisitor {
private Set leaves;
public Set getLeaves(MachineComponent mc) {
leaves = new HashSet();
mc.accept(this);
return leaves;
}
public void visit(Machine m) {
leaves.add(m);
}
public void visit(MachineComposite mc) {

pattern Livre Page 404 Vendredi, 9. octobre 2009 10:31 10

404

Partie VI

Annexes

Iterator iter = mc.getComponents().iterator();


while (iter.hasNext())
((MachineComponent) iter.next()).accept(this);
}
}

Solution 29.4

Une solution est dajouter un argument Set toutes les mthodes accept() et
visit() pour passer lensemble des nuds visits. La classe ProcessComponent
devrait alors inclure une mthode accept() concrte appelant sa mthode
accept() abstraite, lui passant un nouvel objet Set:
public void accept(ProcessVisitor v) {
accept(v, new HashSet());
}

La mthode accept() des sous-classes ProcessAlternation, ProcessSequence


et ProcessStep serait :
public void accept(ProcessVisitor v, Set visited) {
v.visit(this, visited);
}

Les dveloppeurs doivent aussi crer des classes avec des mthodes visit() qui
acceptent lensemble visited. Il leur revient galement de remplir lensemble.
Solution 29.5

Voici les alternatives lapplication du pattern VISITOR:


m

Ajoutez le comportement dont vous avez besoin la hirarchie originale. Vous


pouvez le faire si vous communiquez facilement avec ses dveloppeurs ou si
vous suivez le principe de proprit collective du code.
Vous pouvez laisser une classe simplement traverser une structure de machines
ou de processus sur laquelle elle doit oprer. Si vous avez besoin de connatre le
type dun enfant du composite, par exemple, vous pouvez utiliser loprateur
instanceof, ou vous pourriez sinon introduire des fonctions boolennes, telles
que isLeaf() et isComposite().
Si le comportement que vous voulez ajouter est trs diffrent de celui existant,
vous pouvez crer une hirarchie parallle. Par exemple, la classe MachinePlanner du Chapitre 16, consacr FACTORY METHOD, place un comportement
de planification dans une hirarchie spare.

pattern Livre Page 405 Vendredi, 9. octobre 2009 10:31 10

C
Code source dOozinoz
Le premier avantage de comprendre les patterns, ou modles, de conception est
quils vous aident amliorer votre code. Vous obtiendrez un code plus concis, plus
simple, plus lgant et plus puissant, et sa maintenance en sera aussi facilite. Pour
en percevoir les rels bnfices, voyez-les en action dans du code excut. Vous
devez devenir laise dans la construction ou reconstruction dune base de code
avec des patterns. Il peut tre utile de commencer avec des exemples fonctionnels,
et ce livre inclut de nombreux exemples dimplmentation illustrant leur emploi
dans du code Java. La compilation du code source dOozinoz et lexamen des
exemples fournis tout au long du livre pour tayer les concepts dcrits vous aideront
dbuter limplmentation de patterns dans votre propre code.

Obtention et utilisation du code source


Pour obtenir le code source compagnon de ce livre, rendez-vous sur le site
www.oozinoz.com, tlchargez le fichier zip contenant le code source, et dcompressez-en le contenu. Vous pouvez le placer nimporte o sur votre systme de
fichiers. Ce code est gratuit, vous pouvez lutiliser comme bon vous semble avec
pour seule condition de ne pas affirmer en tre lauteur. Dun autre ct, les auteurs
et lditeur de ce livre ne garantissent en aucune faon que ce code sera utile et
adquat pour quelque objectif que ce soit.

pattern Livre Page 406 Vendredi, 9. octobre 2009 10:31 10

406

Partie VI

Annexes

Construction du code dOozinoz


Si vous ne disposez pas dun environnement de dveloppement pour crire des
programmes Java, vous devez en acqurir un et vous familiariser avec pour pouvoir
dvelopper, compiler et excuter vos propres programmes. Vous pouvez acheter un
outil de dveloppement, tel quIdea dIntellij, ou recourir des outils open source,
tels quEclipse.

Test du code avec JUnit


Les bibliothques Oozinoz incluent un package com.oozinoz.testing conu pour
tre utilis avec JUnit, un environnement de test automatis. Vous pouvez tlcharger JUnit partir de http://junit.org. Si lemploi de cet environnement ne vous est
pas familier, la meilleure faon dapprendre vous en servir est de demander de
laide auprs dun ami ou dun collgue. Autrement, vous pouvez vous aider de la
documentation en ligne ou trouver un livre sur le sujet. Le temps dapprentissage
est moins rapide quavec Ant, par exemple, mais ltudier vous apportera des
comptences qui vous seront profitables pour de nombreuses annes.

Localiser les fichiers


Il peut tre difficile de trouver le fichier particulier correspondant un extrait de
code prsent dans le livre. Le moyen le plus simple de localiser une application
donne est souvent de rechercher son nom dans larborescence du rpertoire oozinoz. Lorganisation de cette arborescence devrait vous permettre de facilement
localiser les fichiers voulus. Le Tableau C.1 reprend les sous-rpertoires de oozinoz
avec une description de leur contenu.
Tableau C.1 : Sous-rpertoires du rpertoire oozinoz

Rpertoire

Contenu

app

Sous-rpertoires des "applications" : fichiers Java sous-jacents aux programmes


excutables. Ils sont gnralement organiss par chapitres. Ainsi app.decorator contient du code se rapportant au Chapitre 27, DECORATOR.

aster

Le code source dAster, une socit fictive.

com

Le code source des applications Oozinoz.

images

Des images utilises par diverses applications Oozinoz.

pattern Livre Page 407 Vendredi, 9. octobre 2009 10:31 10

Annexe C

Code source dOozinoz

407

Rsum
Les efforts investis dans lapprentissage des patterns de conception commenceront
porter leurs fruits lorsque vous changerez votre faon dcrire et de refactoriser,
ou restructurer, du code. Vous pourrez mme appliquer directement des patterns
dans votre propre code. Russir faire fonctionner le code dOozinoz sur votre
propre machine sera un exercice utile, comme dapprendre utiliser des outils open
source, tels quAnt et JUnit. Apprendre ces derniers et parvenir faire tourner le
code dOozinoz, ou de nimporte qui dautre, peut reprsenter un certain travail,
mais les bnfices tirs de ces difficults vous seront profitables pendant de longues
annes grce aux nouvelles comptences acquises, lesquelles seront applicables au
fur et mesure de lapparition de nouvelles techniques et technologies.
Bonne chance ! Si vous rencontrez des problmes ou tes bloqu, nhsitez pas
crire lun de nous deux.
Steve Metsker (Steve.Metsker@acm.org)
William Wake (William.Wake@acm.org)

pattern Livre Page 408 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 409 Vendredi, 9. octobre 2009 10:31 10

D
Introduction UML
Cette annexe est une brve introduction au langage de modlisation UML (Unified
Modeling Language), que ce livre utilise. UML propose une convention de notation
permettant dillustrer la conception de systmes orients objet. Ce langage nest pas
excessivement complexe, mais il est facile den sous-estimer la richesse fonctionnelle. Pour une introduction rapide la plupart de ses fonctionnalits, voyez
louvrage UML Distilled [Fowler et Scott 2003]. Pour une analyse plus approfondie, reportez-vous louvrage The Unified Modeling Language User Guide (le
Guide de lutilisateur UML) [Booch, Rumbaugh, et Jacobsen 1999]. Lapprentissage de nomenclatures et notations standard permet de communiquer au niveau
conception et dtre plus efficace.

Classes
La Figure D.1 applique certaines des fonctionnalits dUML pour illustrer des
classes.
Voici quelques directives concernant llaboration de diagrammes de classes :
m

Dessinez une classe en centrant son nom dans un rectangle. La Figure D.1
montre trois classes : Firework, Rocket et simulation.RocketSim.
Le langage nexige pas quun diagramme doive tout montrer propos dun
lment illustr, comme lensemble des mthodes dune classe ou le contenu
complet dun package.

pattern Livre Page 410 Vendredi, 9. octobre 2009 10:31 10

410

Partie VI

Annexes

Signalez un package en alignant son nom sur la gauche dans un rectangle


adjoint un plus grand encadr pouvant illustrer des classes ou dautres types
dlments. Par exemple, les classes dans la Figure D.1 se trouvent dans le
package Fireworks.
Lorsquune classe est reprsente en dehors dun diagramme de package, ajoutez en prfixe lespace de nom (namespace) spar du nom de la classe par un
point. Par exemple, la Figure D.1 montre que la classe RocketSim se trouve dans
le package simulation.
Vous pouvez spcifier les variables dinstance dune classe dans une subdivision
rectangulaire au-dessous du nom de la classe. La classe Firework possde les
variables dinstance name, mass et price. Faites suivre un nom de variable dun
double-point et de son type.
Vous pouvez signifier quune variable dinstance ou une mthode est prive en
faisant prcder son nom dun signe moins (). Un signe (+) indique quelle est
publique, et un signe dise (#) signale quelle est protge.
Vous pouvez spcifier les mthodes dune classe dans un second rectangle audessous du nom de la classe.
Quand une mthode attend des paramtres en entre, vous pouvez les indiquer,
comme le fait la mthode lookup() dans la Figure D.1
Dans les signatures de mthode, les variables sont spcifies par leur nom suivi
du signe double-point et de leur type. Vous pouvez omettre ou abrger le nom si
le type suggre clairement le rle de la variable.
Soulignez le nom dune mthode pour signifier quelle est statique, comme cest
le cas de la mthode lookup() dans la Figure D.1.
Inscrivez des notes dans un encadr comportant un coin rabattu. Le texte
contenu peut comprendre des commentaires, des contraintes ou du code. Reliez
la note aux autres lments du diagramme par un trait en pointill. Les notes
peuvent apparatre dans nimporte quel diagramme UML.

pattern Livre Page 411 Vendredi, 9. octobre 2009 10:31 10

Annexe D

Introduction UML

fireworks

Rocket

Firework
-name:String
-mass:double
-price:Dollars
+Firework(
name:String,
mass:double,
price:Dollars)
+lookup(name:String):Firework
+getName()
+getMass():double
+getPrice()

simulation.RocketSim

public String getName() {


return name;
}

Figure D.1
Le package Fireworks inclut les classes Firework et Rocket.

411

pattern Livre Page 412 Vendredi, 9. octobre 2009 10:31 10

412

Partie VI

Annexes

Relations entre classes


La Figure D.2 illustre quelques-unes des fonctionnalits dUML servant modliser les relations inter-classes.
Voici quelques directives de notation pour les relations de classes :
m

Spcifiez un nom de classe ou un nom de mthode en italiques pour signifier que


la classe ou la mthode est abstraite. Soulignez le nom pour indiquer quelle est
statique.
Pour indiquer une relation sous-classe/super-classe, utilisez une flche tte
creuse pointant vers la super-classe.
Utilisez une ligne entre deux classes pour indiquer que leurs instances partagent
une quelconque relation. Trs frquemment, une telle ligne signifie quune
classe possde une variable dinstance se rfrant une autre classe. Par exemple, la classe Machine prvoit une variable dinstance pour conserver une rfrence un objet TubMediator.
Utilisez un losange pour indiquer quune instance dune classe contient une
collection dinstances dune autre classe.
Une flche tte ouverte indique la navigabilit. Elle permet de signaler quune
classe possde une rfrence une autre classe, la classe pointe par la flche,
mais que celle-ci ne possde pas de rfrence rciproque.
Lindicateur de pluralit, tel que 0..1, indique le nombre de relations pouvant
apparatre entre des objets. Utilisez le symbole astrisque (*) pour signaler que
zro ou plusieurs instances dune classe peuvent tre relies dautres instances
dune classe associe.
Lorsquune mthode peut gnrer une exception, vous pouvez lindiquer
laide dune flche en pointill allant de la mthode vers la classe dexception.
Etiquetez la flche avec lexpression throw.
Utilisez une flche en pointill entre deux classes pour indiquer une dpendance
nemployant pas de rfrence dobjet. Par exemple, la classe Customer sappuie
sur une mthode statique du moteur de recommandation LikeMyStuff.

pattern Livre Page 413 Vendredi, 9. octobre 2009 10:31 10

Annexe D

Introduction UML

413

MachineComponent

getMachineCount()

Machine

getMachineCount()

MachineComposite

getMachineCount()

0..1
TubMediator

set(:Tub,:Machine)

Customer

getRecommended():
Firework

throws
ArgumentNullException

LikeMyStuff

suggest(
c:Customer):Object

Figure D.2
Un objet MachineComposite contient des objets Machine ou dautres objets composites. La classe
Customer dpend de la classe LikeMyStuff.

pattern Livre Page 414 Vendredi, 9. octobre 2009 10:31 10

414

Partie VI

Annexes

Interfaces
La Figure D.3 illustre les fonctionnalits de base servant la modlisation des
interfaces.

MachineController

interface
MachineDriver

driver:MachineDriver
startMachine()
Cloneable

stopMachine()

StarPressDriver
HskMachineManager2

setTimeout(:double)

ShellDriver

Figure D.3
Vous pouvez signaler une interface au moyen dune tiquette "interface" ou dun symbole ressemblant une sucette.

Voici quelques directives de reprsentation :


m

Vous pouvez signaler une interface en plaant dans un rectangle le texte "interface" et son nom, comme le montre la Figure D.3. Une flche en pointill tte
creuse permet dindiquer quune classe implmente linterface.
Vous pouvez aussi signifier quune classe implmente une interface en utilisant une
ligne surmonte dun cercle (ressemblant une sucette) avec le nom de linterface.
Les interfaces et leurs mthodes sont toujours abstraites en Java. Curieusement,
elles napparaissent pas en italiques comme cest le cas des classes abstraites et
des mthodes abstraites spcifies dans des classes.

Objets
La Figure D.4 illustre un exemple de diagramme dobjets servant reprsenter des
instances spcifiques de classes.

pattern Livre Page 415 Vendredi, 9. octobre 2009 10:31 10

Annexe D

Introduction UML

a:Assay

b:Batch

415

c:Chemical

:Rocket

ShowClient

create
:Rocket
thrust
thrust

Figure D.4
Les reprsentations dobjets mentionnent leur nom et/ou leur type. Un diagramme de squence
signale une succession dappels de mthodes.

Voici quelques directives de modlisation des objets :


m

Spcifiez un objet en indiquant son nom et son type spars par un signe doublepoint. Vous pouvez optionnellement nindiquer que le nom ou que le signe
double-point suivi du type. Dans tous les cas, soulignez le nom et/ou le type.
Utilisez une ligne pour indiquer quun objet possde une rfrence un autre
objet. Vous pouvez utiliser une flche tte ouverte pour signaler la direction de
la rfrence.
Vous pouvez illustrer une squence dobjets envoyant des messages, comme
illustr dans la partie infrieure de la Figure D.4. Lordre de lecture des messages est du haut vers le bas, et les lignes en pointill indiquent lexistence de
lobjet dans le temps.
Utilisez ltiquette "create" pour montrer quun objet en cre un autre. La
Figure D.4 illustre la classe ShowClient crant un objet local Rocket.

pattern Livre Page 416 Vendredi, 9. octobre 2009 10:31 10

416

Partie VI

Annexes

Encadrez un objet par une bordure paisse pour signaler quil est actif dans un
autre thread, processus ou ordinateur. La Figure D.4 montre un objet local
Rocket transmettant une requte relative sa mthode thrust(); un objet
Rocket sexcutant sur un serveur.

Etats
La Figure D.5 illustre un diagramme dtats.

click

Closed

complete

click

Opening

Closing
click

complete

timeout
click
StayOpen

Open
click

Figure D.5
Un diagramme dtats montre les transitions dun tat lautre.

Voici des directives concernant la modlisation dtats :


m

Indiquez un tat dans un rectangle coins arrondis.

Signalez une transition entre deux tats au moyen dun flche tte ouverte.

Un diagramme dtats na pas besoin de correspondre directement une classe


ou un diagramme dobjets. Il peut toutefois reprsenter une transposition
directe, comme le montre la Figure 22.3 du Chapitre 22.

pattern Livre Page 417 Vendredi, 9. octobre 2009 10:31 10

Glossaire

Abstraction. Une classe qui dpend de mthodes abstraites implmentes dans des
sous-classes ou dans les implmentations dune interface.
Adaptateur de classe. Un ADAPTER qui tend la classe adapter et rpond aux
exigences de linterface cible.
Adaptateur dobjet. Un ADAPTER qui tend une classe cible et dlgue une classe
existante.
Algorithme. Une procdure de calcul bien dfinie qui reoit un ensemble de
valeurs en entre et produit une valeur en sortie.
Analyse. Lanalyse dune prparation chimique.
Analyseur syntaxique. Un objet qui reconnat les lments dun langage et dcompose leur structure daprs un ensemble de rgles pour les mettre dans une forme
adapte un traitement subsquent.
API (Application Programming Interface). Linterface, ou lensemble dappels,
quun systme rend accessible publiquement.
Apoge. Le point le plus lev de la trajectoire dun artifice.
Arbre. Un modle objet qui ne contient pas de cycles.
Arbre syntaxique abstrait. Une structure, cre par un analyseur syntaxique, qui
organise le texte en entre daprs la grammaire dun langage.
Bombe arienne. Un artifice lanc partir dun mortier et qui explose en plein vol,
jectant des toiles mises feu.
Carrousel. Un grand rack intelligent qui accepte des produits par une porte et les
stocke.

pattern Livre Page 418 Vendredi, 9. octobre 2009 10:31 10

418

Glossaire

Chandelle romaine. Un tube stationnaire qui contient un mlange de charges


explosives et dtoiles.
Chemin. Dans un modle objet, une srie dobjets telle que chaque objet possde
une rfrence vers lobjet suivant.
Client. Un objet qui utilise ou requiert les mthodes dun autre objet.
Composite. Un groupe dobjets dans lequel certains objets peuvent en contenir
dautres, de faon que certains objets reprsentent des groupes et dautres reprsentent
des lments individuels, ou feuilles.
Constructeur. Dans Java, une mthode spciale dont le nom correspond celui
dune classe et qui sert instancier cette classe.
Copie complte. Une copie dun objet dans laquelle les attributs du nouvel objet
sont des copies compltes des attributs de loriginal.
Copie partielle. Une copie dun objet dans laquelle les attributs du nouvel objet ne
sont pas des copies compltes des attributs de loriginal, laissant le nouvel objet
partager avec loriginal des objets subordonns.
CORBA (Common Object Request Broker Architecture). Une conception standard (architecture commune) qui supporte la transmission de requtes dobjets
entre systmes.
Couche. Un groupe de classes possdant des responsabilits similaires, souvent
runies dans une bibliothque, et prsentant gnralement des dpendances bien
dfinies avec dautres couches.
Couplage lche. Une responsabilit mutuelle limite et bien dfinie entre des
objets qui interagisseant.
Cycle. Un chemin le long duquel un nud, ou objet, apparat deux fois.
Double dispatching. Une conception dans laquelle un objet de classe B passe une
requte un objet de classe A, lequel la repasse immdiatement lobjet de
classe B, avec des informations additionnelles sur le type de lobjet de classe A.
Driver. Un objet qui opre sur un systme informatique, tel quune base de
donnes, ou sur un quipement externe, tel quun traceur, conformment une
interface bien dfinie.

pattern Livre Page 419 Vendredi, 9. octobre 2009 10:31 10

Glossaire

419

EJB (Enterprise JavaBeans). Une spcification darchitecture multiniveau base


sur des composants.
Encapsulation. Une conception qui limite, au moyen dune interface spcifique,
laccs aux donnes et aux oprations dun objet.
Equations paramtriques. Des quations qui dfinissent un groupe de variables,
telles que x et y, en tant que fonctions dun paramtre standard, tel que t.
Etat. Une combinaison des valeurs courantes des attributs dun objet.
Etoile. Une petite bille forme partir dun mlange explosif, entrant habituellement
dans la composition dune bombe arienne ou dune chandelle romaine.
Feuille. Un lment individuel dans un composite.
Flux. Une squence doctets ou de caractres, comme celles apparaissant dans un
document.
Grammaire. Un ensemble de rgles de composition.
Graphe. Un ensemble de nuds et dartes.
Graphe orient. Un graphe dans lequel les artes ont une direction.
GUI (Graphical User Interface). Dans une application, une couche logicielle qui
permet lutilisateur dinteragir avec des boutons, des menus, des barres de dfilement,
des zones de texte, et dautres composants graphiques.
Hirarchie parallle. Une paire de hirarchies de classes dans laquelle chaque
classe dune hirarchie possde une classe correspondante dans lautre hirarchie.
Hook. Un appel de mthode plac par un dveloppeur dans le code pour permettre
dautres dveloppeurs dinsrer du code un point spcifique dune procdure.
IDE (Integrated Development Environment). Un ensemble logiciel combinant des
outils pour ldition et le dbogage de code et des outils pour la cration de
programmes.
Immuable. Qualifie un objet dont les valeurs ne peuvent pas changer.
Implmentation. Les instructions qui forment le corps des mthodes dune classe.
Initialisation paresseuse. Linstanciation dun objet seulement au moment o il est
requis.

pattern Livre Page 420 Vendredi, 9. octobre 2009 10:31 10

420

Glossaire

Interface. Lensemble des mthodes et des champs dune classe auxquels des
objets dautres classes sont autoriss accder. Egalement une interface Java qui
dfinit les mthodes que la classe dimplmentation doit fournir.
Interface de marquage. Une interface qui ne dclare aucun champ ou mthode,
dont la simple prsence indique quelque chose. Par exemple, Cloneable est une
interface de marquage qui garantit ses implmenteurs quils supporteront la
mthode clone() dfinie dans Object.
Interface graphique utilisateur. Voir GUI.
Interprteur. Un objet compos partir dune hirarchie de composition dans
laquelle chaque classe reprsente une rgle de composition dterminant comment
elle implmente ou interprte une opration qui survient dans la hirarchie.
JDBC. Une interface de programmation dapplications pour lexcution dinstructions
SQL. JDBC est une marque, non un sigle.
JDK (Java Development Kit). Un ensemble logiciel incluant des bibliothques de
classes Java, un compilateur, et dautres outils associs. Dsigne souvent spcifiquement les kits disponibles ladresse java.sun.com.
JUnit. Un environnement de test, crit par Erich Gamma et Kent Beck, qui permet
dimplmenter des tests de rgression automatiss dans Java. Disponible ladresse
www.junit.org.
Kit. Une classe avec des mthodes de cration qui retournent des instances dune
famille dobjets. Voyez le Chapitre 17, ABSTRACT FACTORY.
Langage de consolidation. Un langage informatique, tel que Java ou C#, qui tente
de prserver les points forts de ses prdcesseurs et dliminer leurs faiblesses.
Loi de Demeter. Un principe de conception orient objet qui stipule quune
mthode dun objet devrait envoyer des messages uniquement aux objets argument,
lobjet lui-mme, ou aux attributs de lobjet.
Mthode. Limplmentation dune opration.
Modle-Vue-Contrleur. Une conception qui spare un objet intressant, le modle,
des lments de GUI qui le reprsentent et le manipulent, la vue et le contrleur.
Mole. Par dfinition, cest la quantit de matire dun systme contenant autant
dentits lmentaires quil y a datomes dans 12 grammes de carbone 12 .

pattern Livre Page 421 Vendredi, 9. octobre 2009 10:31 10

Glossaire

421

Ce nombre permet dappliquer des quations chimiques tout en travaillant avec des
quantits mesurables de prparations chimiques. Si mw est le poids molculaire
dune entit chimique, mwgrammes de cette entit chimique contiendra un mole de
cette entit chimique.
Mortier. Un tube partir duquel une bombe arienne est lance.
Multiniveau. Un type de systme qui assigne des couches de responsabilits des
objets sexcutant sur diffrents ordinateurs.
Mutex. Un objet partag par des threads rivalisant pour obtenir le contrle du
verrou sur lobjet. Ce terme signifie littralement exclusion mutuelle.
Niveau. Une couche logicielle qui sexcute sur un ordinateur.
Objet mtier. Un objet qui modlise une entit ou un processus dans une entreprise.
Oozinoz. Une entreprise fictive qui fabrique et vend des pices pour feux dartifices
et organise des vnements pyrotechniques.
Opration. La spcification dun service qui peut tre demand partir dune
instance dune classe.
Pattern. Un moyen daccomplir quelque chose, datteindre un objectif.
Pattern de conception. Un pattern qui opre approximativement au niveau classe.
Polymorphisme. Le principe selon lequel linvocation dune mthode dpend la
fois de lopration invoque et du rcepteur de linvocation.
Presse toiles. Une machine qui moule une prparation chimique en lui donnant
la forme dtoiles.
Principe de substitution de Liskov. Un principe de conception orient objet qui
stipule quune instance dune classe devrait fonctionner comme une instance de sa
super-classe.
Racine. Dans un arbre, un nud ou objet distinct qui na pas de parent.
Refactorisation. La modification de code afin damliorer sa structure interne sans
changer son comportement extrieur.

pattern Livre Page 422 Vendredi, 9. octobre 2009 10:31 10

422

Glossaire

Rflexion. La possibilit de travailler avec des types et des membres de types en


tant quobjets.
Relation. Le rapport quentretiennent des objets. Dans un modle objet, il sagit du
sous-ensemble de toutes les rfrences possibles des objets dun type des objets
dun second type.
RMI (Remote Method Invocation). Une fonctionnalit Java qui permet des objets
situs sur des ordinateurs diffrents de communiquer.
Session. Lvnement qui consiste pour un utilisateur excuter un programme,
accomplir des transactions dans ce programme, et le quitter.
Signature. Une combinaison du nom dune mthode ainsi que du nombre et du
type de ses paramtres formels.
SQL (Structured Query Language). Un langage informatique pour interroger des
bases de donnes relationnelles.
Stockage persistant. Une forme de stockage sur un matriel, tel quun disque, o
les informations sont prserves mme si la machine est arrte.
Stratgie. Un plan, ou une approche, pour atteindre un but selon certaines conditions
initiales.
Thorie des graphes. Une conception mathmatique de nuds et dartes.
Lorsquelle est applique un modle objet, les nuds du graphe sont gnralement
des objets et ses artes sont habituellement des rfrences ces objets.
Traverse post-ordonne. Une itration sur un arbre ou un autre objet composite
lors de laquelle un nud est retourn aprs ses descendants.
Traverse pr-ordonne. Une itration sur un arbre ou un autre objet composite
lors de laquelle un nud est retourn avant ses descendants.
Trmie. Un conteneur qui dispense des produits chimiques, gnralement dans une
machine.
Type de retour covariant. Lorsquune sous-classe remplace une mthode et
dclare son type de retour comme tant une sous-classe du type de retour du parent.
UML (Unified Modeling Language). Une notation permettant de reprsenter des
ides de conception.

pattern Livre Page 423 Vendredi, 9. octobre 2009 10:31 10

Glossaire

423

URL (Uniform Resource Locator). Un pointeur vers une ressource Web.


Verrou. Une ressource exclusive qui reprsente la possession dun objet par un
thread.
WIP (Work In Process). Des biens en cours de fabrication dans une usine.
XML (Extensible Markup Language). Un langage textuel qui utilise des balises
contenant des informations relatives au texte et qui spare prcisment les classes
ou types de documents de leurs instances.

pattern Livre Page 424 Vendredi, 9. octobre 2009 10:31 10

pattern Livre Page 425 Vendredi, 9. octobre 2009 10:31 10

Bibliographie
Alexander, Christopher. 1979. The Timeless Way of Building. Oxford, England :
Oxford University Press.
Alexander, Christopher, Sara Ishikawa, et Murray Silverstein. 1977. A Pattern
Language: Towns, Buildings, Construction. Oxford, England : Oxford University
Press.
Arnold, Ken, et James Gosling. 1998. The JavaTM Programming Language, Second
Edition. Reading, MA : Addison-Wesley.
Booch, Grady, James Rumbaugh, et Ivar Jacobsen. 1999. The Unified Modeling
Language User Guide. Reading, MA : Addison-Wesley.
Buschmann, Frank, et al. 1996. Pattern-Oriented Software Architecture: A System
of Patterns. Chichester, West Sussex, England : John Wiley & Sons.
Cormen, Thomas H., Charles E. Leiserson, et Ronald L. Rivest. 1990. Introduction
to Algorithms. Cambridge, MA : MIT Press.
Cunningham, Ward, ed. The Portland Patterns Repository. www.c2.com.
Flanagan, David. 2005. JavaTM in a Nutshell, 5th ed. Sebastopol, CA : OReilly &
Associates.
Flanagan, David, Jim Farley, William Crawford, et Kris Magnusson. 2002. JavaTM
Enterprise in a Nutshell, 2d ed. Sebastopol, CA : OReilly & Associates.
Fowler, Martin, Kent Beck, John Brant, William Opdyke, et Don Roberts. 1999.
Refactoring: Improving the Design of Existing Code. Reading, MA : AddisonWesley.
Fowler, Martin, et Kendall Scott. 2003. UML Distilled, Third Edition. Boston, MA :
Addison-Wesley.
Gamma, Erich, Richard Helm, Ralph Johnson, et John Vlissides. Design Patterns.
1995. Boston, MA : Addison-Wesley.

pattern Livre Page 426 Vendredi, 9. octobre 2009 10:31 10

426

Bibliographie

Gosling, James, Bill Joy, Guy Steele, et Gilad Bracha. 2005. The Java TM Language
Specification, Third Edition. Boston, MA : Addison-Wesley.
Kerievsky, Joshua. 2005. Refactoring to Patterns. Boston, MA : Addison-Wesley.
Lea, Doug. 2000. Concurrent Programming in Java TM, Second Edition. Boston,
MA : Addison-Wesley.
Lieberherr, Karl J., et Ian Holland. 1989. "Assuring good style for object-oriented
programs". Washington, DC. IEEE Software.
Liskov, Barbara. May 1987. "Data abstraction and hierarchy". SIGPLAN Notices,
volume 23, number 5.
Metsker, Steven J. 2001. Building Parsers with Java TM. Boston, MA : AddisonWesley.
Russell, Michael S. 2000. The Chemistry of Fireworks. Cambridge, UK : Royal
Society of Chemistry.
Vlissides, John. 1998. Pattern Hatching: Design Patterns Applied. Reading, MA :
Addison-Wesley.
Wake, William C. 2004. Refactoring Workbook. Boston, MA : Addison-Wesley.
Weast, Robert C., ed. 1983. CRC Handbook of Chemistry and Physics, 63rd ed.
Boca Raton, FL : CRC Press.
White, Seth, Mayderne Fisher, Rick Cattell, Graham Hamilton, et Mark Hapner.
1999. JDBC TM API Tutorial and Reference, Second Edition. Boston, MA : AddisonWesley.
Wolf, Bobby. 1998. "Null object", in Pattern Languages of Program Design 3, ed.
Robert Martin, Dirk Riehle, et Frank Buschmann. Reading, MA : Addison-Wesley.

pattern Livre Page 427 Vendredi, 9. octobre 2009 10:31 10

Index
A
ABSTRACT FACTORY
et FACTORY METHOD 178
et packages 182
kits de GUI 173
objectif 173
solutions aux exercices 379
Abstractions
BRIDGE 61
drivers 66
Adaptateurs
dobjets 25
de classes 25
ADAPTER
adaptateurs de classes et dobjets 25
identification 33
objectif 21
pour des donnes JTable 29
pour des interfaces 21
solutions aux exercices 338
Algorithmes 207
compltion 215
de tri 211
et mthodes 207
vs stratgies 235
Alternances
et VISITOR 327
vs squences 56
Analyseurs syntaxiques
INTERPRETER (pour) 265
pour BUILDER 158
VISITOR (pour) 329

Annulation doprations 189


Applications orientes objet 35
Arbres
dans COMPOSITE 50
syntaxiques abstraits 329
Artes de graphes 50
Arrays (classe) 212
ASP.NET (Active Server Pages for .NET)
122

Attributs
changeants, objets 223
comparaison dans un algorithme de tri 212
dun objet copi 185
B
Boucles
for 296
infinies 57
BRIDGE
abstraction 61
drivers 66
JDBC 67
objectif 61
solutions aux exercices 348
BufferedWriter (classe) 278
BUILDER
avec des contraintes 160
objectif 157
ordinaire 157
solutions aux exercices 373
tolrant 163

pattern Livre Page 428 Vendredi, 9. octobre 2009 10:31 10

428

Index

C
CHAIN OF RESPONSABILITY
ancrage 140
objectif 135
ordinaire 135
refactorisation (pour appliquer) 137
sans COMPOSITE 142
solutions aux exercices 364
Chanes
analyse syntaxique 157
immuables 144
Chargeur de classe, PROXY 128
Chemin, COMPOSITE 51
Classes
adaptateurs (de) 25
chargeur (de) 128
constructeurs (de) 153
de contrle 62
de filtrage 279
de handshaking 64
extension 269
familles (de) 182
hirarchies 61
hirarchies parallles 169
implmentation 15
instances uniques 79
instanciation 167
interfaces 15
modlisation UML 409
pour des couches de code 92
stub 18
visibilit 75
Classes abstraites
adaptateurs dobjets (pour des) 29
dans des hirarchies 61
et interfaces 16
pour des classes de filtrage 279
Clients
dfinition des exigences dans une interface
25

en tant que listeners, Swing 86


enregistrement pour notifications 193

et proxies 122
objets (en tant que) 21
partage dobjets (entre) 143
Clones
de collections 302
prototypage (avec des) 185
Cohrence relationnelle 107
Collections
boucle for 296
clones 302
itrateurs 165, 297
pour la scurit inter-threads 297
tri 212
Collections (classe) 212
COMMAND
commandes de menus 245
en relation avec dautres patterns 251
fourniture dun service 248
hooks 249
objectif 245
solutions aux exercices 393
Commandes de menus 245
Comparaisons
conditions boolennes 262
dans des algorithmes de tri 212
Comparator (interface) 212
Comportement rcursif dans les composites 48
COMPOSITE
arbres 50
comportement rcursif 48
cycles 51, 55
feuilles 47
groupes dobjets 47
itrateurs 303
non-arbre 59
objectif 47
ordinaire 47
solutions aux exercices 345
traverses pr-ordonne/post-ordonne 303

pattern Livre Page 429 Vendredi, 9. octobre 2009 10:31 10

Index

Concurrence, POO 81
Conditions boolennes 261
Consolidation, langage (de) 7
Constantes, dclaration dans
une interface 19
Constructeurs de classes 153
Construction 153
avec des contraintes 160
dfis 153
progressive dobjets 157
solutions aux exercices 371
Contraintes de construction 160
Contrat
dune interface 17
entre les dveloppeurs 221
Contrleurs, conception MVC 90
Copie
complte 383
de collections 302
de prototypes 185
partielle 383
CORBA (Common Object Request Broker
Architecture) 122
Corps de mthodes 204
Couches de code, conception MVC 92
Couplage lche, MEDIATOR 105
Cycles
consquences 59
dans COMPOSITE 51, 55
et VISITOR 323
D
DECORATOR
en relation avec dautres patterns 292
enveloppeurs de fonctions 285
flux et objets dcriture 277
objectif 277
solutions aux exercices 399

Dcouplage 19
Demeter, loi (de) 271
Dmos 36, 342
Dpendances, OBSERVER 98
Diagramme de squence UML 68
Double dispatching, VISITOR 322
Drivers
de base de donnes 67
en tant que BRIDGE 66
Dure de vie, MEMENTO 196
E
Eclipse (IDE) 35
Encapsulation 77
En-ttes de mthodes 204
Enveloppeurs de fonctions 285
Environnement de dveloppement
intgr 35
Equations paramtriques 40
Erreurs potentielles, limination 273
Etats
attributs changeants 223
constants 231
dans MEMENTO 189
machine () 225
modlisation 223, 416
Exceptions
dclaration 206
distantes 123
gnration 206
gestion 206
Extensions 269
indicateurs 273
loi de Demeter 271
principe de substitution de Liskov 270
solutions aux exercices 398

429

pattern Livre Page 430 Vendredi, 9. octobre 2009 10:31 10

430

Index

F
FACADE
dmos 36
quations paramtriques 40
objectif 35
refactorisation (pour appliquer) 37
solutions aux exercices 342
utilitaires 36
FACTORY METHOD
contrle du choix de la classe instancier
167

dans une hirarchie parallle 169


et ABSTRACT FACTORY 178
identification 166
itrateurs 165
objectif 165
solutions aux exercices 375
Familles
dobjets 179
de classes 182
Feuilles
dans COMPOSITE 47
numration 311

G
Grammaire dun langage de programmation 266
Graphes orients 51
GUI (Graphical User Interface)
conception Modle-Vue-Contrleur
(MVC) 90
et MEDIATOR 101
et OBSERVER 85
kits (de) 173
pour des commandes de menus 245
pour une application de visualisation 175
H
Handshaking, classes (de) 64
Hirarchies de classes 61
extensions 315
parallles 169
stabilit 328
Hooks
pour COMMAND 249
pour TEMPLATE METHOD 218

FileWriter (classe) 278, 280

Filtres de flux 279


Flux dE/S 277
FLYWEIGHT
objectif 143
objets immuables 143
partage dobjets 146
refactorisation (pour appliquer) 144
solutions aux exercices 368

IDE (Integrated Development


Environment) 35
Identification
de ADAPTER 33
de FACTORY METHOD 166
de SINGLETON 82
Immuabilit, FLYWEIGHT 143

for (boucle) 295

Implmentations
doprations abstraites 61
de classes 15
par dfaut 29
vides 18

foreach (boucle) 295

Indicateurs 273

Fonctions
boolennes 404
enveloppeurs (de) 285

pattern Livre Page 431 Vendredi, 9. octobre 2009 10:31 10

Index

Initialisation paresseuse
de planificateurs 220
de singletons 80
Instances
initialisation paresseuse 80
principe de substitution de Liskov 270
uniques 79
Intgrit relationnelle 107
Interfaces
adaptation ( des) 21
contrat 17
de marquage 338
et classes abstraites 16
et proxies dynamiques 128
Java 15
modlisation UML 414
obligations 17
pour VISITOR 330
INTERPRETER
exemple 254
langages et analyseurs syntaxiques 265
objectif 253
solutions aux exercices 396
Inverse dune relation binaire 107
InvocationHandler (interface) 129
ITERATOR
ajout dun niveau de profondeur un
numrateur 310
avec scurit inter-threads 297
numration des feuilles 311
objectif 295
ordinaire 295
pour un composite 303
solutions aux exercices 401
traverses pr-ordonne/post-ordonne 303
J
JavaBeans 122
JDBC 67

431

JDK (Java Development Kit) 29


JTable (adaptation pour) 29
JUnit 248, 406
K
Kits
doutils 35
de GUI 173
L
Langage de consolidation 7
Liskov, principe de substitution (de) 270
Listeners, Swing 86, 246
Loi de Demeter 271
M
Machine tats UML 225
Marquage, interfaces (de) 338
MEDIATOR
objectif 101
pour lintgrit relationnelle 106
pour les GUI 101
refactorisation (pour appliquer) 104
solutions aux exercices 359
MEMENTO
annulation doprations 189
dure de vie 196
informations dtat 189
objectif 189
persistance entre les sessions 197
solutions aux exercices 384
Menus
commandes (de) 245
Java 246
Mthodes 203
constructeurs 153
corps 204
dans des classes stub 18

pattern Livre Page 432 Vendredi, 9. octobre 2009 10:31 10

432

Index

Mthodes (suite)
en-tte 204
et algorithmes 207
et polymorphisme 208
gnration dexceptions 206
hooks 218, 249
loi de Demeter 271
minutage 248
modles 211
modificateurs daccs 204
signatures 205
utilitaires 39
visibilit 75
Modle objet 51
relation (de) 107
vs modle relationnel 107
Modle-Vue-Contrleur (MVC)
pour MEMENTO 191
pour OBSERVER 90
Modlisation
dtats 223, 416
dinterfaces 414
dobjets 414
de classes 409
de composites 60
de conditions boolennes 261
de relations inter-classes 412
de stratgies 236
UML 409
Modificateurs daccs 76, 204
Mutex (objet) 301
N
Nuds
dans des graphes 50
dans des itrations 303
enfants 303
parents 51
ttes 303

O
Objets
adaptateurs (d) 25
clients 21
composites 47
encapsulation 77
tat 223
excutables 253
familles (d) 179
feuilles 47
immuables 143
mtiers 92
modlisation UML 414
mutex 301
paires ordonnes 106
partage 143
racines 140
rfrences 51
responsabilit 73, 79
uniques 82
verrous 81
Obligations des interfaces 17
Observable (classe) 92
OBSERVER
approches push/pull 97
dans les GUI 85
maintenance 96
Modle-Vue-Contrleur 90
objectif 85
refactorisation (pour appliquer) 88
solutions aux exercices 354
Oprations
abstraites 61
annulation 189
et mthodes 203
signatures 205
solutions aux exercices 387
stratgiques 235
Outils, kits (d) 35

pattern Livre Page 433 Vendredi, 9. octobre 2009 10:31 10

Index

P
Packages
dans ABSTRACT FACTORY 182
kits doutils 35
Paires ordonnes dobjets 106
Paradigmes
classe/instance 7
concurrents 7
Parents, nuds 51
Partage dobjets 143
Patterns 3
Patterns de conception 4
classification 9
construction 153
extensions 269
interfaces 15
oprations 203
responsabilit 73
Polymorphisme 208, 230, 245
Principe de substitution de Liskov 270
Procdures Voir Algorithmes
Produit cartsien 107
Programmation oriente aspect (POA) 132
Programmation oriente objet (POO)
avec concurrence 81
limination des erreurs potentielles 273
encapsulation 77
loi de Demeter 271
polymorphisme 208
principe de substitution de Liskov 270
PROTOTYPE
en tant quobjet factory 183
objectif 183
pour des clones 185
solutions aux exercices 382
PROXY
distant 122
dynamique 128

433

objectif 115
pour des images 115
solutions aux exercices 362
suppression 120
Pull, OBSERVER 97
Push, OBSERVER 97
R
Racines
dans CHAIN OF RESPONSABILITY 140
dans des graphes 51
Refactorisation
pour appliquer CHAIN OF RESPONSABILITY 137
pour appliquer FACADE 37
pour appliquer FLYWEIGHT 144
pour appliquer MEDIATOR 104
pour appliquer OBSERVER 88
pour appliquer STATE 227
pour appliquer STRATEGY 238
pour appliquer TEMPLATE METHOD 219
Rfrences dobjets 51
Rflexion
dans PROXY 129
pour linstanciation 154
Registre RMI 124
Relations entre objets 106
repeat (boucle) 295
Responsabilit
codage en couches 95
contrle par la visibilit 75
couplage lche 105
distribution vs centralisation 77
et encapsulation 77
objets 79
ordinaire 73
solutions aux exercices 350
Rutilisabilit des kits doutils 35
RMI (Remote Method Invocation) 122

pattern Livre Page 434 Vendredi, 9. octobre 2009 10:31 10

434

Index

S
Scurit inter-threads 297
Squences
dinstructions 211
et VISITOR 327
vs alternances 56
Services
fourniture avec COMMAND 248
spcification 203
Sessions 197
Signatures de mthodes 205
SINGLETON
et threads 81
identification 82
mcanisme 79
objectif 79
solutions aux exercices 353
Solutions aux exercices
ABSTRACT FACTORY 379
ADAPTER 338
BRIDGE 348
BUILDER 373
CHAIN OF RESPONSABILITY 364
COMMAND 393
COMPOSITE 345
construction 371
DECORATOR 399
extensions 398
FACADE 342
FACTORY METHOD 375
FLYWEIGHT 368
interfaces 337
INTERPRETER 396
ITERATOR 401
MEDIATOR 359
MEMENTO 384
OBSERVER 354
oprations 387
PROTOTYPE 382
PROXY 362
responsabilit 350
SINGLETON 353
STATE 390

STRATEGY 392
TEMPLATE METHOD 389
VISITOR 403
Sous-classes
anonymes 94
de classes stub 18
pour des adaptateurs de classes 25
pour INTERPRETER 257
STATE
et STRATEGY 242
tats constants 231
modlisation dtats 223
objectif 223
refactorisation (pour appliquer) 227
solutions aux exercices 390
Stockage persistant 197
STRATEGY
et STATE 242
et TEMPLATE METHOD 243
modlisation de stratgies 236
objectif 235
refactorisation (pour appliquer) 238
solutions aux exercices 392
vs algorithmes 235
Stub, classes 18
Super-classes 24
Swing 245
et OBSERVER 86
listeners 246
widget JTable 29
Systmes multiniveaux 96
T
Tableaux
adaptation une interface 29
et boucles for 296
tri 212
TEMPLATE METHOD
algorithmes de tri 211
compltion dun algorithme 215
et STRATEGY 243

pattern Livre Page 435 Vendredi, 9. octobre 2009 10:31 10

Index

hooks 218
objectif 211
refactorisation (pour appliquer) 219
solutions aux exercices 389
Texte
analyse syntaxique 265
filtres de mise en forme 280
passage la ligne 283
Thorie des graphes 51
Threads
dans SINGLETON 81
pour le chargement dimages 118
scurit (inter-) 297
verrous 81
throws (clause) 204
Traverses pr-ordonne/post-ordonne 303
Tri, algorithmes (de) 211
Types de retour
covariants 388
de mthodes 205
U
UML (Unified Modeling Language) 409
diagramme de squence 68
machine tats 225

notation 7
UnicastRemoteObject (interface) 123
Utilitaires, FACADE 36
V
Verrous
dans une application multithread 301
sur des objets 81
Visibilit
des classes et des mthodes 75
et responsabilit 75
VISITOR
application (de) 315
double dispatching 322
et cycles 323
inconvnients 328
objectif 315
ordinaire 318
solutions aux exercices 403
Vues, conception MVC 90
W
while (boucle) 295
WIP (Work In Process) 82

435

Rfrence

Les design patterns en Java


Les 23 modles de conception fondamentaux
Tout programmeur Java se doit de connatre les 23
design patterns fondamentaux recenss par les clbres
dveloppeurs du Gang of Four, vritable condens de
lexprience de plusieurs gnrations de dveloppeurs, et
aujourdhui incontournable pour crire un code propre et
efficace.
Cet ouvrage, fond sur de nombreux exemples dapplication,
vous aidera comprendre ces modles et dveloppera votre
aptitude les appliquer dans vos programmes.
Forts de leur exprience en tant quinstructeurs et
programmeurs Java, Steve Metsker et William Wake vous
claireront sur chaque pattern, au moyen de programmes
Java rels, de diagrammes UML, de conseils sur les bonnes
pratiques et dexercices clairs et pertinents. Vous passerez
rapidement de la thorie lapplication en apprenant
comment crire un meilleur code ou restructurer du code
existant pour le rationaliser, le rendre plus performant et
plus facile maintenir.
Code source disponible sur le site www.oozinoz.com !
propos des auteurs :
Steven John Metsker, spcialiste des techniques orientes objet sousjacentes la cration de logiciels purs et performants, est lauteur des
ouvrages Building Parsers with Java, Design Patterns Java Workbook
et Design Patterns in C# (parus chez Addison-Wesley). Il est dcd en
fvrier 2008.

TABLE DES MATIRES

Introduction aux interfaces


ADAPTER
FACADE
COMPOSITE
BRIDGE
Introduction la responsabilit
SINGLETON
OBSERVER
MEDIATOR
PROXY
CHAIN OF RESPONSABILITY
FLYWEIGHT
Introduction la construction
BUILDER
FACTORY METHOD
ABSTRACT FACTORY
PROTOTYPE
MEMENTO
Introduction aux oprations
TEMPLATE METHOD
STATE
STRATEGY
COMMAND
INTERPRETER
Introduction aux extensions
DECORATOR
ITERATOR
VISITOR
Code source dOozinoz
Introduction UML

William C. Wake (www.xp123.com) est consultant logiciel, coach et


instructeur indpendant, avec plus de vingt annes dexprience en
programmation. Il est lauteur de Refactoring Workbook et Extreme
Programming Explored (parus chez Addison-Wesley).

Programmation

Niveau : Avanc
Configuration : Multiplate-forme

Pearson Education France


47 bis, rue des Vinaigriers
75010 Paris
Tl. : 01 72 74 90 00
Fax : 01 42 05 22 17
www.pearson.fr

ISBN : 978-2-7440-4097-9