Vous êtes sur la page 1sur 686

Design patterns Tte la premire

Design patterns Tte la premire


Ne serait-ce pas merveilleux sil y avait un livre sur les Design Patterns qui soit plus amusant quun rendez-vous chez le dentiste et plus facile comprendre quune dclaration de revenus? Mais ce nest sans doute quun fantasme...

Eric Freeman Elisabeth Freeman


avec Kathy Sierra Bert Bates Traduction de Marie-Ccile Baland

Beijing Cambridge Kln Paris Sebastopol Taipei Tokyo

Design patterns - Tte la premire


de Eric Freeman , Elisabeth Freeman, Kathy Sierra et Bert Bates Ldition originale de cet ouvrage a t publie aux tats-Unis par OReilly Media sous le titre Head First Design Patterns 2004 OReilly Media DITIONS OREILLY, Paris, 2005
ISBN 2-35402-107-0

ditrice : Couverture conue par : Spcialistes des patterns: Faade et dcoration : Stratgie: Observateur:

Dominique Buraud Ellie Volckhausen et Marcia Friedman Eric Freeman, Elisabeth Freeman Elisabeth Freeman Kathy Sierra et Bert Bates Oliver

Nombre de dsignations utilises par les fabricants et les fournisseurs sont des marques de commerciales dposes. Lorsque ces dsignations apparaissent dans ce livre et quelles sont connues dOReilly Media, Inc. comme tant des marques dposes, elles viennent en capitales ou avec une capitale linitiale. Bien que tout le soin ncessaire, ait t apport la prparation de ce livre, lditeur et les auteurs nassument aucune responsabilit des erreurs ou des omissions, ni dventuels dommages rsultant de lutilisation des informations quil contient. Les programmes figurant dans ce livre ont pour but dillustrer les sujets traits. Il nest donn aucune garantie quant leur fonctionnement une fois compils, assembls ou interprts dans le cadre dune utilisation professionnelle ou commerciale. Autrement dit, si vous utilisez un lment quelconque de Design patterns tte la premire dans le dveloppement dun systme ddi une centrale nuclaire, vous le faites vos risques et prils. En revanche, nous vous encourageons vivement utiliser lapplication VueDJ. Il na t fait de mal aucun canard dans la rdaction de ce livre. Les membres de la Bande des quatre ont consenti la publication de leur photo. Oui, ils sont rellement aussi mignons que cela. Toute reprsentation ou reproduction, intgrale ou partielle, faite sans le consentement de lauteur, de ses ayants droit, ou ayants cause, est illicite (loi du 11 mars 1957, alina 1er de larticle 40). Cette reprsentation ou reproduction, par quelque procd que ce soit, constituerait une contrefaon sanctionne par les articles 425 et suivants du Code pnal. La loi du 11 mars 1957 autorise uniquement, aux termes des alinas 2 et 3 de larticle 41, les copies ou reproductions strictement rserves lusage priv du copiste et non destines une utilisation collective dune part et, dautre part, les analyses et les courtes citations dans un but dexemple et dillustration.

Retrouvez galement la version papier sur notre site : http://www.oreilly.fr/catalogue/9782841773503

la Bande des quatre, leur perspicacit et lexpertise dont ils ont fait preuve en collectant et en communiquant les design patterns ont chang pour toujours la face du dveloppement logiciel et amlior la vie des dveloppeurs du monde entier.

Mais srieusement, quand allons-nous voir une deuxime dition? Aprs tout, cela ne fait jamais que dix ans!

les auteurs

Auteurs/dveloppeurs de Design patterns tte la premire


hF Elisabet reeman

Eric Freeman

artiste numrique. Elle sest implique depuis le dbut sur lInternet, ayant t la cofondatrice de The Ada Project (TAP), un centre de ressources en ligne sur les femmes et linformatique qui est maintenant un projet de lACM. Plus rcemment, Elisabeth a coordonn des efforts de recherche et de dveloppement dans le domaine des mdias numriques pour la Walt Disney Company, o elle a t lun des inventeurs de Motion, un systme de gestion de contenu qui dlivre quotidiennement des traoctets de vido aux utilisateurs de Disney, ESPN et Movies.com. Informaticienne dans lme, Elisabeth est diplme de luniversit Yale et de luniversit dIndiana. Elle a travaill dans de nombreux domaines, notamment les langages de programmation visuels, la syndication RSS et les systmes Internet. Elle a galement dvelopp des programmes encourageant les femmes travailler dans le domaine de linformatique. Ces jours-ci, vous la trouverez en train de siroter du Java ou du Cocoa sur son Mac, bien quelle rve du jour o le monde entier utilisera Scheme. Elisabeth adore la nature et la randonne depuis son enfance cossaise. Quand elle est dehors, son appareil photo nest jamais loin. Cest galement une cycliste passionne, une vgtarienne convaincue et une amie des animaux.Vous pouvez lui envoyer un message beth@wickedlysmart.com vi

Elisabeth est auteur, dveloppeur de logiciels et

Eric est un informaticien qui se passionne pour les mdias et les

architectures logicielles. Il vient de terminer une mission de rve qui a dur quatre ans coordonner les efforts en matire dInternet large bande et daccs sans fil chez Disney et se consacre maintenant de nouveau lcriture, la cration de logiciels et travailler sur Java et sur les Macs.

Eric a pass une bonne partie des annes90 travailler sur des alternatives la mtaphore du bureau avec David Gelernter (et ils se posent toujours tous deux la question pourquoi suis-je oblig de donner un nom un fichier?). Cest sur la base de ces recherches quil a soutenu sa thse de doctorat Yale University en 1997. Il a galement co-fond Mirror Worlds Technologies pour diffuser une version commerciale de son travail, Lifestreams. Dans une vie antrieure, Eric a dvelopp des logiciels pour des rseaux et des supercalculateurs. Vous le connaissez peut-tre galement comme auteur douvrages tels que JavaSpaces Principles Patterns and Practice. Il se souvient avec tendresse davoir implment des systmes espaces de tuples sur Thinking Machine CM-5 et davoir cr certains des premiers systmes dinformation en ligne pour la NASA la fin des annes80. Eric vit actuellement dans le semi-dsert, prs de Santa F. Quand il ne sadonne ni lcriture ni la programmation, vous le trouverez en train de passer plus de temps bricoler son home cinma qu le regarder, ou essayer de restaurer un vieux jeu vido Dragons Lair g dau moins vingt ans. Il ne rechignerait pas non plus passer ses nuits dans le rle de DJ avec de la musique lectronique. crivez-lui eric@wickedlysmart.com ou visitez son blog http://www.ericfreeman.com

Crateurs de la collection Tte la premire (et conspirateurs sur ce livre)


Kathy Sierra

Bert Bates

Bert est architecte et dveloppeur de logiciels, mais Kathy sintresse aux thories de lapprentissage depuis

lpoque o elle tait conceptrice de jeux (elle a crit des programmes pour Virgin, MGM et Amblin). Elle a mis au point la majeure partie du concept de ce livre lorsquelle enseignait les nouveaux mdias luniversit de Californie Los Angeles. Plus rcemment, elle a t formatrice de formateurs chez Sun Microsystems, o elle a enseign aux instructeurs la faon de transmettre les dernires technologies et dvelopp plusieurs examens de certification. Avec Bert Bates, elle a utilis activement les concepts de Java tte la premire pour former des milliers de dveloppeurs. Elle a fond le site javaranch.com, qui a obtenu en2003 et2004 le Jolt Cola Productivity Award du magazine Software Development. Elle enseigne galement Java dans les croisires Geek Cruise (geekcruises.com). Elle a rcemment quitt la Californie pour vivre au Colorado. Elle a d apprendre de nouveaux mots comme antigel et cache-nez, mais la lumire y est fantastique. Kathy aime courir, skier, faire du skateboard et jouer avec son cheval islandais. Elle sintresse aussi aux phnomnes inhabituels. Ce quelle dteste le plus: lentropie. Vous pouvez la trouver su Javaranch, ou lire ses participations occasionnelles au blog de java.net. crivezlui kathy@wickedlysmart.com.

dix ans dexprience dans le domaine de lintelligence artificielle lont amen sintresser aux thories de lapprentissage et aux apports des nouvelles technologies la formation. Depuis lors, il apprend ses clients mieux programmer. Rcemment, il a dirig chez Sun lquipe de dveloppement de plusieurs examens de certification Java

Il a consacr la premire dcennie de sa carrire dinformaticien aider des clients du secteur de la radiodiffusion, comme Radio New Zealand, Weather Channel et A & E (Arts & Entertainment Network). Lun des ses projets favoris a t de tout temps un programme de simulation complet pour lUnion Pacific Railroad. Bert est de longue date un joueur de go dsesprment accro et il travaille un programme de go depuis bien trop longtemps. Cest un bon guitariste et il est en train de sexercer au banjo. Cherchez-le sur Javaranch, sur IGS (Internet Go Server) ou crivez-lui terrapin@wickedlysmart.com.

vii

table des matires

Table des matires (rsum)


Intro 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Bienvenue aux Design Patterns: introduction Tenez vos objets au courant: le pattern Observateur Dcorer les objets: le pattern Dcorateur Un peu de cuisine oriente objet: les patterns fabriques Des objets uniques en leur genre: le pattern Singleton Encapsuler linvocation: le pattern Commande Savoir sadapter: les patterns Adaptateur et Faade Encapsuler les algorithmes: le pattern Patron de mthode Des collections bien gres: les patterns Itrateur et Composite Ltat des choses : le pattern tat Contrler laccs aux objets: le pattern Proxy Patterns de Patterns: Patterns composs Les patterns dans le monde rel: Mieux vivre avec les patterns Annexe : Les patterns restants xxiii 1 37 79 109 169 191 235 275 315 385 429 499 577 611

Table des matires (le contenu rel)


Intro
Votre cerveau et les Design Patterns.
Voil que vous essayez dapprendre quelque chose tandis que votre cerveau fait tout ce quil peut pour vous empcher de mmoriser. Votre cerveau pense Mieux vaut laisser de la place pour les choses vraiment importantes, comme savoir quels sont les animaux sauvages quil convient dviter ou se rendre compte que faire du snowboard en maillot de bain nest pas une bonne ide. Mais comment procder pour le convaincre que votre vie dpend de votre matrise des Design Patterns? qui sadresse ce livre? Nous savons ce que pense votre cerveau Mtacognition Soumettez votre cerveau diteurs techniques Remarciements xxiv xxv xxvii xxix xxxii xxxiii

viii

intro aux Design Patterns


Bienvenue aux Design Patterns
Quelquun a dj rsolu vos problmes. Dans ce chapitre, vous allez
apprendre comment (et pourquoi) exploiter lexprience et les leons tires par dautres dveloppeurs qui ont dj suivi le mme chemin, rencontr les mmes problmes de conception et survcu au voyage. Nous allons voir lusage et les avantages des design patterns, revoir quelques principes fondamentaux de la conception OO et tudier un exemple de fonctionnement dun pattern. La meilleure faon dutiliser un pattern est de le charger dans votre cerveau puis de reconnatre les points de vos conceptions et des applications existantes auxquels vous pouvez les appliquer. Au lieu de rutiliser du code, les patterns vous permettent de rutiliser de lexprience.

Souvenezvous: connatre des concepts tels que labstraction, lhritage et le polymorphisme ne fait pas de vous un bon concepteur orient objet. Celui qui matrise les patterns sait comment crer des conceptions souples, faciles maintenir et capables de rsister au changement.

Le simulateur de canards Jol rflchit lhritage.... Et si nous utilisions une interface? La seule et unique constante du dveloppement Sparer ce qui change de ce qui reste identique Concevoir le comportement des canards Tester le code Modifier dynamiquement les comportements Vue densemble des comportements encapsuls A-UN peut tre prfrable EST-UN Le pattern Stratgie Le pouvoir dun vocabulaire partag Comment utiliser les design patterns? Votre bote outils de concepteur Solutions des exercices
Compor tement
voler()

2 5 6 8 10 11 18 20 22 23 24 28 29 32 34

de vol

encaps

ul

<<interface>>Vol Comportement

Ailes VolerAvecDes
voler() { du canard nte le vol // implme }

NePasVoler peut pas voler() { canard ne faire - le / ne rien } voler !

Votre C
apsul
r!

ERVEA

Un ensemble de patterns

Client

ol tementV Vol compor ancan Comportement comportementC Cancan Comportement nager() afficher() () effectuerCancan

Canard

Compor

tement

de can
<<interface>> r

canem

ent enc

Comportemen
cancane

tCancan

effectuerVol() ntVol() setComporteme ntCancan() ... aux canards setComporteme s propres S mthode // AUTRE

CanardMuet Coincoin Cancan


cancane r() { peut pas cancane canard ne faire - le // ne rien }

Mandarin

CanardEnPlas

tique

Objet qui contient un tat


Colvert
{ afficher() } dun colvert // aspect
O bj et S

Leurre
{ afficher() // aspect dun leurre }

{ afficher() in} dun mandar // aspect

{ e} afficher() en plastiqu dun canard // aspect

r() { cancane ment nte le cancane // implme }

e r() { cancane en plastiqu ent du caneton // couinem }

OBSERVATEUR

8
int
u je
t

8 8 8 8
Ob
jet C anard

Ob

Objets d pe

jet C hat

ndants

MVC
Mod le

Obj et S

ouris

Mise jour et notification automatiques

ller ontro

Observateurs

te Requ

Vue

visit grce Votre code, sre sign de lemploi de ! ns er patt


ix

table des matires

2
Principes OO

le pattern Observer
Tenez vos objets au courant
Ne manquez plus jamais rien dintressant! Nous avons un
pattern qui met vos objets au courant quand il se passe quelque chose qui pourrait les concerner. Ils peuvent mme dcider au moment de lexcution sils veulent rester informs. Observateur est lun des patterns le plus utilis dans le JDK et il est incroyablement utile. Avant la fin de ce chapitre, nous tudierons galement les relations un--plusieurs et le faible couplage (oui, oui, nous avons dit couplage). Avec Observateur, vous serez lattraction de la soire Patterns. La station mtorologique 39 44 45 48 51 53 56 57 64 71 74 78

Bases de lOO
. ce qui varie Encapsulez hritage. position l m o c la z e Prfr s, non des s interface e d z e m m a r Prog ions. ment implmentat oupler faible c e d s u vo Efforcez ui interagissent. les objets q
Abstraction n Encapsulatio me Polymorphis Hritage

Faites connaissance avec le pattern Observateur Diffusion + Souscription = pattern Observateur Comdie express : un sujet dobservation Le pattern Observateur: dfinition Le pouvoir du faible couplage Concevoir la station mto Implmenter la station mto Utiliser le pattern Observateur de Java La face cache de java.util.Observable Votre bote outils de concepteur Solutions des exercices

RELATION UN--PLUSIEURS Objet qui contient un tat


O

ard

Obj

et Ca

Obj
ris

a et Ch

Mise jour et notification automatiques

Obj

ou et S

Observateurs

Objets d

pendants

bj

et S jet u

int

8 8

Obj

en
t

i et Ch

le pattern Dcorateur
Dcorer les objets
Ce chapitre pourrait sappeler Les bons concepteurs se mfient de lhritage. Nous allons revoir la faon dont on
abuse gnralement de lhritage et vous allez apprendre comment dcorer vos classes au moment de lexcution en utilisant une forme de composition. Pourquoi ? Une fois que vous connatrez les techniques de dcoration, vous pourrez affecter vos objets (ou ceux de quelquun dautre) de nouvelles responsabilits sans jamais modifier le code des classes sous-jacentes. Bienvenue chez Starbuzz Coffee 80 86 88 89 91 92 95 100 102 105 106

Je croyais que les vrais hommes sous-classaient tout. Ctait avant de dcouvrir le pouvoir de lextension au moment de lexcution et non celui de la compilation. Maintenant, regardezmoi!

Le principe Ouvert-Ferm Faites connaissance avec le pattern Dcorateur Construire une boisson avec des dcorateurs Le pattern Dcorateur: dfinition Dcorons nos boissons crire le code de Starbuzz Dcorateurs du monde rel: les E/S Java crire votre propre dcorateur dE/S Java Votre bote outils de concepteur Solutions des exercices

xi

table des matires

4
Linterface abstraite FabriqueIngredientsPizza est linterface qui dfinit la faon de crer une famille de produits apparents tout ce quil nous faut pour confectionner une pizza.
<<interface>> FabriqueIngredientsPizza
creerPate() creerSauce() creerFromage() creerLegumes() creerPoivrons() creerFruitsDeMer()

les patterns fabriques


Un peu de cuisine oriente objet
Apprtez-vous confectionner des conceptions OO faiblement couples. Crer des objets ne se limite pas utiliser
loprateur new. Vous allez apprendre que linstanciation est une activit qui ne devrait pas toujours tre publique et qui peut souvent entraner des problmes de couplage. Et vous nen avez pas vraiment envie, nest-ce pas? Dcouvrez comment les patterns fabriques peuvent vous aider vous librer de dpendances embarrassantes. Quand vous voyez new, pensez concret
Les clients de la Fabrique Abstraite sont les deux instances de notre Pizzeria, PizzeriaBrest et PizzeriaStrasbourg.
PizzeriaBrest
creerPizza()

110 112 114 115 117 120 121 123 125 131 132 134 137 138 139 144 145 146 153 154 156 160 162 164

la Pizzeria dObjectville Encapsuler la cration des objets Construire une simple fabrique de pizzas Fabrique Simple: dfinition Une structure pour la pizzeria Laisser les sous-classes dcider Crer une pizzeria Dclarer une mthode de fabrique Il est enfin temps de rencontrer le pattern Fabrication Hirarchies de classes parallles Le Pattern Fabrication: dfinition Une Pizzeria trs dpendante Problmes de dpendance des objets Le principe dinversion des dpendances Pendant ce temps, la pizzeria... Familles dingrdients... Construire nos fabriques dingrdients Un coup dil Fabrique abstraite Dans les coulisses Le pattern Fabrique abstraite: dfinition Fabrication et Fabrique Abstraite compars Votre bote outils de concepteur Solutions des exercices

<<interface>> Pate

PateSoufflee

PateFine

<<interface>> Sauce

SauceTomatesCerise

SauceMarinara

FabriqueIngredientsPizzaBrest
creerPate() creerSauce() creerFromage() creerLegumes() creerPoivrons() creerFruitsDeMer()

FabriqueIngredientsPizzaStrasbourg
creerPate() creerSauce() creerFromage() creerLegumes() creerPoivrons() creerFruitsDeMer()

<<interface>> Fromage

Mozzarella

ParmigianoReggiano

<<interface>> Moules

La tche des fabriques concrtes consiste fabriquer les ingrdients des pizzas. Chaque fabrique sait comment crer les objets qui correspondent sa rgion.

MoulesSurgelees

MoulesFraiches

Chaque fabrique produit une implmentation diffrente de la famille de produits.

xii

le pattern Singleton
Des objets uniques en leur genre
Notre prochain arrt est le Pattern Singleton notre passeport pour
la cration dobjets uniques en leur genre dont il nexiste quune seule instance. Vous allez srement tre ravi dapprendre que, parmi tous les patterns, le Singleton est le plus simple en termes de diagramme de classes. En fait, ce diagramme ne contient quune seule classe! Mais ne vous mprenez pas: malgr sa simplicit du point de vue de la conception des classes, nous allons rencontrer pas mal de bosses et de nids de poule dans son implmentation. Alors, bouclez vos ceintures. Un objet et un seul Le Petit Singleton Dissquons limplmentation du Pattern Singleton Confessions dun Singleton La fabrique de chocolat
Allo, le QG ?

170 171 173 174 175 177 178 179 180 184 186 188

Le pattern Singleton: dfinition

Houston Nous avons un problme...

Vous tes la JVM Grer le multithread Singleton: questions et rponses Votre bote outils de concepteur Solutions des exercices

O lPatterns O elation ille ear und fa it ef inm init-und x eteesurs,

f e si intere u d de r le e u pulu d farcf -e h t c un a -in na tg t ha u rs,vab cy eie n et ac sm gt s p nin Str e iee a it t O e ace he ir psu g c a e a t r c d a t u f n t r e n e s D t je n e t S b u a e o m je s. r b n it l n o h le n u in p io b e c it e pnD a f .n sse na et uu a g tr see gorenD nit t sq urec ic m a nis snd r r ,n in h e,a iq e lo cu t n b d r rq m ,t n ao io e e e a b il t rie F p o b is je in au yd va a a d nea s d d ic m u ne r e e n e b re ap ia o nd .uix ue je f ud sla m s io t u b q h les d F nt tx jo o e q r it f a n is r r il o it u e c e o is t t d lg c r h u d n a m c la e l s n a l u i r t t r io lt le u o a u n e t q g a t d o s j ie ta point pt e n es le f s et b r t ix ss o m c n n u io la ea ht t cso ne,n li t c la perd t c te u n sso esn r lu s ue le un ou me n i et uor o g s r so o d p if eit io in p ss t e x t u S o n t n la n a a n e c u io iv m t t e t t sr c a am n m e sa ic ie a r so d r is m e so b st p x r la a pened u in la e nIl n a p ala ta.un io .F t nse t n et ance. a eioin ier e m tt sa ic c nst ule li e r t is b n iq n c a iq u st ia Fc te n a c in .e lio r nea r a a p t b ie om su st c n ta lo eq n n g in io a o aun ivss l t e f t st p r ia cla y c in s u n t a le s e edeed dlg r linst ss ds c css cnla n e erd d la ca et pou ees.e dlgue ru ssss la c e n u la c ustsd..esdso es. es sous-class

xiii

table des matires

6
Note
age omse free au Ch rth wi erge rgur Bumb Ha ake Malt Sh ke Milk-sha

le pattern Commande
Encapsuler linvocation
Dans ce chapitre, nous allons envisager lencapsulation un tout autre niveau: nous allons encapsuler linvocation des mthodes. Oui, en encapsulant les appels de mthodes, nous allons pouvoir
cristalliser des traitements afin que lobjet qui les invoque nait pas besoin de savoir comment il sont excuts: il suffit quil utilise nos mthodes cristallises pour obtenir un rsultat. Nous pourrons mme faire des choses drlement futes avec ces appels de mthodes encapsuls, par exemple les sauvegarder pour la journalisation ou les rutiliser pour implmenter des annulations dans notre code. Maisons de rve La tlcommande 192 193 194 197 198 199 201 203 206 208 210 212 215 220 224 225 228 229 230 232

e est compose dun La Commandeier et des lments feuille de pap inscrits dessus. sont qui u men du
pas ser Co mm and

Je prendrai un Hamburger au fromage et un milk-shake

Un coup dil aux classes des fournisseurs Pendant ce temps, la caftria... tudions les interactions Rles et responsabilits
rt pa D

e()

pren

dreC

omm

ande

()

Le client sait ce quil veut, et il passe une commande


La Serveuse prend la Commande. Quand elle a termin, elle appelle sa mthod e faireMarcher() pour lancer la prparation de la Commande.

De la Caftria au pattern Commande Notre premier objet de commande Le pattern Commande: dfinition Le pattern Commande et la tlcommande Implmenter la tlcommande Tester la tlcommande Il est temps de rdiger cette documentation... Utiliser un tat pour implmenter une annulation Toute tlcommande a besoin dun mode group! Utiliser une macrocommande Autres utilisations de Commande: files de requtes Autres utilisations de Commande: journalisation des requtes Votre bote outils de concepteur Solutions des exercices

Note

xiv

mande es La Com nt tout contiest ructions les in aires pour s. ss nce er le repa prpar nne des er Elle do au Cuisini s ordresdes mthode avec que telles amburger(). faireH

e fromag ger au Cheese Hambur er with Burg ke Milk-sha Malt Shake

r fai

eM

ar

ch

er

()

faireHamburger(), faireMilkshake()

Le Cuisinier suit les instructions de la Commande et confectionne le repas.

su

lta

7
Prise murale europenne

les patterns Adaptateur et Faade


Savoir sadapter
Dans ce chapitre, nous allons entreprendre des choses impossibles, comme faire entrer une cheville ronde dans un trou carr. Cela vous semble impossible ? Plus maintenant avec les design patterns. Vous
souvenez-vous du pattern Dcorateur ? Nous avons envelopp des objets pour leur attribuer de nouvelles responsabilits. Nous allons recommencer, mais cette fois avec un objectif diffrent: faire ressembler leurs interfaces quelque chose quelles ne sont pas. Pourquoi donc ? Pour pouvoir adapter une conception qui attend une interface donne une classe qui implmente une interface diffrente. Et ce nest pas tout. Pendant que nous y sommes, nous allons tudier un autre pattern qui enveloppe des objets pour simplifier leur interface. Nous sommes entours dadaptateurs Adaptateurs orients objet Le pattern Adaptateur expliqu Le pattern Adaptateur: dfinition 236 237 241 243 244 247 248 249 252 255 258 261 264 265 270 272

Adaptateur CA

Adaptateurs de classe et adaptateurs dobjet Face--face: lAdaptateur dobjet et lAdaptateur de classe Adaptateurs du monde rel Adapter une Enumeration un Iterator Face--face: Dcorateur et Adaptateur Home Cinma Lumires, Camra, Faade! Construire la faade de votre home cinma Le pattern Faade: dfinition Ne parlez pas aux inconnus Votre bote outils de concepteur Solutions des exercices

Fiche CA US

Adapt Client
requ ete()

ue req

rad teT

uite

()

Le Client est implment selon linterface cible.

Adaptateur

interfa ce adapt

inte

e rfac

cible

LAdaptateur implmente linterface cible et contient une instance de lAdapt.

Dindon tait linterface de ladapt

xv

table des matires

8
Nous avons reconnu que les deux recettes taient globalement identiques, mme si certaines tapes demandaient des implmentations Th e diffrentes. Nous avons donc gnralis la u lea de illir recette et nous lavons 1 Faire bou dans leau place dans la classe mper le sachet de base. 2 Faire tre
3 4
1

le pattern Patron de mthode


Encapsuler les algorithmes
Nous narrtons pas dencapsuler. Nous avons encapsul la cration dobjets et linvocation de mthodes. Nous avons encapsul des interfaces complexes, des canards, des pizzas... Par quoi pourrions nous continuer? Nous allons entreprendre dencapsuler des
fragments dalgorithmes afin que les sous-classes puissent sy adapter au moment de leur choix. Nous allons mme dcouvrir un principe de conception inspir par Hollywood. Des classes pour prparer le caf et le th Puis-je transformer votre caf en abstraction? Approfondir la conception
C af e
Faire bo uil lir de

277 280 281 282 285 286 287 288 289 290 292 293 294 296 297 299 300 301 302 304 306 307 308 311 312

Abstraire suivreRecette()
le au

Fil trer le Ve rse r le Ajou te r

caf une tass t e

Quavons-nous fait? Faites connaissance avec Patron de mthode Prparons un peu de th Que nous a apport le Patron de mthode ? Le pattern Patron de mthode: dfinition

dan Verser le th Ajo ute r du citr

s une tas se

on

caf dans

du sucre

et du lai

BoissonCafeinee
gnralise
1 2

Faire bouillir de leau Prparer Verser la boisson dans une tasse Ajouter des supplments

gnralise

sappuie sur les sousclasses pour certaines tapes

3 4

sappuie sur les sous-classes pour certaines tapes

Sous-classe

The

Sous-cla

sse Cafe

Code la loupe Mthodes adaptateurs et Patron de mthode... Utiliser la mthode adaptateur Caf? Th? Excutons le test Le principe dHollywood Principe dHollywood et Patron de mthode Patrons de mthode ltat sauvage Trier avec Patron de mthode Nous avons des canards trier... Comparer des canards et des canards Les secrets de la machine trier les canards Les adaptateurs de Swing Applets Face--face: Patron de mthode et stratgie Votre bote outils de concepteur Solutions des exercices

2 4

Faire tremper le sache t Ajouter du citron

dans leau

connat BoissonCafeineetap es de et contrle les ise les la recette et ral mme, tapes1 et3 elle mais elle sen remet les The ou Cafe .pour tapes2 et4

2 4

Filt rer le caf Ajo ute r du suc re et du lait

xvi

9
Tous les menus
MenuC

les patterns Itrateur et Composite


Des collections bien gres
Il y a des quantits de faon de placer des objets dans une collection. Tableaux, piles, listes, tables de hachage: vous avez le choix. Chacune
a ses avantages et ses inconvnients. Mais il y aura toujours un moment o votre client voudra oprer des itrations sur ces objets. Allez-vous alors lui montrer votre implmentation? Esprons que non! Ce serait un manque de professionnalisme absolu. Eh bien vous navez pas besoin de risquer votre carrire. Vous allez voir comment autoriser vos clients procder sans mme jeter un coup dil la faon dont vous stockez vos objets. Vous apprendrez galement crer des super collections dobjets qui peuvent parcourir des structures de donnes impressionnantes dun seul trait. Et si cela ne suffit pas, vous allez dcouvrir une chose ou deux sur les responsabilits des objets. Fusion de la Caftria et de la Crperie dObjectville Les implmentations de Lon et Nol Pouvons-nous encapsuler litration? Faites connaissance avec le pattern Itrateur Ajoutons un itrateur MenuCafeteria Examiner la conception actuelle Une conception plus propre avec java.util.Iterator
Hashtable

316 318 323 325 326 331 333 335 336 339 348 349 353 356 359 362 368 372 374 380 381

Din

ie

er Me

M en uB

nu

ra ss er ie

Voici notre Arraylist qui contient les menus de chaque restaurant.

er rep

Menu Creperie

Menu Brasserie
Menu Cafeteria
M en uI
te
m

enu

M
It em

en uIt

M
em

enu

It em

en

uIt em

1
Pla

Tableau
t
Pla

en

uI tem

2
t

en

uI te m

ArrayList

Dessert Menu
1
M

M en uI

t em

O cela nous amne-t-il? Le pattern Itrateur: dfinition Une seule responsabilit Itrateurs et collections Itrateurs et collections en Java 5 Juste au moment o nous pensions avoir termin... Le pattern Composite: dfinition Concevoir des menus avec Composite Implmenter le Composite Flash-back sur Itrateur Litrateur nul La magie dItrateur et Composite en duo... Votre bote outils de concepteur Solutions des exercices

4
en
uI te

Pla
m

2
M

en uI

te m

3
M

en uI

te m

4
M
en
uI tem

Il faut que MenuCafeteria contienne un sousmenu, mais on ne peut pas affecter un menu un tableau de plats: cela ne peut pas fonctionner, parce quil sagit de deux types diffrents.

xvii

table des matires

10
Distribon, SARL
Le monde dans lequel les distributeurs ne sont jamais vides

le pattern Etat
Ltat des choses
Un fait mconnu: les patterns Stratgie et tat sont des jumeaux qui ont t spars la naissance. Comme vous le
savez, le pattern Stratgie a cr une affaire des plus florissantes sur le march des algorithmes interchangeables. Quant lui, tat a peut-tre suivi une voie plus noble: il aide les objets contrler leur comportement en modifiant leur tat interne. On le surprend souvent dire ses clients Rptez aprs moi: Je suis bon, je suis intelligent, ma simple prsence suffit... Comment implmenter un tat? 387 388 390 394 396 399 401 402 410 411 417 420 423 424

e Plus bd s bon on

ition dans les mentionner une trans Nous avons oubli de ine... n de remplir la zil nous faut un moye Pouve spcifications dorig nouveau diagramme.trava le Voici ! vide est elle il quand machine du si bon fait avez Vous ? nous pour vous limplmenter ibuteur: nul doute que vous pourrez ajouter sur le reste du distr ! cette fonction en un clin dil - Les ingnieurs de Distribon remplir

Machines tats: premire mouture Premier essai de machine tats Vous vous y attendiez... une demande de changement! Nous sommes dans un drle dtat... Dfinir linterface et les classes pour les tats Implmenter les classes pour les tats Retravaillons le distributeur Le pattern tat: dfinition

ce

A unee pic

tou
pice

rne

insrer pi

rp

oig

ne

de Pas c pi e
bon
bonbo ns = 0

jecter
n Bonbo vendu

bon

s>

r dlivre bonbon

tat vs Stratgie Bilan de sant Nous avons failli oublier! Votre bote outils de concepteur Solutions des exercices

xviii

11

le pattern Proxy
Contrler laccs aux objets
Avez-vous dj jou gentil flic, mchant flic? Vous
tes le gentil policier et vous rendez volontiers service de bonne grce, mais comme vous ne voulez pas que qui que ce soit vous sollicite, vous demandez au mchant de servir dintermdiaire. Cest l le rle dun proxy: contrler et grer les accs. Comme vous allez le voir, un proxy peut se substituer un objet dune quantit de faons. Les proxies sont connus pour transporter des appels de mthodes entiers sur lInternet la place dautres objets. On les connat galement pour remplacer patiemment un certain nombre dobjets bien paresseux. Contrler les distributeurs Le rle du proxy distant Dtour par RMI Un Proxy pour le distributeur Les coulisses du proxy distant Le pattern Proxy: dfinition Prts pour le Proxy virtuel? Concevoir le proxy virtuel pour les pochettes de CD Les coulisses du proxy virtuel Utiliser la classe Proxy de lAPI Java Comdie express: des sujets sous protection Crer un proxy dynamique Le zoo des proxys Votre bote outils de concepteur Solutions des exercices
<<interface>> Sujet

Non Sexy

430 434 437 450 458 460 462 464 470 474 478 479 488 491 492

<<interface>> InvocationHandler

requte()

invoke()

maintenant Le Proxy estdeu x classes. constitu de


RealSubject requte()

Proxy requte()

InvocationHandler invoke()

xix

table des matires

12
Le tempo est rgl 119 BPM et vous voudriez laugmenter 120..

patterns composs
Patterns de patterns
Qui aurait cru que les patterns taient capables de collaborer? Vous avez dj t tmoin de lanimosit qui rgne dans les face-face (et vous navez pas vu le combat mort que lditeur nous a forcs retirer de ce livre). Alors qui aurait pu penser que des patterns pourraient finir par sentendre? Eh bien, croyez-le ou non, certaines des conceptionsOO les plus puissantes font appel plusieurs patterns. Apprtez-vous passer la vitesse suprieure: il est temps dtudier les patterns composs.

Patterns composs Runion de famille Ajouter un adaptateur


Cliquez sur ce bouton

500 501 504 506 508 513 516 523 524 526 528 532 534 537 539 542 545 546 547 549 557 560 561

Ajouter un dcorateur Ajouter une fabrique Ajouter un composite et un itrateur Ajouter un observateur

Vue

...ce qui a pour rsultat dinvoquer le contrleur.

Contrleur
Vous voyez la barre pulser deux fois par seconde

Le contrleur demande au modle dincrmenter les BPM de un.

Rsum des patterns Vue plongeante: le diagramme de classes Modle-Vue-Contrleur, la chanson Les design patterns sont la cl de MVC Mettons nos lunettes spciales patterns Contrler le tempo avec MVC... Le Modle La Vue Le Contrleur Explorer le pattern Stratgie Adapter le modle Maintenant, nous sommes prts contrler un cur MVC et le Web Design patterns et Model 2 Votre bote outils de concepteur Solutions des exercices

Vue

Comme les BPM sont rgls 120, la vue reoit une notification toutes les demi secondes.

deleT Mo rche() emp


ma

setBPM()

getBPM

arrt() ()

La vue est mise jour et affiche 120BPM

La vue est informe que les BPM ont chang. Elle appelle .. getBPM() sur ltat du modle

xx

13

mieux vivre avec les patterns


Les patterns dans le monde rel
Maintenant, vous tes prt vivre dans un monde nouveau, un monde peupl de Design Patterns. Mais avant de
vous laisser ouvrir toutes ces nouvelles portes, nous devons aborder quelques dtails que vous rencontrerez dans le monde rel. Eh oui, les choses y sont un peu plus complexes quelles ne le sont ici, Objectville. Suivez-nous! Vous trouverez la page suivante un guide extra qui vous aidera effectuer la transition... Le guide dObjectville Design Pattern: dfinition Regardons la dfinition de plus prs 578 579 581 582 583 586 587 589 594 597 599 600 601 602 603 604 606 608 609

z: contient de nombre isi notre guide. Il rel. Dans ce guide, vous alle Merci davoir cho terns dans le monde nition pat les c ave e vivr rantes sur la dfi cou s plu les sses ides fau e quelles sont les b Apprendr pattern. prendre dun design de patterns et com erbes cataloguescurer un. pro ir quil existe de sup b Dcouvr s devez absolument vous en pourquoi vou n design pattern. ploi inopportun du te associe lem hon la nent. r vite E b patterns appartien lles catgories les reconnatre que e ndr lisez pre s; Ap rou b rserve aux gouterns. patterns nest pas la dcouverte dedevenez vous aussi un auteur de pat b Voir que et ide quatre notre rfrence rap strieuse Bande des identit de la my t quand la vraie sen pr re Et b t sera rvle. es de chevet que tou distancer les livr pas vous laisserpos ne e er. ndr sd pre b Ap terns se doit de utilisateur de pat . me un matre zen rcer votre esprit com exe e ndr vos collgues b Appre ner ion ress amis et imp nt vous faire desvocabulaire. me com ir uvr co b D liorant votre dveloppeurs en am

tville Le guide dObjec esign Patterns les D Mieux vivre avec s pour ux trucs et astuce

Que la force soit avec vous Catalogues de patterns Comment crer des patterns Donc, vous voulez tre auteur de patterns? Organiser les design patterns Penser en termes de patterns Votre esprit et les patterns Noubliez pas le pouvoir du vocabulaire partag Les cinq meilleures faons de partager votre vocabulaire Tour dObjectville avec la Bande des quatre Votre voyage ne fait que commencer... Autres ressources sur les design patterns Le zoo des patterns Annihiler le mal avec les anti-patterns Votre bote outils de concepteur

Richa

rd He

lm
Ralph Johnson

Quitter Objectville...

La bande des quatre


Erich Gamma

John Vlissides

xxi

table des matires

14

Annexe: Les patterns restants


La popularit nest pas donne tout le monde.Beaucoup
de choses ont chang ces dix dernires annes. Depuis la parution de Design Patterns: Catalogue de modles de conception rutilisables, les dveloppeurs ont appliqu ces patterns des milliers de fois. Ceux que nous avons rsums ici sont des patterns GoF part entire. Bien quils soient tout aussi officiels que ceux que nous avons tudis jusqu maintenant, leur emploi est moins frquent. Mais ils sont tout aussi remarquables, et, si la situation le demande, vous pouvez les utiliser la tte haute. Dans cette annexe, notre objectif est de vous donner une vue densemble de ces patterns.

Le Client demande au Visiteur dobtenir des informations sur la structure du Composite... On peut ajouter de nouvelles mthodes au Visiteur sans affecter le Composite.

r geLe Visiteur doit pouvoir ,appele et cest l tEtat() entre les classes les nouvelles que vous pouvez ajouterutilise ra. mthodes que le client
() ale ob Gl () ote ries s() N t o e l ge tCa tein s() ge tPro cide ge tGlu ge

Les classes du Composite nont rien dautre faire que dajouter une mthode getEtat() (et ne pas se soucier de sexposer elles-mmes).

Pont Monteur Chane de responsabilit Poids-mouche Interprte Mdiateur Mmento Prototype Visiteur

612 614 616 618 620 622 624 626 628

te() getSta

Menu

Visitor

getState() getS tate () ge tS ta Plat te ()

Plat

e() tat tS ge

Client / vigateur

Na-

nt Le Navigateur sait comme la guider le Visiteur dans structure du Composite.

Ingredient

Ingredient

i
xxii

Index

631

comment lire ce livre

Intro

Je narrive pas croire quils aient mis a dans un livre sur les design patterns!

Ce livre est-il pour vous?


Ce livre sadresse quiconque ayant suffisamment dargent pour le payer. Et il fera un trs beau cadeau.

: us brle les lvres vo i qu n io t es qu ons la ? ion, nous rpond ct se e t t s design patterns ce le s r an su D e ag vr ou un -ils mis a dans Pourquoi ont

xxiii

comment lire ce livre

qui sadresse ce livre?


Si vous rpondez oui ces trois questions:
1 2
Connaissez-vous Java? (Inutile dtre un gourou.)

ez C#, Si vous connaiss i. ss cest bien au

Voulez-vous apprendre, comprendre, mmoriser et appliquer les design patterns, ainsi que les bons principes de conception sur lesquels ces patterns sappuient? Prfrez-vous une conversation stimulante autour dun bon dner un cours bien ennuyeux et bien aride?

cet ouvrage est pour vous.

Et qui nest-il pas destin?


Si vous rpondez oui toutes ces questions:
1
tes-vous totalement dbutant en Java? (Vous navez pas besoin dtre trs avanc, et, mme si vous ne connaissez pas Java mais uniquement C#, vous comprendrez sans doute au moins 80% des exemples de code. Une exprience en C++ pourrait galement suffire.) tes-vous un concepteur ou un dveloppeur OO press recherchant un ouvrage de rfrence? tes-vous un architecte la recherche de design patterns dentreprise? Avez-vous peur dessayer quelque chose de diffrent? Prfreriez-vous vous faire arracher une dent plutt que de mlanger des carreaux et des rayures? Croyez-vous quun ouvrage technique ne peut pas tre srieux si les composants Java y sont anthropomorphiss?

Ce livre nest pas pour vous.

[note du marketing: ce livre est des personne possdant une carte de cr tin toute dit.]
xxiv
intro

lintro

Nous savons ce que vous pensez.


Comment ce livre peut-il tre srieux? quoi riment toutes ces images? Est-ce quon peut vraiment apprendre de cette faon? Je mangerais bien une pizza.

Votre er veau pe se CECI esct que importan nt.

Et nous savons ce que votre cerveau pense.


Votre cerveau est avide de nouveaut. Toujours lafft, il ne cesse de chercher et dattendre quelque chose dinhabituel. Cest comme a quil est construit, et cest comme a quil vous aide rester en vie. Aujourdhui, il est peu probable que vous serviez de petit-djeuner un tigre. Mais votre cerveau reste sur ses gardes. On ne sait jamais Alors que fait-il de toutes les choses ordinaires, routinires et banales que vous rencontrez? Il fait tout ce quil peut pour les empcher dinterfrer avec son vrai travail: enregistrer ce qui compte. Il ne se soucie pas de mmoriser les choses ennuyeuses; il considre a priori quelles nont aucune importance et il les filtre. Mais comment votre cerveau sait-il ce qui est important? Supposons que vous partiez en excursion pour la journe et quun tigre surgisse devant vous. Que se passe-t-il dans votre tte? Les neurones sembrasent. Les motions vous submergent. Les hormones dferlent. Et votre cerveau ne sait rien dautre

Gnial. Plus que 637 pages mourir dennui.

cerveauLA Votre q e CE t. pense sud 'intr n'a pa

Cela peut tre important ! Ne loubliez pas !


Mais imaginez que vous tes chez vous ou dans une bibliothque. Cest un lieu sr et chaleureux. Pas le moindre tigre lhorizon Vous tes en train dtudier. De rviser un examen. Ou dessayer de comprendre un sujet bien technique dont votre patron vous a dit quil vous prendra huit dix jours au plus. Seul problme: votre cerveau essaie de vous faire un gros cadeau. Il essaie de faire en sorte que des contenus videmment sans importance npuisent des ressources dj rares. Des ressources mieux employes mmoriser les choses vraiment essentielles. Comme les tigres. Comme les incendies. Comme le danger quil y a faire du snowboard en short. Et il ny a a aucun moyen simple de lui dire: H, cerveau, merci bien, mais mme si ce livre est ennuyeux comme la pluie, et mme si jai le moral zro, je veux que tu fasses attention.
vous tes ici

xxv

comment lire ce livre

mme un appren co r eu ct le re ot n s n ro ous consid

ant.

up lap ts, et facilitent beauco ent galemmoriser que les mo et le transfert). Elles aid pel rap le sur des doCalc() tu les s dan n atio lior m da re. nd pre com ux ment mie la porte, et non la fin de limage auquel il se rap return value plus vite les fois x deu Placer le texte prs de dre ou rs t aux apprenants de me per , loin s plu ou e pag tenus. problmes lis aux con ences s. De rcentes expri tionnel et personnali sa sadressant er te nv tex co un le eux sty mi Adopter un rappelaient beaucoup se nts dia i avaient lu un tu qu des ets e suj ont montr qu versation que des con la de ton le t suprieures sur performances taien directement eux plus formaliste. Leurs on gage de fa lan de le t ler sen par , pr te fier tex une histoire que ponti ter on re rac le plus t dt e us vau ribl -vo eux ter tez t Ces de 40%. Mi p au srieux. qui pr ite. une mthode abstra viter de se prendre tro rs, ? jou ier les enc s fr tou s dnez ou un con On na pas de corps. : lami avec qui vou dattention rones, il ne se passe pas pas activement vos neu cez xer ne s vou Si ir. Faire rflch tiv, engag, curieux. Il Un lecteur doit tre mo e. tt re vot s et de dan se grand cho de tirer des conclusions Bains oudre des problmes, s, Est-ce quune Salle de dfi de oin bes a doit avoir envie de rs il e, ? Ou bien ssances. Pour ce fair nai EST-UNE Baignoire con lles uve sol no i qu de r its N ? gnre ir et dactiv est-ce une relation A-U s qui incitent rflch sens. urs sie plu dexercices, de question el app t hres crbraux et fon licitent les deux hmisp du lecteur. Nous er ver lattention Capter et cons nt lire ce livre, mais ime vra x veu e J r avons tous pens un jou e. Votre cerveau pag re abstract void roam(); ds la premi il me tombe des mains i attire lil, ce i sort de lordinaire, qu prte attention ce qu dun nouveau sue tud L u. nd tte intressant, trange, ina e est i qu d fastidieuse. Votre tre s cile, na pas besoin d e corp technique, mme diffi jet ennuyez pas. s vou Pas d ne s ! vou e si ent od par un apprendra plus facilem u vea mthm cer r e . aptiTer tin virgule s maintenant que notre poin - Faire appel aux motions. Nous savon de son contenu motionnel. ent vous ressentez quelque ue chose dpend largem s vous rappelez quand tude mmoriser quelq Vou s. vou ur po chien. Nous pte ce qui com c un petit garon et son Vous vous souvenez de ires fendre le cur ave puissance que isto tedh tou pas de s ion lon sat par chose. Non, nous ne lamusement ou la sen it, ios cur la t le monde se, tou pri e qu sur me la ez quelque chose parlons dmotions com puzzle, que vous appren se que les un cho ez ue olv elq rs qu s ez vou sav e s vous prouvez lorsqu dez compte que vou ren s vou s vou e squ lor cile, ou considre comme diffi pas. super-gourous ne savent

re, puis faire t dabord comprend fau Il e? os ch ue elq n qu e de force. Les Comment apprend-o pas de remplir sa tt git sa ne hologie Il . er bli ou obiologie et en psyc en sorte de ne pas cognitives, en neur s ce te ien tex sc du en e es qu ch e os dernires recher sage exige autre ch ent que lapprentis de lducation montr . au tre cerve ns ce qui stimule vo imprim. Nous savo : Besoin d'appeler de nos principes Voici quelques-uns une mthode sur le iles fac s plu up uco bea t stant serveur % ualiser. Les images son service RMI di Permettre de vis prentissage (jusqu 89

xxvi

intro

lintro

Mtacognition: apprendre apprendre


Si vous voulez rellement apprendre, plus vite et plus en profondeur, soyez attentif la manire dont vous faites attention. Rflchissez la faon dont vous rflchissez. Apprenez apprendre. La plupart dentre nous nont pas suivi de cours sur la mtacognition ou la thorie de lapprentissage lorsque nous tions lcole. Nous tions censs apprendre, mais on nous disait rarement comment faire. Mais si vous avez ce livre entre les mains, nous supposons que vous voulez apprendre Java. Et vous ne voulez probablement pas y consacrer une ternit. Pour tirer le meilleur parti de cet ouvrage, dun autre livre ou de toute exprience dapprentissage, apprenez matriser votre cerveau. Lastuce consiste lamener considrer ce que vous allez apprendre comme quelque chose de rellement important, de capital pour votre bien-tre, daussi important quun tigre. Sinon, vous tes vou un combat sans fin: votre cerveau fera tout son possible pour vous empcher de mmoriser.
Quest-ce que je vais bien pouvoir trouver pour faire rentrer ce truc dans ma tte?

Mais comment FAIRE au juste pour que votre cerveau traite Java comme un tigre affam ?
Il y a deux faons de procder: lune lente et ennuyeuse, lautre rapide et efficace. La solution lente consiste purement et simplement rpter. Vous savez certainement que vous tes capable dapprendre et de mmoriser le plus ingrat des sujets si vous rptez sans arrt la mme chose. Au bout dun nombre suffisant de rptitions, votre cerveau pense: a ne me semble pas important pour lui, mais sil ressasse ce truc, cest que a doit ltre. La voie rapide consiste faire tout ce qui non seulement augmente mais aussi diversifie lactivit crbrale. Les lments de la page prcdente constituent une grande partie de la solution, et ils se sont tous rvls capables daider votre cerveau travailler en votre faveur. Certaines tudes dmontrent que linsertion de mots dans les images quils dcrivent (et non dans des lgendes ou dans le corps du texte) force le cerveau tenter de comprendre les relations entre le texte et limage, et provoque des connexions neuronales plus nombreuses. Plus ces connexions se multiplient, plus il a de chances de comprendre que le contenu est digne dattention et mrite dtre retenu. Un style conversationnel aide galement. Les gens on tendance faire plus attention une conversation, puisquils sont censs la suivre et ventuellement y prendre part. Chose surprenante: votre cerveau ne se soucie pas ncessairement de savoir que vous conversez avec un livre! En revanche, si le style est aride et formaliste, il le peroit exactement comme celui dun confrencier prorant devant une salle pleine dauditeurs passifs. Inutile en ce cas de rester veill. Mais les graphismes et le ton de la conversation ne sont quun dbut.

vous tes ici xxvii

comment lire ce livre

Voici ce que NOUS avons fait :


Nous avons utilis des images, parce que votre cerveau les prfre au texte. Pour lui, une seule dentre elles vaut rellement mieux que 1024 mots. Et, lorsque texte et image vont de pair, nous avons fusionn le texte et limage, parce que votre cerveau fonctionne mieux lorsque les mots sont intgrs ce quil dcrivent plutt que placs dans une lgende ou enfouis quelque part dans la page imprime.
RELATION UN--PLUSIEURS Objet qui contient un tat
O

Nous avons utilis la rptition, en exprimant la mme chose de diffrentes manires, en recourant Mise jour et notification automatiques divers types de supports et en faisant appel plusieurs sens, pour augmenter les chances que le contenu soit encod dans plusieurs zones de votre cerveau. Nous avons utilis concepts et images de faon inattendue, parce que votre cerveau aime la nouveaut. Nous avons ajout au moins un peu de contenu motionnel, parce que votre cerveau prte attention la biochimie des motions. Cest ce qui vous aide mmoriser, mme si ce nest d rien dautre quun peu dhumour, de surprise ou dintrt. Nous avons utilis un style conversationnel, personnel, parce que votre cerveau est plus enclin tre attentif quand il croit que vous tes engag dans une conversation que lorsquil pense que vous assistez passivement une prsentation. Il fait de mme quand vous lisez. Nous avons inclus plus de 50 exercices, parce que votre cerveau apprend et retient mieux quand vous faites quelque chose que lorsque vous lisez. Ces exercices sont difficiles mais faisables parce que cest ce que la plupart des gens prfrent. Nous avons fait appel plusieurs styles dapprentissage, parce que vous prfrez peut-tre les procdures pas--pas, alors que quelquun dautre voudra dabord saisir limage globale et quun troisime lecteur cherchera simplement un exemple de code. Mais, indpendamment de ses prfrences, chacun peut tirer parti de la vision dun mme contenu diversement prsent. Nous avons inclus des contenus qui sadressent aux deux hmisphres de votre cerveau: plus ce dernier est engag, plus vous tes mme dapprendre, de mmoriser et de vous concentrer plus longtemps. Comme faire travailler lun des cts du cerveau permet souvent lautre de se reposer, la priode durant laquelle votre apprentissage est productif est plus longue. Nous avons galement incorpor des histoires et des exercices qui prsentent plusieurs points de vue, parce que votre cerveau est ainsi fait que vous apprenez plus en profondeur sil est forc de faire des valuations et de porter des jugements. Enfin, nous avons inclus des dfis dans les exercices en posant des questions qui nont pas de rponse simple, parce que votre cerveau apprend et retient mieux lorsquil doit travailler (tout comme il ne suffit pas de regarder les autres la gym pour rester en bonne forme physique). Mais nous avons fait de notre mieux pour que, si vous devez travailler dur, ce soit sur les bons lments: pour que vous ne consacriez pas un neurone de trop traiter un exemple difficile comprendre, ni analyser un texte difficile, bourr de jargon et horriblement laconique. Nous avons fait appel des personnes dans les histoires, dans les exemples et dans les images, parce que vous tes une personne. Et votre cerveau sintresse plus aux personnes quaux choses. Nous avons adopt une approche 80/20. Si vous visez un doctorat en Java, nous supposons que vous ne lirez pas que ce livre. Cest pourquoi nous ne parlons pas de tout, mais seulement de ce que vous utilisez rellement.

Objet C

Objet C

h at

Objet S

Observateurs

Le gourou des patterns

POINTS DIMPACT

Problmes de conception

xxviii

intro

Objets d

o u r is

pendants

a n ar d

bje int je t Su

8 8

Objet C

h ie

Voici ce que VOUS pouvez faire pour dompter votre cer veau.
dcouper et coller sur le fr igo
1
Prenez votre temps. Plus vous comprenez, moins vous avez mmoriser.

lintro

Nous avons donc fait notre part. Le reste dpend de vous. Les conseils qui suivent sont un point de dpart. coutez votre cerveau et dterminez ce qui fonctionne pour vous et ce qui ne fonctionne pas. Essayez de nouvelles techniques.

Buvez beaucoup deau.

Ne vous bornez pas lire. Arrtez-vous et rflchissez. Quand vous voyez une question, ne vous prcipitez pas sur la rponse. Imaginez que quelquun la pose rellement. Plus vous forcez votre cerveau penser, plus vous avez de chances dapprendre et de retenir.
2
Faites les exercices. Prenez des notes.

Votre cerveau fonctionne mieux lorsquil est convenablement irrigu. La dshydratation (qui peut apparatre avant la soif) diminue les fonctions cognitives.
7
Parlez-en voix haute.

Si nous les avions faits pour vous, ce serait comme de demander quelquun de faire de la gym votre place. Et ne vous contentez pas de les lire. Prenez un crayon. Tout montre que lactivit physique durant lapprentissage permet dapprendre plus.
3
Lisez Il ny a pas de questions stupides .

La parole active une autre partie du cerveau. Si vous essayez de comprendre quelque chose ou daugmenter vos chances de vous le remmorer plus tard, dites-le haute voix. Mieux encore, essayez de lexpliquer quelquun. Vous apprendrez plus vite et vous dcouvrirez peut-tre des ides qui vous ont chapp la lecture.
8
coutez votre cerveau.

Et lisez tout. Ce ne sont pas des encadrs optionnels: ils font partie du contenu! Parfois, les questions sont plus utiles que les rponses.
4
Ne restez pas toujours la mme place.

Veillez ne pas le surcharger. Si vous vous surprenez lire en diagonale ou oublier ce que vous venez de lire, il est temps de faire une pause. Pass un certain point, vous napprenez pas plus vite en essayant den absorber davantage, et vous risquez mme daltrer le processus.
9
Ressentez quelque chose !

Levez-vous, tirez-vous, dplacez-vous, changez de chaise, changez de pice. Cela aide votre cerveau ressentir quelque chose et empche lapprentissage dtre trop associ un endroit particulier.
5
Faites de ce livre votre dernire lecture avant de dormir. Au moins la dernire chose difficile.

Votre cerveau a besoin de savoir que cest important. Impliquez-vous dans les histoires. Crez vos propres lgendes pour les photos. Sagacer dune mauvaise plaisanterie vaut mieux que ne rien ressentir du tout.

10 Concevez quelque chose! Appliquez ce que vous apprenez une nouvelle conception ou la rorganisation dun ancien projet. Une partie de lapprentissage (surtout le transfert dans Faites quelque chose, afin dacqurir une exprience qui la mmoire long terme) aura lieu aprs que vous aurez dpasse les exercices et les activits de ce livre. Vous repos le livre. Votre cerveau a besoin de temps lui navez besoin que dun crayon et dun problme pour travailler plus. Si vous interposez quelque chose de rsoudre un problme qui pourrait bnficier de nouveau dans ce laps de temps, une partie de ce que vous lintroduction dun ou plusieurs design patterns. viendrez dapprendre sera perdue.
vous tes ici

xxix

comment lire ce livre

Lisez moi
Il sagit dune introduction aux design patterns, non dun ouvrage de rfrence. Nous avons dlibrment limin tout ce qui pouvait faire obstacle lapprentissage, quel que soit le sujet abord un moment donn. Et, lors de votre premire lecture, vous devrez commencer par le commencement, parce que ce livre tient compte de ce que vous avez dj vu et appris. Nous utilisons des diagrammes pseudo-UML simples. Mme sil y a de grandes chances que vous ayez dj rencontr UML, nous ne labordons pas dans ce livre et il ne constitue pas un prrequis. Si vous ne connaissez pas encore UML, pas de souci: nous vous donnerons quelques indications en temps voulu. Autrement dit, vous naurez pas tudier les Design patterns et UML en mme temps. Nos diagrammes ressemblent UML mme si nous essayons dtre fidles au standard, nous faisons parfois quelques petites entorses aux rgles, gnralement pour des besoins gostement artistiques. Nous nabordons pas tous les Design patterns de la cration. Il existe beaucoup de Design patterns: les patterns dorigine (connus sous le nom de patterns GoF), les patterns J2EE de Sun, les patterns JSP, les patterns architecturaux, les patterns spcifiques la conception de jeux et des quantits dautres. Mais comme notre objectif tait dcrire un livre qui ne pse pas trois tonnes, nous ne les abordons pas tous ici. Nous nous concentrerons donc sur les patterns fondamentaux les plus importants parmi les patterns GoF dorigine, et nous nous assurerons que vous comprenez vraiment, rellement et en profondeur quand et comment les utiliser. Vous trouverez galement en annexe un bref aperu des autres (ceux que vous tes moins susceptible dutiliser). En tout tat de cause, lorsque vous aurez termin Design patterns Tte la premire, vous serez capable de prendre nimporte quel catalogue de patterns et de vous y mettre rapidement. Les activits ne sont PAS optionnelles. Les exercices et les activits ne sont pas l pour dcorer: ils font partie intrinsque de ce livre. Certains sont l pour vous aider mmoriser, dautres comprendre et dautres encore pour vous aider appliquer ce que vous avez appris. Ne sautez pas les exercices. Les mots-croiss sont les seules choses que vous ntes pas oblig de faire, mais ils donneront votre cerveau une chance de rflchir aux mots dans un contexte diffrent. Nous employons le mot composition dans lacception gnrale quil a en OO, qui est plus large que son sens strict en UML. Lorsque nous disons un objet est compos avec un autre objet, nous voulons dire que ces deux objets sont lis par une relation A-UN. Notre emploi reflte lusage traditionnel du terme et cest celui de louvrage de la Bande des quatre (vous apprendrez ce que cest plus tard). Plus rcemment, UML a raffin ce terme pour distinguer plusieurs types de composition. Si vous tes expert en UML, vous pourrez toujours lire ce livre et faire facilement correspondre notre emploi du mot composition avec des concepts plus dtaills. xxx intro

isons un , un Nous utilU ML faux imp fi UML s li


MetteurEnScne
getFilms getOscars() getDegrsDeKevinBacon()

lintro

La redondance est intentionnelle et importante. Un livre Tte la premire possde un trait distinctif : nous voulons rellement que vous compreniez. Et nous voulons que vous vous souveniez de ce que vous avez appris lorsque vous le refermez. La plupart des ouvrages de rfrence ne sont pas crits dans cet objectif, mais, dans celui-ci, il sagit dapprendre. Cest pourquoi vous verrez les mmes concepts abords plusieurs fois. Les exemples de code sont aussi courts que possible. Nos lecteurs nous disent quel point il est frustrant de devoir parcourir 200lignes de code pour trouver les deux instructions quils ont besoin de comprendre. Ici, la plupart des exemples sont prsents dans le contexte le plus rduit possible, pour que la partie que vous voulez apprendre demeure simple et claire. Ne vous attendez donc pas ce que le code soit robuste, ni mme complet. Les exemples sont spcifiquement conus pour lapprentissage et ne sont pas toujours pleinement fonctionnels. Dans certains cas, nous navons pas inclus toutes les instructions import ncessaires: si vous programmez en Java, nous supposons que vous savez par exemple quArrayList se trouve dans java.util. Si ces instructions ne font pas partie du noyau de lAPI J2SE normale, nous le mentionnons. Nous avons galement publi tout le code source sur Web pour que vous puissiez le tlcharger. Vous le trouverez en anglais sur : http://www.wickedlysmart.com/headfirstdesignpatterns/code.html et en franais sur http://www.oreilly.fr//catalogue/2841773507.html De mme, pour nous concentrer sur laspect pdagogique du code, nous navons pas plac nos classes dans des packages (autrement dit, elles sont toutes dans le package Java par dfaut). Nous ne recommandons pas cette pratique dans le monde rel. Lorsque vous tlchargerez nos exemples de code, vous constaterez que toutes les classes sont dans des packages. Musclez vos neurones nont pas de rponse. Les exercices Pour certains, il nexiste pas de bonne rponse. Pour dautres, cest vous quil appartient de dcider si vos rponses sont justes ou non. Dans certains de ces exercices, vous trouverez des indications destines vous mettre dans la bonne direction. Terminologie de ldition franaise Dans cette dition, nous avons suivi pour lessentiel la terminologie de Jean-Marie Lasvergres dans sa traduction de louvrage dErich Gamma, Richard Helm, Ralph Johnson et John Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software, Boston: Addison-Wesley 1995, parue sous le titre Design patterns, catalogue de modles de conception rutilisables, Paris, Vuibert, 1999. Nous avons notamment repris les noms des patterns, leurs dfinitions officielles et les noms de leurs composants dans les diagrammes de classes. Vous trouverez nanmoins quelques exceptions cette rgle, notamment en ce qui concerne le pattern Proxy, et certains noms de classes comme Crateur pour Facteur (pattern Fabrication) ou Cible pour But (pattern Adaptateur), leur emploi tant devenu plus courant. De mme, nous avons adapt certains noms de mthode pour respecter les conventions de nommage Java.

vous tes ici

xxxi

lquipe des diteurs techniques

diteurs techniques
Jef Cumps

Valentin Crettaz
Barney Marispini

Ike Van Atta

Leader sans peur et sans reproche de lquipe de rvision de Design patterns Tte la premire.

Johannes deJong

Jason Menard

Mark Sprit

zler

Dirk Schreckmann
xxxii
intro

lintro

la mmoire de Philippe Maquet


1960-2004
Ton extraordinaire expertise technique, ton enthousiasme sans faille et ton profond respect des apprenants nous inspireront toujours Nous ne toublierons jamais.

Philippe Maquet

Remerciements
Chez OReilly: Nos remerciements les plus chaleureux Mike Loukides qui est lorigine du projet et a contribu transformer le concept Tte la premire en collection. Et un grand merci la force motrice derrire Tte la premire, Tim OReilly. Merci la maman de la collection, la talentueuse Kyle Hart, la star du rock and roll Ellie Volkhausen pour sa couverture inspire et Colleen Gorman pour sa prparation de copie impitoyable. Enfin, merci Mike Hendrickson pour stre fait le champion de cet ouvrage et pour avoir construit lquipe. Nos intrpides relecteurs : Nous sommes extrmement reconnaissants au directeur de la rvision technique, Johannes de Jong, Tu es notre hros, Johannes. Et nous ne dirons jamais assez quel point les contributions du co-responsable de lquipe de rviseurs de Javaranch, feu Philippe Maquet, a t prcieuse. Il a illumin lui tout seul la vie de milliers de dveloppeurs et limpact quil a eu sur leur existence (et les ntres) sera ternel. Jef Cumps est terriblement dou pour dtecter les problmes dans les premiers jets, et il a fait encore une fois une norme diffrence. Merci Jef ! Valentin Crettaz (tenant de la programmation oriente aspect), qui a t avec nous depuis le premier Tte la premire, a prouv (comme toujours) quel point son expertise technique et son intuition taient prcieuses. Tu es tonnant, Valentin (mais laisse tomber cette cravate). Deux nouveaux venus dans lquipe de rvision de TLP, Barney Marispini et Ike Van Atta, ont effectu un travail remarquable sur le livre vous nous avez fourni un feedback vraiment crucial. Merci davoir rejoint lquipe. Les modrateurs et gourous de Javaranch, Mark Spritzler, Jason Menard, Dirk Schreckmann, Thomas Paul et Margarita Isaeva, nous ont galement fourni une excellente aide technique. Et, comme toujours, un merci spcial au cow-boy en chef de javaranch.com, Paul Wheaton. Merci aux finalistes du concours de Javaranch: Votez pour la couverture de Design patterns Tte la premire. Si Brewster a gagn en soumettant lessai qui nous a persuads de choisir la jeune femme que vous voyez sur la couverture. Les autres finalistes taient Andrew Esse, Gian Franco Casula, Helen Crosbie, Pho Tek, Helen Thomas, Sateesh Kommineni et Jeff Fisher.
vous tes ici xxxiii

encore des remerciements

Ce nest pas fini*


De la part dEric et dElisabeth
crire un livre Tte la premire ressemble une quipe sauvage accompagne par deux guides tonnants, Kathy Sierra et Bert Bates. Avec Kathy et Bert, on jette toutes les conventions aux orties et on pntre dans un monde dans lequel on raconte des histoires, on se proccupe de thorie de lapprentissage, de sciences cognitives et de culture pop, et o le lecteur joue toujours le premier rle. Merci vous deux de nous avoir permis dentrer dans cet univers tonnant: nous esprons avoir fait honneur au concept Tte la premire. Srieusement, ctait une exprience hors du commun. Merci pour vos prcieux conseils, pour nous avoir pousss aller de lavant, et, par-dessus tout, pour nous avoir fait confiance (avec votre bb). Vous tes srement terriblement smart et vous tes aussi les trentagnaires les plus cools de notre connaissance. Un grand merci Mike Loukides et Mike Hendrickson. Mike L. nous a accompagns chaque tape du chemin. Mike, ton feedback perspicace nous a aids faonner le livre et tes encouragements nous ont aids continuer. Mike H., merci pour avoir persist pendant cinq ans nous convaincre dcrire un livre sur les patterns. Nous avons fini par le faire, et nous sommes heureux davoir attendu Tte la premire. Un merci trs spcial Erich Gamma, qui a fait bien plus que rpondre lappel du devoir en relisant ce livre (il en a mme emport une premire version en vacances). Erich, ton intrt nous a inspirs et ta relecture technique exhaustive la amlior sans commune mesure. Merci galement toute la Bande des quatre pour leur soutien et leur intrt et pour nous avoir rendu visite Objectville. Nous sommes galement redevables Ward Cunningham et la communaut des patterns qui ont cr le Portland Pattern Repository une ressource qui nous a t indispensable lorsque nous crivions ce livre. Il faut un village pour crire un ouvrage technique. Bill Pugh et Ken Arnold nous ont donn leur avis dexperts sur Singleton. Joshua Marinacci nous a fournis dexcellents conseils et astuces sur Swing. Larticle de John Brewer, Why a Duck?, nous a suggr lide du simulateur de canards (et nous sommes heureux quil aime lui aussi ces volatiles). Dan Friedman a inspir lexemple du Petit Singleton. Daniel Steinberg a jou le rle dagent de liaison technique et de rseau de soutien motionnel. Et merci James Dempsey de chez Apple pour nous avoir autoriss utiliser sa chanson sur MVC. Enfin, nous remercions personnellement lquipe de rviseurs de Javaranch pour leurs remarques de premier ordre et leur soutien chaleureux. Nous vous devons beaucoup plus que vous ne le pensez.

De la part de Kathy et Bert


Nous aimerions remercier Mike Hendrickson davoir trouv Eric et Elisabeth... mais cela nous est impossible. cause deux, nous avons dcouvert ( notre grande horreur) que nous ntions pas les seuls pouvoir crire un livre Tte la premire ;-). Mais si les lecteurs veulent croire que ce sont rellement Kathy et Bert qui ont fait les trucs sympas dans ce livre, qui sommesnous pour les dtromper?

De la part de Marie-Ccile
La traduction technique est souvent un exercice assez austre. Merci Dominique Buraud de mavoir permis de prendre beaucoup de plaisir traduire les deux premiers volumes de la collection Tte la Premire. Et de mme quil faut un village pour crire un ouvrage technique, il faut un village pour le traduire. Merci tous ceux qui mont fourni pour ces deux livres explications et suggestions sur Java et les Design patterns, mais aussi sur des sujets aussi varis que la musique lectronique, le style cyberpunk, le bon usage du point-virgule, la fabrication des pizzas ou les tournures idiomatiques: Luc Carit, Christian Cler, Marie-Jose Keller et Fred Kolinski, Violette Kubler, David et Batrice Morton, Franoise Pougeol, Catherine Pougeol et Lilo de chez Pizza Clip. Et merci enfin Michel Beteta et Frdric Laurent pour la prcision de leur relecture. xxxiv
intro
*La raison de tous ces remerciements est que nous testons la thorie selon laquelle toute personne mentionne dans un livre en achtera au moins un exemplaire, voire plusieurs si elle en fait part sa famille et ses amis. Si vous voulez figurer dans les remerciements de notre prochain livre et si vous avez une grande famille, crivez-nous.

1 Introduction aux Design Patterns

Bienvenue aux g g Design Patterns


Maintenant quon vit Objectville, il faut quon se mette aux Design Patterns... Tout le monde ne parle que de a. On va bientt tre le clou de la soire patterns que Jacques et Lucie organisent tous les mercredis !

Quelquun a dj rsolu vos problmes. Dans ce chapitre, vous allez


apprendre comment (et pourquoi) exploiter lexprience et les leons tires par dautres dveloppeurs qui ont dj suivi le mme chemin, rencontr les mmes problmes de conception et survcu au voyage. Nous allons voir lusage et les avantages des design patterns, revoir quelques principes fondamentaux de la conception OO et tudier un exemple de fonctionnement dun pattern. La meilleure faon dutiliser un pattern est de le charger dans votre cerveau puis de reconnatre les points de vos conceptions et des applications existantes auxquels vous pouvez les appliquer. Au lieu de rutiliser du code, lespatterns vous permettent de rutiliser de lexprience. nouveau chapitre

SuperCanard

Tout a commenc par une simple application, SuperCanard


Jol travaille pour une socit qui a rencontr un norme succs avec un jeu de simulation de mare aux canards, SuperCanard. Le jeu affiche toutes sortes de canards qui nagent et mettent des sons. Les premiers concepteurs du systme ont utilis des techniques OO standard et cr une superclasse Canard dont tous les autres types de canards hritent.

Tous les canards cancanent et nagent. La superclasse gre le code de limplmentation.

cancaner() nager() afficher()

Canard

// AUTRES mthodes propres aux canards...

ypes Comme tous les sous-t ct pe as de canard ont un e diffrent, la mthod ite. afficher() est abstra

e ype d t s u e so arge Chaqurd a la ch le cana lmenter de la dimp ortement her() comp ode affic dont il mth la faon cran. pour atra l appar

Colvert
afficher() { // aspect dun colvert}

Mandarin
afficher() { // aspect dun mandarin }

e cares types d anard. t u a x u e r b De nom ent de la classe C nards hrit

Lan pass, la socit a subi de plus en plus de pression de la part de la concurrence. lissue dune semaine de sminaire rsidentiel consacr au brainstorming et au golf, ses dirigeants ont pens quil tait temps de lancer une grande innovation. Il leur faut maintenant quelque chose de rellement impressionnant prsenter la runion des actionnaires qui aura lieu aux Balares la semaine prochaine.

Chapitre 1

introduction aux Design Patterns

Maintenant, nous voulons que les canard VOLENT


Les dirigeants ont dcid que des canards volants taient exactement ce quil fallait pour battre la concurrence plate couture. Naturellement, le responsable de Jol leur a dit que celui-ci naurait aucun problme pour bricoler quelque chose en une semaine. Aprs tout, a-t-il dit, Jol est un programmeur OO... a ne doit pas tre si difficile !

Il suffit que jajoute une mthode voler() dans la classe Canard et tous les autres canards en hriteront. Lheure est venue de montrer mon vrai gnie.

Joe

Ce que nous voulons.


Canard
cancaner() nager() afficher()

usles so ent s e t Tou es hrit class oler(). de v

voler()
// AUTRES mthodes propres un canard...

Ce que Jol ajout.

Colvert
afficher() { // aspect dun colvert }

Mandarin
afficher() { // aspect dun mandarin }

Autres typ

es de canar

d...

vous tes ici

il y a quelque chose qui ne va pas

Il y a quelque chose qui ne va pas


Jol, je suis la runion des actionnaires. On vient de passer la dmo et il y a plein de canards en plastique qui volent dans tout lcran. Cest une plaisanterie ou quoi ? Tu as peut-tre envie de passer du temps sur Monster.fr ?

Que sest-il pass ?


Jol a oubli que toutes les sous-classes de Canard ne doivent pas voler. Quand il a ajout le nouveau comportement la superclasse Canard, il a galement ajout un comportement qui ntait pas appropri certaines de ses sousclasses. Maintenant, il a des objets volants inanims dans son programme SuperCanard. Une mise jour locale du code a provoqu un effet de bord global (des canards en plastique qui volent) !
Canard cancaner() nager() afficher() voler() // AUTRES mthodes propres un canard...

OK, on dirait quil y a un l g e r dfaut dans ma conception. Je ne vois pas pourquoi ils ne peuvent pas appeler a une fonctionnalit. Cest plutt marrant...

la dans ) ( r vole ant , il a donna caa l p En rclasse ards l pris supe S les can er, y com nt TOU t de vol e devraie paci qui ne l ceux pas.
Colvert afficher() { // aspect dun colvert }

Ce quil prenait pour une super application de lhritage dans un but de rutilisation semble plus problmatique quand il sagit de maintenance.
CanardEnPlastique

Mandarin afficher() { // aspect dun mandarin }

cancaner() { // redfinir pour couiner } afficher() { // aspect dun canard en plastique

nards en Puisque les ca lent pas, vo plastique ne t redfinie cancaner() es pour couiner.

Chapitre 1

introduction aux Design Patterns

Jol rflchit lhritage...


Je pourrais toujours me contenter de redfinir la mthode voler() dans CanardEnPlastique, comme je le fais pour cancaner()...
Je pourrais toujours me contenter de redfinir la mthode voler() dans CanardEnPlastique, comme je le fais pour cancaner()...

CanardEnPlastique cancaner() { // couiner } afficher () { .// canard en plastique } voler() { // redfinir pour ne rien faire }

Leurre

cancaner() { // redfinir pour ne rien faire } afficher() { // leurre}

la e classe deue les r t u a e n u ez q Voici les . Remarqu hirarchie volent pas plus que e, leurres ne n plastique. En outr canards e anent pas non plus. ils ne canc

voler() { // redfinir pour ne rien faire }

vos crayons Sharpen your pencil


Dans la liste ci-aprs, quels sont les inconvnients utiliser lhritage pour dfinir le comportement de Canard ? (Plusieurs choix possibles.)

A. Le code est dupliqu entre les sousclasses.

D. Il est difficile de connatre tous les comportements


des canards.

B. Les changements de comportement au


moment de lexcution sont difficiles

E. Les canards ne peuvent pas voler et cancaner en


mme temps.

C. Nous ne pouvons pas avoir de canards


qui dansent.

F. Les modifications peuvent affecter involontairement


dautres canards.
vous tes ici

Lhritage nest la rponse

Et si nous utilisions une interface ?


Jol sest rendu compte que lhritage ntait probablement pas la rponse : il vient de recevoir un mmo annonant que les dirigeants ont dcid de ractualiser le produit tous les six mois (ils nont pas encore dcid comment). Il sait que les spcifications vont changer en permanence et quil va peut-tre tre oblig de redfinir voler() et cancaner() pour toute sousclasse de Canard qui sera ajoute au programme... ad vitam aeternam. Il a donc besoin dun moyen plus sain pour que seuls certains types de canard (mais pas tous) puissent voler ou cancaner.
Je pourrais extraire la mthode voler() de la superclasse Canard, et crer une interface Volant() qui aurait une mthode voler(). Ainsi, seuls les canards qui sont censs voler implmenteront cette interface et auront une mthode voler()... et je pourrais aussi crer une interface Cancanant par la mme occasion, puisque tous les canards ne cancanent pas.

Canard
Cancaneur cancaner()

Volant voler()

nager() afficher() // AUTRES mthodes propres aux canards...

Colvert afficher() voler() cancaner()

Mandarin afficher() voler() cancaner

CanardEnPlastique afficher() cancaner


afficher()

Leurre

Et VOUS ? Que pensez-vous de cette conception ?

Chapitre 1

introduction aux Design Patterns

Cest, comment dire lide la plus stupide que tu aies jamais eue. Et le code dupliqu ? Si tu pensais que redfinir quelques mthodes tait une mauvaise ide, quest-ce que a va tre quand tu devras modifier un peu le comportement de vol dans les 48 sous-classes de Canard qui volent ?!

Que feriez-vous la place de Jol ?


Nous savons que toutes les sous-classes ne doivent pas avoir de comportement qui permette aux canards de voler ou de cancaner. Lhritage ne constitue donc pas la bonne rponse. Mais si crer des sous-classes qui implmentent Volant et/ou Cancaneur rsout une partie du problme (pas de canards en plastique qui se promnent malencontreusement dans lcran), cela dtruit compltement la possibilit de rutiliser le code pour ces comportements et ne fait que crer un autre cauchemar sur le plan de la maintenance. De plus, tous les canards qui doivent voler ne volent peut-tre pas de la mme faon... ce stade, vous attendez peut-tre quun design pattern arrive sur son cheval blanc et vous sauve la mise. Mais o serait le plaisir ? Non, nous allons essayer de trouver une solution lancienne en appliquant de bons principes de conception OO.
Ne serait-ce pas merveilleux sil y avait un moyen de construire des logiciels de sorte que les modifications aient le moins dimpact possible sur le code existant ? Cela nous permettrait de passer moins de temps retravailler le code et plus de temps inventer des trucs plus cools...

vous tes ici

le changement est constant

La seule et unique constante du dveloppement


Bien, quelle est la seule chose sur laquelle vous puissiez toujours compter en tant que dveloppeur ?
Indpendamment de lendroit o vous travaillez, de lapplication que vous dveloppez ou du langage dans lequel vous programmez, quelle est la seule vraie constante qui vous accompagnera toujours ?

(prenez un miroir pour voir la rponse)

TNEMEGNAHC EL
De nombreux facteurs peuvent motiver le changement. numrez quelles pourraient tre les raisons de modifier le code de vos applications (nous en avons list deux pour vous aider dmarrer).

Quel que soit le soin que vous ayez apport la conception dune application, elle devra prendre de lampleur et voluer au fil du temps. Sinon, elle mourra.

vos crayons Sharpen your pencil

Mes clients ou mes utilisateurs dcident quils veulent autre chose ou quils ont besoin dune nouvelle fonctionnalit. Mon entreprise a dcid quelle allait changer de systme gestion de bases de donnes et quelle allait galement acheter ses donnes chez un autre fournisseur dont le format est diffrent. Argh !

Chapitre 1

introduction aux Design Patterns

Attaquons le problme...
Nous savons donc que le recours lhritage na pas t un franc succs, puisque le comportement des canards ne cesse de varier dune sous-classe lautre et que ce comportement nest pas appropri toutes les sous-classes. Les interfaces Volant et Cancaneur semblaient tout dabord prometteuses seuls les canards qui volent implmenteraient Volant, etc. sauf que les interfaces Java ne contiennent pas de code : il ny a donc pas de code rutilisable. Chaque fois que vous voulez changer un comportement, vous tes oblig de le rechercher et de le modifier dans toutes les sous-classes dans lesquelles il est dfini, en introduisant probablement quelques nouveaux bogues en cours de route ! Heureusement, il existe un principe de conception fait sur mesure pour cette situation.

Extrayez ce qui varie et encapsulez-le pour ne pas affecter le reste de votre code. Rsultat ? Les modifications du code entranent moins de consquences inattendues et vos systmes sont plus souples !

Principe de conception
Identifiez les aspects de votre application qui varient et sparez-les de ceux qui demeurent constants

Le premier de nos nombreux principes de de conception. Nous leur consacrerons plus temps tout au long de cet ouvrage.
Autrement dit, si lun des aspects de votre code est susceptible de changer, par exemple avec chaque nouvelle exigence, vous savez que vous tes face un comportement quil faut extraire et isoler de tout ce qui ne change pas. Voici une autre faon de formuler ce principe : extraire les parties variables et les encapsuler vous permettra plus tard de les modifier ou de les augmenter sans affecter celles qui ne varient pas. Malgr sa simplicit, ce concept constitue la base de presque tous les design patterns. Tous les patterns fournissent un moyen de permettre une partie dun systme de varier indpendamment de toutes les autres. Cela dit, il est temps dextraire les comportements de canard des classes Canard !

vous tes ici

extrayez ce qui varie

Sparer ce qui change de ce qui reste identique


Par o commencer ? Pour linstant, en dehors des problmes de voler() et de cancaner(), la classe Canard fonctionne bien et ne contient rien qui semble devoir varier ou changer frquemment. part quelques lgres modifications, nous allons donc la laisser pratiquement telle quelle. Maintenant, pour sparer les parties qui changent de celles qui restent identiques , nous allons crer deux ensembles de classes (totalement distinctes de Canard), lun pour voler et lautre pour cancaner. Chaque ensemble de classes contiendra toutes les implmentations de leur comportement respectif. Par exemple, nous aurons une classe qui implmente le cancanement, une autre le couinement et une autre le silence.

Nous savons que voler() et cancaner() sont les parties de la classe Canard qui varient dun canard lautre. Pour sparer ces comportements de la classe Canard, nous extrayons ces deux mthodes de la classe et nous crons un nouvel ensemble de classes pour reprsenter chaque comportement.

toujours la La classe Canard est canards, mais nous superclasse de tous les ments de vol et de extrayons les comporte plaons dans une cancanement et nous les sses. autre structure de cla

Maintenant, le vol et le leur cancanement ont chacun s. propre ensemble de classe

Cest l que vont rsider les diffrentes implmentations des comportements

Extraction de ce qui varie

rd Cla sse Cana

Comportements de canard

10

Chapitre 1

Co mpo

e sd rtement en Co e n mp n ca orte ments de ca

vo l

introduction aux Design Patterns

Conception des comportements de canard


Comment allons-nous procder pour concevoir les classes qui implmentent les deux types de comportements ? Nous voulons conserver une certaine souplesse. Aprs tout, cest dabord la rigidit du comportement des canards qui nous a caus des ennuis. Et nous savons que nous voulons affecter des comportements aux instances des classes Canard. Par exemple instancier un nouvel objet Colvert et linitialiser avec un type spcifique de comportement de vol. Et, pendant que nous y sommes, pourquoi ne pas faire en sorte de pouvoir modifier le comportement dun canard dynamiquement ? Autrement dit, inclure des mthodes set dans les classes Canard pour pouvoir modifier la faon de voler du Colvert au moment de lexcution. Avec ces objectifs en tte, voyons notre deuxime principe de conception :

Principe de conception
Programmer une interface, non une implmentation

Nous allons utiliser une interface pour reprsenter chaque comportement par exemple ComportementVol et ComportementCancan et chaque implmentation dun comportement implmentera lune de ces interfaces. Cette fois, ce ne sont pas les classes Canard qui implmenteront les interfaces pour voler et cancaner. En lieu et place, nous allons crer un ensemble de classes dont la seule raison dtre est de reprsenter un comportement (par exemple couiner ), et cest la classe comportementale, et non la classe Canard, qui implmentera linterface comportementale. Ceci contraste avec notre faon ultrieure de procder, dans laquelle un comportement provenait dune implmentation concrte dans la superclasse Canard ou dune implmentation spcialise dans la sous-classe elle-mme. Dans les deux cas, nous nous reposions sur une implmentation. Nous tions contraints utiliser cette implmentation spcifique et il ny avait aucun moyen de modifier le comportement ( part crire plus de code). Avec notre nouvelle conception, les sous-classes de Canard auront un comportement reprsent par une interface (ComportementVol et ComportementCancan), si bien que limplmentation relle du comportement (autrement dit le comportement spcifique concret cod dans la classe qui implmente ComportementVol ou ComportementCancan) ne sera pas enferm dans la sous-classe de Canard.

Dsormais, les comportements de Canard rsideront dans une classe distincte une classe qui implmente une interface comportementale particulire. Ainsi, les classes Canard nauront besoin de connatre aucun dtail de limplmentation de leur propre comportement.
ComportementVol
voler() <<interface>>

VolerAvecDesAiles
voler() { // implmente le vol du canard }
voler() {

NePasVoler // ne rien faire - le canard ne peut pas voler ! }

vous tes ici

11

programmer une interface

Je ne vois pas pourquoi il faut utiliser une interface pour ComportementVol. On peut faire la mme chose avec une superclasse abstraite. Est-ce que ce nest pas tout lintrt du polymorphisme ?.

Programmer une interface signifie en ralit Programmer un supertype .


Le mot interface a ici un double sens. Il y a le concept dinterface, mais aussi la construction Java interface. Vous pouvez programmer une interface sans rellement utiliser une interface Java. Lide est dexploiter le polymorphisme en programmant un supertype pour que lobjet rel lexcution ne soit pas enferm dans le code. Et nous pouvons reformuler programmer un supertype ainsi : le type dclar des variables doit tre un supertype, gnralement une interface ou une classe abstraite. Ainsi, les objets affects ces variables peuvent tre nimporte quelle implmentation concrte du supertype, ce qui signifie que la classe qui les dclare na pas besoin de savoir quels sont les types des objets rels ! Vous savez dj probablement tout cela, mais, juste pour tre srs que nous parlons de la mme chose, voici un exemple simple dutilisation dun type polymorphe imaginez une classe abstraite Animal, avec deux implmentations concrtes, Chien et Chat. Programmer une implmentation donnerait le code suivant : Chien c = new Chien (); c.aboyer();

superty tre unepe abstrait (peu classe ab interface OU t straite) une


Animal
emettreSon()

Dclarer la variable c de type Chien (une implmentation concrte dAnimal) nous oblige coder une implme ntation concrte. Nous savons que cest un Chien, mais nous pouvons maintenant utiliser la rfrence animal de manire polymorphe.

Mais programmer une interface ou un supertype donnerait ceci : Animal animal = new Chien (); animal.emettreSon();

ations metnet l p im s r c n o c
Chien
emettreSon() aboyer(); } aboyer() { //aboiement}

Mieux encore, au lieu de coder en dur linstanciation du sous-type (comme new Chien ()) dans le code, affectez lobjet de limplmentation concrte au moment de lexcution :
Chat
emettreSon() { miauler(); } miauler() { // miaulement}

a = getAnimal(); a.emettreSon();

Nous ne savons pas QUEL le soustype rel de lanimal... toutEST ce qui nous intresse, cest quil sait rpondre emettreSon(). comment

12

Chapitre 1

introduction aux Design Patterns

Implmenter les comportements des canards


Nous avons ici deux interfaces, ComportementVol et ComportementCancan, ainsi que les classes correspondantes qui implmentent chaque comportement concret :

ComportementVol
voler()

<<interface>>

e l est un classes o V t n e tem s les Compor ce que toute mentent. a interf olent impl classes dont qui vs les nouvelles voler nont Toute bres doivent mthode les memplmenter la qu im . voler()

nous avons le cancanement ; e mthode ur po i ic e m m e D ne contientquun une interface qui it tre implmente cancaner() qui do
<<interface>>

ComportementCancan
cancaner()

VolerAvecDesAiles
voler() { // implmente le vol du canard }

NePasVoler voler() { // ne rien faire - le canard ne peut pas voler ! }

Cancan
cancaner() { // implmente le cancanement }

Coincoin
cancaner() { // implmente le couinement }

CanardMuet
cancaner() { // ne rien faire - le canard ne peut pas cancaner ! }

Canar ds qu Et voici l c a i n im c p a l m n e e n les canard tation po nt Voici lim s u r qui ne peu vent pas tous vol pour plmentation voler d t o qui ont us les cana u rds des aile s.

Canards qui couinent.

Canards qui nmettent aucun son.

Avec cette conception, les autres types dobjets peuvent rutiliser nos comportements de vol et de cancanement parce que ces comportements ne sont plus cachs dans nos classes Canard ! Et nous pouvons ajouter de nouveaux comportements sans modifier aucune des classes comportementales existantes ni toucher aucune des classes Canard qui utilisent les comportements de vol.

si enons ain la t b o s u o N ages de la les avant ISATION sans ne L g I a T p RU e qui accom surcharg e lhritag

vous tes ici

13

le comportement dans une classe

Il nythere a pas arede no

Est-ce que je dois toujours implmenter mon application dabord, voir les lments qui changent, puis revenir en arrire pour sparer et encapsuler ces lments ?

Q:

Dumb Questions Questions Stupides

R:

Cela fait vraiment bizarre davoir une classe qui nest quun comportement. Est-ce que les classes ne sont pas censes reprsenter des entits ? Est-ce quelles ne doivent pas avoir un tat ET un comportement ?

Q:

Pas toujours. Quand vous concevez une application, vous anticipez souvent les points de variation, puis vous avancez et vous construisez le code de manire suffisamment souple pour pouvoir les grer. Vous verrez que vous pouvez appliquer des principes et des patterns nimporte quel stade du cycle de vie du dveloppement. Est-ce quon devrait aussi transformer Canard en interface ?

R:

Q:

Dans un systme OO, oui, les classes reprsentent des entits qui ont gnralement un tat (des variables dinstance) et des mthodes. Dans ce cas, lentit se trouve tre un comportement. Mais mme un comportement peut avoir un tat et des mthodes; un comportement de vol peut avoir des variables dinstance qui reprsentent les attributs du vol (nombre de battements dailes par minute, altitude et vitesse maximales, etc.).

R:

Pas dans ce cas. Vous verrez quune fois que nous aurons tout assembl, nous serons contents de disposer dune classe concrte Canard et de canards spcifiques, comme Colvert, qui hritent des proprits et des mthodes communes. Maintenant que nous avons enlev de Canard tout ce qui varie, nous avons tous les avantages de cette structure sans ses inconvnients.

vos crayons Sharpen your pencil


1 En utilisant notre nouvelle conception, que feriez-vous pour ajouter la propulsion raction lapplication SuperCanard ?

14

Chapitre 1

1) Crer une classe VolAReaction qui implmente linterface ComportementVol. Rponses :

Voyez-vous une classe qui pourrait utiliser le comportement de Cancan et qui nest pas un canard ?

2) Par exemple un appeau (un instrument qui imite le cri du canard).

introduction aux Design Patterns

Intgrer les comportements des canards


La cl est quun Canard va maintenant dlguer ses comportements au lieu dutiliser les mthodes voler() et cancaner() dfinies dans la classe Canard (ou une sous-classe).
Voici comment :
1

Nous allons dabord ajouter la classe Canard deux variables dinstance nommes comportementVol et comportementCancan, qui sont dclares du type de linterface (non du type de limplmentation concrte). Chaque objet Canard affectera ces variables de manire polymorphe pour rfrencer le type de comportement spcifique quil aimerait avoir lexcution (VolerAvecDesAiles, Coincoin, etc.). Nous allons galement ter les mthodes voler() et cancaner() de la classe Canard (et de toutes ses sous-classes) puisque nous avons transfr ces comportements dans les classes ComportementVol et ComportementCancan. Nous allons remplacer voler() et cancaner() dans la classe Canard par deux mthodes similaires, nommes effectuerVol() et effectuerCancan(). Vous allez bientt voir comment elles fonctionnent.

Les variables comportementales sont dclares du type de lINTERFACE qui gre le comportement

Canard
ComportementVol comportementVol ComportementCancan comportementCancan

Lors de lexcution, les variables dinstance contiennent une rfrence un comportement spcifique.

Ces mthodes remplacent voler() et cancaner().

vo l

nager() afficher() effectuerVol() // AUTRES mthodes propres aux canards...

Com

ts portemen

de

Com

portements

can de

Comportements de canard

Implmentons maintenant effectuerCancan(): public class Canard { ComportementCancan comportementCancan; // autres variables public void effectuerCancan() { comportementCancan.cancaner(); } }

quelque une rfrence a d ar an C e qu e Cha ente linterfac chose qui implm Cancan. e, Comportementnc anement lui-mm ca n so r re g t rtemen Au lieu de Les dlgue ce compo lobjet Canard c par comportementCancan. u d en r es f nt dclar lobjet r mportement so variables du co RFACE comportementale. type de lINTE

Rien de plus simple, nest-ce pas ? Pour cancaner, un Canard demande lobjet rfrenc par comportementCancan de le faire sa place. Dans cette partie du code, peu nous importe de quelle sorte dobjet il sagit. Une seule chose nous intresse : il sait cancaner !
vous tes ici

ca ne m ent

effectuerCancan()

15

intgrer les comportements des canards

Suite de lintgration...
3

Bien. Il est temps de soccuper de la faon dont les variables dinstance comportementVol et comportementCancan sont affectes. Jetons un coup dil la classe Colvert :
public class Colvert extends Canard { public Colvert() { comportementCancan = new Cancan(); comportementVol = new VolerAvecDesAiles(); } }

Souvenez-vous que Colvert hrite les variables dinstance comportementCancan et comportementVol de la classe Canard.

rt utilise la Puisquun Colve ur cancaner, classe Cancan porCancan() est quand effectueonsabilit du appele, la resp t dlgue cancanement es n et nous lobjet Cancaai cancan. obtenons un vr erAvecDesAiles Et il utilise Vol comme type deVol. Comportement

public void afficher() { System.out.println(Je suis un vrai colvert); } }

Le cancan de Colvert est un vrai cancan de canard vivant, pas un coincoin ni un cancan muet. Quel est le mcanisme ? Quand un Colvert est instanci, son constructeur initialise sa variable dinstance comportementCancan hrite en lui affectant une nouvelle instance de type Cancan (une classe dimplmentation concrte de ComportementCancan). Et il en va de mme pour le comportement de vol le constructeur de Colvert ou de Mandarin initialise la variable dinstance comportementVol avec une instance de type VolerAvecDesAiles (une classe dimplmentation concrte de ComportementVol).

16

Chapitre 1

introduction aux Design Patterns

Attends une seconde. Tu nas pas dit quon ne doit PAS programmer une implmentation ? Mais quest-ce quon fait dans ce constructeur ? On cre une nouvelle instance dune classe dimplmentation concrte de Cancan !

Bien vu. Cest exactement ce que nous faisons... pour linstant. Plus tard, nous aurons dans notre bote outils dautres patterns qui nous permettront dy remdier. Remarquez pourtant que si nous affectons bien les comportements aux classes concrtes (en instanciant une classe comportementale comme Cancan ou VolerAvecDesAiles et affectant linstance notre variable de rfrence comportementale), nous pourrions facilement changer cela au moment de lexcution. Nous avons toujours beaucoup de souplesse, mais la faon daffecter les variables dinstance nest pas trs satisfaisante. Mais rflchissez : puisque la variable dinstance comportementCancan est du type de linterface, nous pourrons (par la magie du polymorphisme) affecter dynamiquement une instance dune classe dimplmentation ComportementCancan diffrente au moment de lexcution. Faites une pause et demandez-vous comment vous implmenteriez un canard pour que son comportement change lexcution. (Vous verrez le code qui le permet dans quelques pages.)

vous tes ici

17

tester le comportement des canards

Tester le code de Canard


1 Tapez et compilez le code de la classe Canard ci-aprs (Canard.

java) et celui de la classe Colvert de la page 16 (Colvert.java).


public abstract class Canard {

rfrence clare deux variables de D s ComportementVol comportementVol; les types des interface usComportementCancan comportementCancan; pour so les es ut comportementales. To public Canard() { me m s le } classes de Canard (dan t. en rit package) en h
public abstract void afficher(); public void effectuerVol() { comportementVol.voler(); } public void effectuerCancan() { comportementCancan.cancaner(); } public void nager() { System.out.println(Tous les canards flottent, mme les leurres!); }

Dlgue la classe comportementale.

} 2 Tapez et compilez le code de linterface ComportementVol

(ComportementVol.java) et les deux classes dimplmentation comportementales (VolerAvecDesAiles.java et NePasVoler.java).


public interface ComportementVol { public void voler();}

Linterface que toutes les classes comportementales qui volent implmentent.

public class VolerAvecDesAiles implements ComportementVol { public void voler() { System.out.println(Je vole !!); } }

du Implmentation de vol pour les comportement LENT... canards qui VO


Implmentation comportement du les canards qui de vol pour PAS (comme lesne VOLENT plastique et les canards en leurres).

public class NePasVoler implements ComportementVol { public void voler() { System.out.println(Je ne sais pas voler) } }

18

Chapitre 1

introduction aux Design Patterns

Tester le code de Canard (suite)...


3 Tapez et compilez le code de linterface ComportementCancan

(ComportementCancan.java) et celui des trois classes dimplmentation comportementales (Cancan.java, CancanMuet.java et CoinCoin.java).
public interface ComportementCancan { public void cancaner() ; } public class Cancan implements ComportementCancan { public void cancaner() { System.out.println(Cancan); } }

public class CancanMuet implements ComportementCancan { public void cancaner() { System.out.println(Silence); } }

public class Coincoin implements ComportementCancan { public void cancaner() { System.out.println(Coincoin); } } 4

tapez et compilez le code de la classe de test (MiniSimulateur.java).


public class MiniSimulateur { public static void main(String[] args) { Canard colvert = new Colvert(); colvert.effectuerCancan(); colvert.effectuerVol(); } }

Excutez le code !
Fichier dition Fentre Aide Yadayadayada

%java MiniSimulateur Cancan Je vole !!

thode hrite Cette ligne appelle la m rt, qui dlgue alors Colve effectuerCancan() de de lobjet (autrement ComportementCancan la rfrence hrite sur dit appelle cancaner() canard). du comportementCancan e me avec la mthode hrit Puis nous faisons de m . rt effectuerVol() de Colve

vous tes ici

19

des canards au comportement dynamique

Modifier le comportement dynamiquement


Quel dommage que nos canards possdent tout ce potentiel de dynamisme et quils ne lutilisent pas ! Imaginez que vous vouliez fixer le type de comportement du canard via des mthodes set dans la sous-classe de Canard au lieu de linitialiser dans le constructeur.
1

Ajoutez deux nouvelles mthodes la classe Canard :


public void setComportementVol(ComportementVol cv) { comportementVol = cv; } public void setComportementCancan(ComportementCancan cc) { comportementCancan = cc; }
Canard
ComportementVol comportementVol; ComportementCancan comportementCancan; nager() afficher() effectuerCancan() effectuerVol() setComportementVol() setComportementCancan() // AUTRES mthodes propres aux canards...

Nous pouvons appeler ces mthodes chaque fois que nous voulons modifier le comportement dun canard la vole.
note de lditeur : jeu de mots gratuit

Crez un nouveau type de Canard (PrototypeCanard.java).


public class PrototypeCanard extends Canard { public PrototypeCanard() { comportementVol = new NePasVoler(); comportementCancan = new Cancan(); }

onde... rd vient au m na ca u ea uv no Notre en de voler. sans aucun moy

public void afficher() { System.out.println(Je suis un prototype de canard); } }

Crez un nouveau type de ComportementVol (PropulsionAReaction.java).

Qu cela ne tienne ! Nous crons un nouveau comportement de vol : la propulsion raction

public class PropulsionAReaction implements ComportementVol { public void voler() { System.out.println(Je vole avec un racteur !); } }

20

Chapitre 1

introduction aux Design Patterns

Modifiez la classe de test (MiniSimulateur.java), ajoutez PrototypeCanard et munissez-le dun racteur.

avant

public class MiniSimulateur { public static void main(String[] args) { Canard colvert = new Colvert(); colvert.effectuerCancan(); colvert.effectuerVol();

Canard proto = new PrototypeCanard(); proto.effectuerVol(); proto.setComportementVol(new PropulsionAReaction()); proto.effectuerVol(); } }

l de Le premier appe dlgue effectuerVol() ementVol lobjet comport nstructeur dfini dans le co anard, qui est de PrototypeC NePasVoler. une instance de

Si cela fonctionne, le canard comportement de vol dynam a chang de iquement ! Ce serait IMPOSSIBLE si limpl rsidait dans la classe Canar mentation d
Fichier dition Fentre Aide Yabadabadoo

Ceci invoque la mthode set hrite du prototype, et ...voil ! Le prototype est soudain dot dune fonctionnalit de vol raction

Excutez-le !

%java MiniSimulateur Cancan Je vole !! Je ne sais pas voler Je vole avec un racteur !

aprs

Pour modifier le comportement dun canard au moment de lexcution, il suffit dappeler la mthode set correspondant ce comportement.
vous tes ici

21

vue densemble

Vue d e nsemble des comportements encapsuls


Bien. Maintenant que nous avons plong au cur de la conception du simulateur, il est temps de venir respirer la surface et de jeter un coup dil global.
Vous trouverez ci-aprs toute la structure de classes retravaille. Nous avons tout ce quil nous faut : des canards qui drivent de Canard, des comportements de vol qui implmentent ComportementVol et des comportements de cancanement qui implmentent ComportementCancan. Remarquez aussi que nous avons commenc dcrire les choses un peu diffremment. Au lieu de penser les comportements des canards comme un ensemble de comportements, nous allons commencer les penser comme une famille dalgorithmes. Rflchissez-y : dans la conception de SuperCanard, les algorithmes reprsentent ce quun canard ferait diffremment (diffrentes faons de voler ou de cancaner), mais nous pourrions tout aussi bien utiliser les mmes techniques pour un ensemble de classes qui implmenteraient les diffrentes faons de calculer la taxe dhabitation selon les diffrentes communes. Soyez trs attentif aux relations entre les classes. Prenez un crayon et notez la relation approprie (EST-UN, A-UN et IMPLMENTE) sur chaque flche du diagramme de classes.

Le client utilise une famille dalgorithmes encapsule pour voler et cancaner.


Canard
ComportementVol comportementVol

Comportement de vol encapsul


<<interface>> ComportementVol voler()

Client
nager() afficher() effectuerCancan() effectuerVol()

VolerAvecDesAiles
voler() { // implmente le vol du canard }

NePasVoler voler() { / ne rien faire - le canard ne peut pas voler ! }

ComportementCancan comportementCancan

chaque Pensez le de ensemb tements compor une famille comme ithmes. dalgor

setComportementVol() setComportementCancan() // AUTRES mthodes propres aux canards...

Comportement de cancanement encapsul


ComportementCancan
cancaner() <<interface>>

Colvert
afficher() { // aspect dun colvert }

Leurre
afficher() { // aspect dun leurre }

Mandarin
afficher() { // aspect dun mandarin}

CanardEnPlastique
afficher() { // aspect dun canard en plastique}

Cancan
cancaner() { // implmente le cancanement }

Coincoin
cancaner() { // couinement du caneton en plastique }

CanardMuet
cancaner() { // ne rien faire - le canard ne peut pas cancaner ! }

s ment sont e t r o comp es Ces lgorithm ables. a rchange inte


22
Chapitre 1

introduction aux Design Patterns

A-UN peut tre prfrable EST-UN


La relation A-UN est une relation intressante : chaque canard a un ComportementVol et un ComportementCancan auxquels ils dlgue le vol ou le cancanement. Lorsque vous assemblez deux classes de la sorte, vous utilisez la composition. Au lieu dhriter leur comportement, les canards lobtiennent en tant composs avec le bon objet comportemental. Cette technique est importante ; en fait, nous avons appliqu notre troisime principe de conception : Matre et disciple...
Matre : Petit scarabe, dis-moi ce que tu as appris sur la Voie de lorientation objet. Disciple : Matre, jai appris que la promesse de la Voie de lorientation objet tait la rutilisation. Matre : Continue, scarabe... Disciple : Matre, lhritage permet de rutiliser toutes les bonnes choses, et nous parviendrons rduire radicalement les temps de dveloppement et programmer aussi vite que nous coupons le bambou dans la fort. Disciple : Scarabe, consacre-t-on plus de temps au code avant que le dveloppement ne soit termin ou aprs ? Disciple : La rponse est aprs, matre. Nous consacrons toujours plus de temps la maintenance et la modification des logiciels qu leur dveloppement initial. Matre : Alors, scarabe, doit on placer la rutilisation au-dessus de la maintenabilit et de lextensibilit ? Disciple : Matre, je commence entrevoir la vrit. Matre : Je vois que tu as encore beaucoup apprendre. Je veux que tu ailles mditer un peu plus sur lhritage. Comme tu las constat, lhritage a des inconvnients et il y a dautres moyens de parvenir la rutilisation.

Principe de conception
Prfrez la composition lhritage

Comme vous lavez constat, crer des systmes en utilisant la composition procure beaucoup plus de souplesse. Non seulement cela vous permet dencapsuler une famille dalgorithmes dans leur propre ensemble de classes, mais vous pouvez galement modifier le comportement au moment de lexcution tant que lobjet avec lequel vous composez implmente la bonne interface comportementale. La composition est utilise dans de nombreux design patterns et vous en apprendrez beaucoup plus sur ses avantages et ses inconvnients tout au long de cet ouvrage.

Musclez vos neurones

Un appeau est un instrument que les chasseurs emploient pour imiter les appels (cancanements) des canards. Comment implmenteriez-vous un appeau sans hriter de la classe Canard ?

vous tes ici

23

le pattern Stratgie

propos des design patterns...

er

Flicitations pour votre premier pattern !

Vous venez dappliquer votre premier design pattern, le pattern STRATEGIE. Oui, vous avez utilis le pattern Stratgie pour revoir la conception de lapplication SuperCanard. Grce ce pattern, le simulateur est prt recevoir toutes les modifications que les huiles pourraient concocter lors de leur prochain sminaire aux Balares. Nous navons pas pris le plus court chemin pour lappliquer, mais en voici la dfinition formelle :

Le pattern Stratgie dfinit une famille dalgorithmes, encapsule chacun deux et les rend interchangeables. Stratgie permet lalgorithme de varier indpendamment des clients qui lutilisent.

ion dfinit sionE T T s e re tez CE z impr Ressor vous voudre fluencer vot quand s amis ou in ner vo . patron

24

Chapitre 1

introduction aux Design Patterns

Problme de conception
Vous trouverez ci-dessous un ensemble de classes et dinterfaces pour un jeu daventure. Elles sont toutes mlanges. Il y a des classes pour les personnages et des classes pour les comportements correspondant aux armes que les personnages peuvent utiliser. Chaque personnage ne peut faire usage que dune arme la fois, mais il peut en changer tout moment en cours de jeu. Votre tche consiste les trier... (Les rponses se trouvent la fin du chapitre.)
Votre tche : 1. 1 Rorganiser les classes. 2. 2 Identifier une classe abstraite, une interface et huit classes ordinaires.
3 3. Tracer des flches entre les classes.

a. Tracez ce type de flche pour lhritage ( extends ). b. Tracez ce type de flche pour linterface ( implements ). c. Tracez ce type de flche pour A-UN.

4 Placer la mthode setArme() dans la bonne classe. 4.


Personnage
ComportementArme arme ; combattre();

ComportementPoignard Reine
combattre() { ... }
utiliserArme() { // implmente porter un coup de poignard }

ComportementArcEtFleches
utiliserArme() { // implmente tirer une flche }

ComportementArme
utiliserArme();

<<interface>>

Roi
combattre() { ... }

Troll
combattre() { ... }

ComportementHache
utiliserArme() { // implmente frapper avec une hache }

Chevalier
combattre() { ... }

ComportementEpee
utiliserArme() { // implmente brandir une pe }

setArme(ComportementArme a) { this.arme = a; }

vous tes ici

25

un peu de vocabulaire

Entendu la caftria...
Alice
Jaimerais une galette de bl noir avec du jambon, du fromage et un oeuf non brouill, une autre avec des petits lardons, des oignons et un peu de crme frache,un verre de cidre, et pour le dessert deux boules de glace la vanille avec de la sauce au chocolat et de la crme chantilly et un caf dcafin !

Flo
Donnez moi une complte miroir, une paysanne, une bole, une dame blanche et un DK !

Quelle est la diffrence entre ces deux commandes ? Aucune ! Elles sont parfaitement identiques, sauf quAlice utilise quatre fois plus de mots et risque dpuiser la patience du ronchon qui prend les commandes. Que possde donc Flo quAlice na pas ? Un vocabulaire commun avec le serveur. Non seulement il leur est plus facile de communiquer, mais le serveur a moins mmoriser, puisquil a tous les patterns de la crperie en tte. Les design patterns vous permettent de partager un vocabulaire avec les autres dveloppeurs. Une fois que vous disposez de ce vocabulaire, vous communiquez plus facilement avec eux et vous donnez envie de dcouvrir les patterns ceux qui ne les connaissent. Cela vous permet galement de mieux apprhender les problmes darchitecture en pensant au niveau des patterns (des structures) et pas au niveau des dtails, des objets.

26

Chapitre 1

introduction aux Design Patterns

Entendu dans le box voisin...


Alors, jai cr cette classe Diffusion. Elle conserve la trace de tous les objets qui lcoutent, et, chaque fois quune nouvelle donne arrive, elle envoie un message chaque auditeur. Ce qui est gnial, cest que les auditeurs peuvent se joindre la diffusion toutmoment, et quils peuvent mme se dsabonner. Cest une conception vraiment dynamique et faiblement couple !

Paul

Musclez vos neurones

Connaissez-vous dautres vocabulaires communs en dehors de la conception OO et des caftrias ? (Pensez aux garagistes, aux charpentiers, aux grands chefs, au contrle de trafic arien). Quest-ce que le jargon permet de communiquer ? Quels sont les aspects de la conception OO qui sont vhiculs par les noms des patterns? Quelles sont les qualits voques par le nom Pattern Stratgie ?

Paul, pourquoi ne pas dire simplement que tu appliques le pattern Observateur ?

Exactement. Si vous communiquez avec des patterns, les autres dveloppeurs savent immdiatement et prcisment de quoi vous parlez. Mais prenez garde la patternite ... Vous savez que vous lavez contracte quand vous commencez appliquer des patterns pour programmer Hello World...

vous tes ici

27

un vocabulaire commun

Le pouvoir dun vocabulaire commun


Lorsque vous communiquez en utilisant des patterns, vous faites plus que partager un JARGON.

Les vocabulaires partags sont PUISSANTS. Quand vous communiquez avec un autre dveloppeur ou avec votre quipe en employant un nom de pattern, vous ne communiquez pas seulement un mot mais tout un ensemble de qualits, de caractristiques et de contraintes que le pattern reprsente. Les patterns vous permettent dexprimer plus de choses en moins de mots. Quand vous utilisez un pattern dans une description, les autres dveloppeurs comprennent tout de suite avec prcision la conception que vous avez en tte. Parler en termes de patterns permet de rester plus longtemps dans la conception . Dcrire un systme logiciel en termes de patterns vous permet de demeurer au niveau de la conception plus longtemps, sans devoir plonger dans les petits dtails de limplmentation des objets et des classes. Un vocabulaire commun peut turbopropulser votre quipe de dveloppement. Une quipe verse dans les design patterns avance beaucoup plus rapidement grce llimination des malentendus. Un vocabulaire commun incite les dveloppeurs juniors apprendre plus vite. Les dveloppeurs juniors coutent les dveloppeurs expriments. Quand les dveloppeurs senior utilisent des design patterns, les juniors sont plus motivs pour les apprendre. Construisez une communaut dutilisateurs de patterns dans votre socit.

gie ern Strat t t a p le s nts ppliquon Nous a menter les diffre ds. Cet pour implments de nos canar omportements comporte s informe que les cs dans un nonc vou ont t encapsul acile tendre de canard classes distinct, f saire, au groupe de ier, mme, si nces et modife lexcution. moment d
e nions d nt u r e eme vu d -vous isaient rapid ion ? z e v a l n t Combie tion qui sen implmenta p d e s c l con es dtai dans d

uipe com mesure que votre q s ides et son se mencera partager de patterns, exprience en termes mmunaut de vous construirez une co patterns.
Pensez lancer un groupe dtude des patterns dans votre socit. Peuttre mme pourriez-vous tre pay apprendre...

28

Chapitre 1

introduction aux Design Patterns

Comment utiliser les design patterns ?


Nous avons tous utilis des bibliothques et des frameworks prexistants. Nous les prenons, crivons le code en utilisant leur API, le compilons dans nos programmes et tirons parti dune grande quantit de code que quelquun dautre a crit. Pensez au API Java et toutes les fonctionnalits quelles vous offrent : rseau, interfaces utilisateurs, E/S, etc. Les bibliothques et les frameworks font gagner beaucoup de temps dans la construction dun modle de dveloppement o nous pouvons nous contenter de choisir les composants que nous insrerons directement. Mais... ils ne nous aident pas structurer nos propres applications de faon quelles soient plus souples, plus faciles comprendre et maintenir. Cest l que les design patterns entrent en scne. Les design patterns ne sintgrent pas directement dans votre code, ils passent dabord par votre CERVEAU. Une fois que vous avez charg les patterns dans votre cerveau et que vous vous dbrouillez bien avec, vous pouvez commencer les appliquer vos propres conceptions et retravailler votre ancien code quand vous constatez quil commence se rigidifier et se transformer en un inextricable fouillis de code spaghetti.
Comport ement
voler()

de vol

encapsu

> <<interface> l entVo Comportem

NePasVoler

sAiles VolerAvecDe
voler() { du canard ente le vol // implm }

peut pas voler() { canard ne faire - le / ne rien }

voler !

Votre C
l
!

ERVEA

Client

rtementVol entVol compo ntCancan Comportem comporteme entCancan Comportem nager() afficher() can() effectuerCan effectuerVol() entVol() setComportem entCancan() canards... setComportem propres aux mthodes // AUTRES

Canard

Comp

t de ortemen
cancaner

cancan

ement

encapsu

Un ensemble de patterns

Comportem

> <<interface> ncan entCa

Coincoin
lastique

Cancan
{ cancaner() ement ente le cancan // implm }

CanardEnP
Mandarin

{ ue cancaner() en plastiq caneton ment du // couine }

cancaner { peut pas cancaner() canard ne faire - le // ne rien }

t CanardMue

Objet qui contient un tat


Colvert
{ afficher() } dun colvert // aspect

Leurre
{ afficher() } dun leurre // aspect

{ afficher() rin} dun manda // aspect

{ ue} afficher() en plastiq dun canard // aspect

OBSERVATEUR

8 8 8 8

Objet Cana

t Objet Cha

pendants Objets d

bj et Sujet

int

ie n O b je t C h

MVC
Mod le

Ob

is jet Sour
Observateurs

Mise jour et notification automatiques

Con

trolle

te Requ

visit grce Votre code, re ign patterns ! lemploi des des

Vue

Il ny a pas are no therede


Si les design patterns sont tellement gniaux, pourquoi quelquun ne les a-t-il pas transforms en bibliothque pour que je naie plus rien faire ?

Q:

Dumb Questions Stupides Questions


Les bibliothques et les frameworks ne sont donc pas des design patterns ?

Q:

commenc comprendre les patterns, vous matriserez plus vite les API qui sont structures autour de patterns. Alors, il ny a pas de bibliothques de design patterns ?

Les design patterns sont au-dessus des bibliothques. Ils nous indiquent comment structurer les classes et les objets pour rsoudre certains problmes. Cest nous de les adapter nos applications.

R:

Les bibliothques et les frameworks ne sont pas des design patterns : ils fournissent des implmentations spcifiques que nous lions notre code. Mais il arrive que les bibliothques et les frameworks sappuient sur les design patterns dans leurs implmentations. Cest super, parce quune fois que vous aurez

R:

Q:

Non, mais vous allez dcouvrir quil existe des catalogues qui numrent les patterns que vous pouvez employer dans vos applications.

R:

vous tes ici

29

pourquoi design patterns ?

Les patterns ne sont rien dautre que lapplication de bons principes OO... Erreur rpandue, scarabe, mais cest plus subtil encore.Tu as beaucoup apprendre...

Dveloppeur sceptique

Gourou bienveillant

Dveloppeur : O.K, hmm, pourquoi nest-ce pas seulement une affaire de bonne conception objet ? Je veux dire, tant que japplique lencapsulation et que je connais labstraction, lhritage et le polymorphisme, est-ce que jai vraiment besoin des design patterns ? Est-ce que ce nest pas plus simple que cela ? Est-ce que ce nest pas la raison pour laquelle jai suivi tous ces cours sur lOO ? Je crois que les design patterns sont utiles pour ceux qui connaissent mal la conception OO. Gourou : Ah, cest encore un de ces malentendus du dveloppement orient objet : connatre les bases de lOO nous rend automatiquement capables de construire des systmes souples, rutilisables et faciles maintenir. Dveloppeur : Non ? Gourou : Non. En loccurrence, la construction de systmes OO possdant ces proprits nest pas toujours vidente, et seul beaucoup de travail a permis de la dcouvrir. Dveloppeur : Je crois que je commence saisir. Ces faons de construire des systmes orients objets pas toujours videntes ont t collectes... Gourou : Oui, et constituent un ensemble de patterns nomms Design Patterns. Dveloppeur : Et si je connais les patterns, je peux me dispenser de tout ce travail et sauter directement des conceptions qui fonctionnent tout le temps ? Gourou : Oui, jusqu un certain point. Mais noublie pas que la conception est un art. Un pattern aura toujours des avantages et des inconvnients. Mais si tu appliques des patterns bien conus et qui ont fait leurs preuves au fil du temps, tu auras toujours beaucoup davance.

30

Chapitre 1

introduction aux Design Patterns

Souvenez-vous que la connaissance de concepts comme labstraction, lhritage et le polymorphisme ne fait pas de vous un bon concepteur orient objet. Un gourou de la conception rflchit la faon de crer des conceptions souples, faciles maintenir et qui rsistent au changement.

Dveloppeur : Et si je ne trouve pas de pattern ? Gourou : Les patterns sont sous-tendus par des principes orients objet. Les connatre peut taider quand tu ne trouves pas de pattern qui corresponde ton problme. Dveloppeur : Des principes ? Tu veux dire en dehors de labstraction, de lencapsulation et... Gourou : Oui, lun des secrets de la cration de systmes OO faciles maintenir consiste rflchir la faon dont ils peuvent voluer, et ces principes traitent de ces problmes.

vous tes ici

31

votre bote outils de concepteur

Votre bote outils de concepteur


Vous avez presque termin le premier chapitre ! Vous avez dj mis quelques outils dans votre bote outils OO. Rcapitulons-les avant de passer au chapitre 2.

POINTS DIMPACT

Connatre les bases de


lOO ne fait pas de vous un bon concepteur.

Les bonnes conceptions


OO sont souples, extensibles et faciles maintenir.

Bases de lOO
Abstraction n Encapsulatio me Polymorphis Hritage

Principes OO

ous ons que vpts OO s o p p u s s ce Nou er z les con connaisse : comment organis le de base s en exploitant hritage les classe hisme, comment lion polymorp orte de concept est une s rat et comment . Si vous par cont lation fonctionne sujets, lencapsu eu rouill sur ces a tte tes un pz votre livre Jav s, puis ressorte re et revoyez-le pitre. la premi apidement ce cha relisez r

Les patterns vous montrent


comment construire des systmes OO de bonne qualit.

Les patterns rsument une


exprience prouve de la conception objet.

Les patterns ne contiennent


pas de code, mais des solutions gnriques aux problmes de conception. Vous devez les adapter aux applications spcifiques.

dtail i varie. u rrons plus en res q ve e re c s z le s le u ou s N Encap ons daut ition s nous en ajouter o p et m o c la Prfrez la liste. n o lhritage. es, n es interfac d z e m m a r g Pro ntations. e m l p im s e d

Les patterns ne sont


pas invents, ils sont dcouverts.

La plupart des patterns et


des principes traitent des problmes de changement dans le logiciel.

finit le chacun ie - de Stratg capsu changeables. hmes, nd er dalgorit de n re intl s le t orithme e lg a x u i e d t e t m n r e ie pe amment des cli s qu Stratg dpend varier in lutilisent.

PatternsOO une famille

Tout au long de ce livre, rflchissez la faon dont les patterns sappuient sur les concepts de base et les principes OO.

La plupart des patterns


permettent une partie dun systme de varier indpendamment de toutes les autres.

On essaie souvent
dextraire ce qui varie dun systme et de lencapsuler.

En voici un, dautres sui

vront !

Les patterns fournissent


un langage commun qui peut optimiser votre communication avec les autres dveloppeurs.

32

Chapitre 1

introduction aux Design Patterns

Donnons votre cerveau droit quelque chose faire. Ce sont vos mots-croiss standard. Tous les mots de la solution sont dans ce chapitre.
1 2 3 4

6 7 8

10

11

12

13

14 15 16 17 18

19

20

Horizontalement 1. Mthode de canard. 3. Modification abrge. 7. Les actionnaires y tiennent leur runion. 9. __________ ce qui varie. 11. Java est un langage orient __________. 12. Dans la commande de Flo. 13. Pattern utilis dans le simulateur. 15. Constante du dveloppement. 17. Comportement de canard. 18. Matre. 19. Les patterns permettent davoir un ___________ commun. 20. Les mthodes set permettent de modifier le ____________.dune classe.

Verticalement 2. Bibliothque de haut niveau. 4. Programmez une _________, non une implmentation. 5. Les patterns la synthtisent. 6. Ils ne volent ni ne cancanent. 8. Rseau, E/S, IHM. 10. Prfrez-la lhritage. 11. Paul applique ce pattern. 14. Un pattern est une solution un problme _________. 16. Sous-classe de Canard.

vous tes ici

33

solution du problme de conception

Solution du problme de conception


Personnage est la superclasse abstraite de tous les autres personnages (Roi, Reine, Chevalier et Troll) tandis que ComportementArme est une interface que toutes les armes implmentent. En consquence, tous les personnages et toutes les armes sont des classes concrtes. Pour changer darme, chaque personnage appelle la mthode setArme() qui est dfinie dans la superclasse Personnage. Lors dun combat, la mthode utiliserArme() est appele sur larme courante dun personnage donn afin dinfliger de grands dommages corporels un autre personnage.

classe abstraite
Personnage
ComportementArme arme ; combattre(); setArme(ComportementArme a) { this.arme = a; }

Un Personnage A-UN ComportementArme.


Troll

Roi
combattre() { ... }

Chevalier
combattre() { ... } combattre() { ... }

Reine
combattre() { ... }

<<interface>> ComportementArme
utiliserArme();

ComportementEpee
utiliserArme() { // implmente brandir une pe}

ComportementArcEtFleches
utiliserArme() { // implmente tirer une flche }

objet TE QUELce R O P M I N a Notez que plmenter linterf mple un im e x e it a r r Pa pour entArme. ou une Comportemune brosse dents trombone, che. canne p
34
Chapitre 1

utiliserArme() { // implmente porter un coup de poignard }

ComportementPoignard

utiliserArme() { // implmente frapper avec une hache }

ComportementHache

introduction aux Design Patterns

Solutions
vosyour crayons Sharpen pencil
Dans la liste ci-aprs, quels sont les inconvnients utiliser lhritage pour dfinir le comportement de Canard ? (Plusieurs choix possibles.)

C. Il est difficile de connatre tous les comportements des canards. A. Le code est dupliqu entre les sous-classes. B. Les changements de comportement au moment de D. Les canards ne peuvent pas voler et cancaner en mme temps. lexcution sont difficiles. E. Les modifications peuvent affecter involontairement dautres canards. C. Nous ne pouvons pas avoir de canards qui dansent.

C V O L T O N E L U 19 V O C A B U L A I R E E R T C
20

16

F F I C H E R R 5 6 A E L M X E 9 10 E N C A P S U L W O E R O M R R R P I E 12 D K O E S S N I C T E 15 I C H
17

B A E Z
11

O B J E T B 13 S T R A T E R 14 V R A N G E M E N T

M O D I F N T 8 E A R E S P R I F A C E G I E

C 18 G O U R O U R R E N N T

O M P

O R T

E M E

vosyour crayons Sharpen pencil

Quels sont les facteurs qui peuvent induire des changements dans vos applications ? Votre liste est peut-tre diffrente, mais voici quelquesunes de nos rponses. Est-ce que cela vous rappelle quelque chose ?

Mes clients ou mes utilisateurs dcident quils veulent autre chose ou quils ont besoin dune nouvelle fonctionnalit. Mon entreprise a dcid quelle allait changer de systme de SGBD et quelle allait galement acheter ses donnes chez un autre fournisseur dont le format est diffrent. Argh ! La technologie volue et nous devons mettre jour notre code afin dutiliser dautres protocoles. Aprs tout ce que nous avons appris en construisant notre systme, nous aimerions revenir en arrire et intgrer quelques amliorations.
vous tes ici

35

2 le pattern Observateur

Tenez vos g objets au courant

H Kevin, jappelle tout le monde pour dire que la runion du groupe Patterns a t repousse dimanche soir. On va parler du pattern Observateur. Cest le meilleur, Kevin. Cest le MEILLEUR je te dis !

Ne manquez plus jamais rien dintressant ! Nous avons un pattern qui


met vos objets au courant quand il se passe quelque chose qui pourrait les concerner. Ils peuvent mme dcider au moment de lexcution sils veulent rester informs. Observateur est lun des patterns le plus utilis dans le JDK et il est incroyablement utile. Avant la fin de ce chapitre, nous tudierons galement les relations un--plusieurs et le faible couplage (oui, oui, nous avons dit couplage). Avec Observateur, vous serez lattraction de la soire Patterns.

nouveau chapitre

37

une station mtorologique

Flicitations ! Votre quipe vient de remporter le march de la construction de la station mtorologique de dernire gnration, consultable en ligne, de MtoExpress, SA.

tation es e s c h a rg ire notre s u tr s n o c r Cahier d onn pou ne ! ble en lig t slecti a z lt e u v s a n o s c u ns ! Vo ration et en Flicitatio ue de dernire gn eteo (brev oment M s iq e e g n lo n o o r mto objet D s un m sur notre rologique e o Nous s t a b m a s r e n s hrique). itio p d s n o o c tm s a La station le n e trois io i enregistr gromtrie et press a dabord u ir q n , r ) u s r o f u i o c , hy simples, cation qu mprature une appli rvisions p z t ie e donn (te s r e c u s q bjet que vou , statisti re que lo u s e m aimerions onditions actuelles t l au fur e :c tes. temps re affichages n e r plus rcen u s jo le s e is m n ss veut don tous trois toExpre quiert les c M a . o le te ib e s n sent DonneesM it tre exte dveloppeurs puis o d o t nm autres Nous ette statio ur que les srer directement. o p I P De plus, c A e et les in ialiser un ffichages commerc a PI ! s e r p o r urs p iez cette A s le is r tier : n e r s u li o a f r vous modle m our t e n u e q ll s e n c x io cturer p voir un e souhaiter s de les fa : vous aincu da n v o n y o c o t v s r e fin ress ous p MtoExp lients accrochs, n t le meilleur pour la c E s t. une fois le age quils utilisen h c fi f a . e s tion et chaqu ck option de concep to s ts n n e e s m u y c serez pa e vos do impatienc c e v a s n ndo Nous atte . ion alpha s votre ver ent, Cordialem de rs source gan, PDG a ie h R c p fi u s o le L Jeanr chrono voyons pa n e s u o v P.S. Nous eteo. DonneesM
38
Chapitre 2

press, SA MtoEx ade de la Torn e 100, all al sc Pa int98000 Sa

le pattern observateur

Vue d e nsemble de l a pplication


Les trois composants du systme sont la station mto (lquipement physique qui recueille les donnes sur le temps), lobjet DonneesMeteo (qui mmorise les donnes provenant de la station et actualise laffichage) et laffichage lui-mme que les utilisateurs consultent pour connatre les conditions mtorologiques actuelles.

uelles est lun Conditions act hages possibles. des trois affic ut galement Lutilisateur pestatistiques et demander des des prvisions.

Capteur dhumidit

Extrait les donnes

affiche

Conditions actuelles
Temp: 22 Hygro: 60 Pression:

Capteur de temprature

Station mto
Capteur de pression

Objet DonneesMeteo Dispositif daffichage

Ce que MtoExpress fournit

Ce que nous implmentons

Lobjet DonneesMeteo sait comment communiquer avec la station physique pour obtenir les donnes jour. Il actualise alors laffichage de trois lments diffrents : Conditions actuelles (affiche la temprature, lhygromtrie et la pression atmosphrique), Statistiques et Prvisions.

Si nous choisissons de laccepter, notre tche consiste crer une application qui utilise lobjet DonneesMeteo pour actualiser ces trois affichages : conditions actuelles, statistiques et prvisions.

vous tes ici

39

la classe DonneesMeteo

lintrieur de la classe DonneesMeteo


Le lendemain matin, les fichiers source de DonneesMeteo arrivent comme promis. Nous jetons un coup dil au code qui semble relativement simple :

DonneesMeteo

ggetTemperature() getHumidite() getPression() actualiserMesures() // autres mthodes

les s mesures ression le t n e n r et p etou thodes rrature, humidit m is o r t s p Ce tes : tem ces plus rcenrique. MMENT teo O C h p s ir o o v m a t e a de s eesM pas besoines ; lobjet Donn informations s n o v a n s ect Nous station le sont aff variables ment obtenir de la sait com jour.
/* * Cette mthode est appele * chaque fois que les mesures * ont t mises jour * */ public void actualiserMesures() { // Votre code ici }

eo et DonneesMetque bj lo e d s ur pe Les dvelop un indice propos de ce nous ont laiss ter... nous devons ajou
Souvenez-vous que Conditi que lUN des trois afficha ons actuelles nest ges possibles.

DonneesMeteo.java

Conditions actuelles
Temp : 22 Hygro : 60 Pression :

Notre tche consiste implmenter la mthode actualiserMesures() pour quelle mette jour les trois affichages : conditions actuelles, statistiques et prvisions.

Dispositif daffichage

40

Chapitre 2

le pattern observateur

Rcapitulons...
Les spcifications de MtoExpress ntaient pas des plus claires et nous devons plus ou moins deviner ce quil faut faire. Alors, que savons-nous jusqu maintenant ?

R La classe DonneesMeteo a des mthodes daccs


pour trois valeurs de mesures : temprature, hygromtrie et pression atmosphrique.

getTemperature() getHumidite() getPression()

R La mthode actualiserMesures() est appele chaque

fois quune nouvelle mesure est disponible. (Nous ne savons pas comment cette mthode est appele, et peu nous importe ; nous savons simplement quelle lest.

actualiserMesures()

Statistiques
Avg. temp: 16 Min. temp: 10 Max. temp: 26

Conditions actuelles

R Nous devons implmenter trois affichages qui utilisent les donnes mtorologiques : un affichage des conditions actuelles, un affichage des statistiques et un affichage des prvisions. Ils doivent tre mis jour chaque fois que DonneesMeteo acquiert de nouvelles donnes.

Temp : 22 Hygro : 60 Pression :

Affichage Deux

Affichage Un

Prvisions

TT T

Affichage Trois

R Le systme doit tre extensible : dautres dveloppeurs


pourront crer des affichages personnaliss et les utilisateurs pourront ajouter ou retirer autant dlments quils le souhaitent lapplication. Actuellement, nous ne connaissons que les trois types daffichage initiaux (conditions actuelles, statistiques et prvisions).

?
Affichages futurs

vous tes ici

41

premier essai de la station mto

Premier essai pifomtrique de station mto


Voici une premire possibilit dimplmentaction : nous suivons lindication des dveloppeurs de MtoExpress et nous ajoutons notre code la mthode actualiserMesures() :

public class DonneesMeteo { // dclaration des variables dinstance public void measurementsChanged() { float temp = getTemperature(); float humidite = getHumidite(); float pressure = getPression();

Obtenir les mesures les plus rcentes en appelant les mthodes get de DonneesMeteo (dj implmentes). Actualiser les affichages...

affichageConditions.actualiser(temp, humidite, pression); affichageStats.actualiser(temp, humidite, pression); affichagePrevisions.actualiser(temp, humidite, pression); } // autres mthodes de DonneesMeteo }

lment pour Appeler chaque son affichage en mettre jour t les mesures les lui transmettan plus rcentes.

vos crayons Sharpen your pencil


Daprs notre premire implmentation, quels noncs suivants sont vrais ? (Plusieurs rponses possibles.)

A. Nous codons des implmentations


concrtes, non des interfaces.

D. Les lments daffichage nimplmentent


pas une interface commune.

B. Nous devons modifier le code pour


chaque nouvel lment daffichage.

E. Nous navons pas encapsul les parties


qui varient.

C. Nous navons aucun moyen dajouter


(ou de supprimer) des lments daffichage au moment de lexcution.

F. Nous violons lencapsulation de la classe


DonneesMeteo.

42

Chapitre 2

le pattern observateur

Qu est ce qui cloche dans notre implmentation ?


Repensez tous les concepts et les principes du chapitre 1...

public void actualiserMesures() { float temp = getTemperature(); float humidite = getHumidite(); float pression = getPression();

Point de variation : nous devons lencapsuler.

affichageConditions.actualiser(temp, humidite, pression); affichageStats.actualiser(temp, humidite, pression); affichagePrevisions.actualiser(temp, humidite, pression); }

En codant des implmentations concrtes, nous navons aucun moyen dajouter ni de supprimer des lments sans modifier le programme.
Hum, je sais que je suis nouveau ici, mais vu quon est dans le chapitre sur le pattern Observateur, on pourrait peut-tre commencer lutiliser ?

Au moins, nous semblons utiliser une interface commune pour communiquer avec les affichages... Ils ont tous une mthode actualiser() qui lit les valeurs de temp, humidite et pression.

Jetez un coup dil au pattern Observateur, puis revenez en arrire et imaginez comment nous pourrions lexploiter pour notre application mtorologique.

vous tes ici

43

faites connaissance avec le pattern Observateur

Faites connaissance avec le pattern Obser vateur


Vous savez comment fonctionne un abonnement un journal ou un magazine :
1

Un diteur se lance dans les affaires et commence diffuser des journaux. Vous souscrivez un abonnement. Chaque fois quil y a une nouvelle dition, vous la recevez. Tant que vous tes abonn, vous recevez de nouveaux journaux. Quand vous ne voulez plus de journaux, vous rsiliez votre abonnement. On cesse alors de vous les livrer. Tant que lditeur reste en activit, les particuliers, les htels, les compagnies ariennes, etc., ne cessent de sabonner et de se dsabonner.

Manquer ce qui se passe Objectville ? Jamais!Bien sr quon va sabonner !

44

Chapitre 2

le pattern observateur

Diffusion + Souscription = pattern Obser vateur


Si vous comprenez ce quest labonnement un journal, vous avez compris lessentiel du pattern Observateur, sauf que nous appelons lditeur le SUJET et les abonns les OBSERVATEURS. Regardons de plus prs :

lobjet Sujet chande s e nn do les nd ua Q en sont informs. gent, les observateurs


gre une Lobjet Sujet nque. donne quelco
2 2
int

Les observateurs ont souscrit un abonnement (se sont enregistrs) auprs du Sujet pour recevoir les mises jour quand les donnes du Sujet changent.

Ob

n jet Chie

2 2
Ob
jet Chat

Ob

jet Sujet

Quand les donnes changent, les nouvelles valeurs sont communiques aux observateurs.
pas un Cet objet nest n est pas observateur : il les donnes inform quand nt. du Sujet change

Ob

is jet Sour

Objets Observateur

Ob

rd jet Cana

vous tes ici

45

une journe dans la vie du pattern Observateur

Une journe dans la vie du pattern Obser vateur


i mo z 2 re ist g e nr int O /e bj s i et Sujet r c us so
Ob
n jet Chie

Un objet Canard arrive et dit au Sujet quil veut devenir observateur.


Canard veut absolument participer laction : tous ces ints que le Sujet envoie chaque fois que son tat change ont lair bien allchants...

Ob

je

jet Chat

rd Ob jet Cana

Ob

is jet Sour
Observateurs

Lobjet Canard est maintenant un observateur officiel.


Canard est remont bloc... il est sur la liste et il attend avec la plus grande impatience la prochaine notification pour recevoir un int.

bj et Sujet
Ob

int

Ob

n jet Chie

rd jet Cana

Ob

jet Chat

Ob

is jet Sour
Observateurs

Le Sujet a une nouvelle valeur !


Maintenant, Canard et tous les autres observateurs sont informs que le Sujet a chang.

8 8 8 8
Ob Ob
n jet Chie

bj et Sujet

int

rd jet Cana

Ob

jet Chat

Ob

is jet Sour
Observateurs

46

Chapitre 2

le pattern observateur

Lobjet Souris demande tre supprim de la liste des observateurs.


Lobjet Souris reoit des ints depuis des lustres et il en a assez. Il dcide donc quil est temps de cesser dtre observateur.

bj et Sujet

int

Ob

n jet Chie

su pp rim rd ez Ob jet Cana /d s ab on ne z-m oi


Ob

Ob

jet Chat

is jet Sour
Observateurs

Sortie de Souris !
Le Sujet accuse rception de la requte de Souris et la supprime de la liste des observateurs.

bj et Sujet
Ob

int

Ob

n jet Chie

rd jet Cana

Ob

jet Chat

Ob

is jet Sour
Observateurs

Le Sujet a un nouvel int.


Tous les observateurs en reoivent notification, sauf Souris qui nest plus abonn. Ne le dites personne, mais tous ces ints lui manquent secrtement... Peuttre redemandera-t-il un jour tre un observateur.
O

14

14 14 14
Ob Ob
n jet Chie

bj et Sujet

int

rd jet Cana

Ob

jet Chat

Ob

is jet Sour
Observateurs

vous tes ici

47

comdie express

Comdie express : un sujet d obser vation


Dans le sketch daujourdhui, deux dveloppeurs daprs la bulle technologique rencontrent en personne un chasseur de ttes...
Bonjour, cest Lo. Je cherche un poste de dveloppeur Java. Jai cinq ans dexprience et... Oui, oui, comme tout le monde, bb. Je te mets sur ma liste de dveloppeurs Java. Ne mappelle pas, cest moi qui tappellerai !

2
Chasseur de ttes/Sujet

1
Dveloppeur n1
Bonjour, Maud lappareil. Jai crit un tas de systmes EJB. Je suis intresse par tout poste en dveloppement Java. Je tajoute la liste, tu seras au courant en mme temps que tout le monde.

4 3
Dveloppeur n2 48
Chapitre 2

Sujet

le pattern observateur Pendant ce temps, la vie continue pour Lo et Maud. Si un poste en Java se prsente, ils en seront informs. Aprs tout, ils sont observateurs.

Merci. Jenvoie tout de suite mon CV. Ce type est vraiment une enflure. Quil aille se faire pendre. Je vais chercher moi-mme.

H, les observateurs ! Il y a une opportunit chez JavaBeans-R-Us. Sautez dessus ! Ne la laissez pas passer ! Blablabla... Plein de sous sur mon compte en banque !

7
Observateur Observateur

6
Sujet
Arghhh !!! coute-moi bien, Maud. Tu ne travailleras plus jamais dans cette ville si jai mon mot dire. Je te supprime de mon rpertoire !!!

Maud trouve son propre job ! Tu peux me retirer de ta liste, jai trouv mon job moi-mme !

8
Observateur

Sujet
vous tes ici

49

le pattern Observateur : dfinition

Deux semaines plus tard...

Maud aime la vie. Elle ne fait plus partie des observateurs. Elle apprcie galement la prime confortable quelle a touche pour avoir vit lentreprise de payer un chasseur de ttes.

Mais quest devenu notre cher Lo ? Nous avons entendu dire quil avait battu le chasseur de ttes son propre jeu. Non seulement il est toujours observateur, mais il a galement sa propre liste, et il informe ses propres observateurs. Lo est la fois sujet et observateur.

50

Chapitre 2

le pattern observateur

Le pattern Obser vateur : dfinition


Quand vous essayez de reprsenter le pattern Observateur, un service dabonnement un journal est une mtaphore qui permet de bien visualiser le processus. Mais, dans le monde rel, le pattern Observateur est gnralement dfini comme ceci :

Le pattern Observateur dfinit une relation entre objets de type un--plusieurs, de faon que, lorsque un objet change dtat, tous ceux qui en dpendent en soient notifis et soient mis jour automatiquement.
Relions cette dfinition la faon dont nous avons dcrit le pattern :

Le pattern Observateur dfinit une relation un-plusieurs entre des objets. Quand ltat de lun des objets change, tous les objets dpendants en sont informs.

RELATION UN--PLUSIEURS Objet qui contient un tat


8 8 8 8 8
Ob
rd jet Cana

Ob

jet Chat

Mise jour et notification automatiques

Ob

is jet Sour
Observateurs

Sujet et observateurs dfinissent la relation un--plusieurs. Les observateurs sont dpendants du sujet : quand ltat du sujet change, les observateurs en sont informs. Selon le type de notification, lobservateur peut galement tre mis jour avec les nouvelles valeurs. Comme vous le verrez, il y a plusieurs faons dimplmenter le pattern Observateur mais la plupart tournent autour dune conception qui comprend des interfaces Sujet et Observateur. Voyons un peu... vous tes ici

Objets d

pendants

bj et Sujet

int

Ob

n jet Chie

51

faible couplage

Le pattern Observateur : le diagramme de classes


ts es obje L . t e j ur e Su terfac interface poateur et n i l i c i Vo nt cette e observ utilise gistrer comm bonnement. senre silier leur a pour r

Chaque sujet peut avoir plusieurs observateurs


<<interface>>
observateur

eurs Tous les observat implmenter t en iv potentiels do ervateur. Cette linterface Obs une mthode, interface na qu t appele actualiser(), qui es jet change. quand ltat du Su
Observateur
actualiser() <<interface>>

Sujet

enregistrerObservateur() supprimerObservateur() notifierObservateurs()

SujetConcret
enregistrerObservateur() {...}

sujet

ObservateurConcret
actualiser() // autres mthodes spcifiques

implmente Un sujet concret ce Sujet. toujours linterfaes d ajout et Outre les mthod sujet concret de suppression, le thode implmente une m rs() qui notifierObservateu ur tous les sert mettre jo fois que observateurs chaque ltat change.

supprimerObservateur() {...} notifierObservateurs() {...} getEtat() setEtat()

Q: R:

t peut Le sujet concre des mthodes galement avoir so n tat pour accder (des dtails et le modifier . ultrieurement)

Les observateurs concrets peuvent tre nimporte quelle classe qui implmente linterface Observateur. Chaque observateur senregistre auprs dun sujet rel pour recevoir les mises jour.

Quel est le rapport avec la relation un--plusieurs ?

Q: R:

Et que vient faire la dpendance l-dedans ?

Dans le pattern Observateur, le Sujet est lobjet qui contient ltat et qui le contrle. Il y a donc UN sujet avec un tat. En revanche, les observateurs utilisent ltat, mme sils ne le possdent pas. Il y a PLUSIEURS observateurs qui comptent sur le sujet pour les informer de ses changements dtat. Il y a donc une relation entre UN sujet et PLUSIEURS observateurs.

Comme le sujet est le seul propritaire de cette donne, les observateurs en dpendent pour quils soient actualiss quand elle change. Cest une conception objet plus saine que si lon permettait plusieurs objets de contrler la mme donne.

52

Chapitre 2

le pattern observateur

Le pouvoir du Faible Couplage


Lorsque deux objets sont faiblement coupls, ils peuvent interagir sans pratiquement se connatre. Le pattern Observateur permet une conception dans laquelle le couplage entre sujets et observateurs est faible. Pourquoi ?
Le sujet ne sait quune chose propos de lobservateur : il implmente une certaine interface (linterface Observateur). Il na pas besoin de connatre, ni la classe concrte de lobservateur, ni ce quil fait ni quoi que ce soit dautre. Nous pouvons ajouter des observateurs tout moment. Comme le sujet dpend uniquement dune liste dobjets qui implmentent linterface Observateur, nous pouvons ajouter des observateurs volont. En fait, nous pouvons remplacer nimporte quel observateur par un autre au moment de lexcution : le sujet continuera ronronner comme si de rien ntait. De mme, nous pouvons supprimer des observateurs nimporte quand. Nous navons jamais besoin de modifier le sujet pour ajouter de nouveaux types dobservateurs. Disons que se prsente une nouvelle classe concrte qui a besoin dtre un observateur. Nous navons pas besoin dajouter quoi que ce soit au sujet pour grer ce nouveau type. Il suffit dimplmenter linterface Observateur dans la nouvelle classe et de lenregistrer en tant quobservateur. Le sujet ne sen soucie aucunement : il continuera diffuser des notifications tous les objets qui implmentent linterface Observateur. Nous pouvons rutiliser les objets et les sujets indpendamment les uns des autres. Si nous avons un autre emploi dun sujet ou dun observateur, nous pouvons les rutiliser sans problme parce quils sont faiblement coupls. Les modifications des sujets naffectent pas les observateurs et inversement. Comme ils sont faiblement coupls, nous sommes libres de les modifier notre guise tant que les objets continuent remplir leurs obligations : implmenter les interfaces

Combien de modifications uvezdiffrentes po ? vous identifier

Principe de conception
Efforcez-vous de coupler faiblement les objets qui interagissent.

Les conceptions faiblement couples nous permettent de construire des systmes OO souples, capables de faire face aux changements parce quils minimisent linterdpendance entre les objets.
vous tes ici

53

planifier la station mto

vos crayons Sharpen your pencil


Avant de continuer, essayez desquisser les classes ncessaires pour implmenter la Station Mto, notamment la classe DonneesMeteo et ses diffrents affichages. Vrifiez que votre diagramme montre la faon dont tous les composants sassemblent et dont un autre dveloppeur pourrait implmenter son propre affichage. Sil vous faut un peu daide, lisez la page suivante : vos collgues sont dj en train de parler de la conception de la Station Mto.

54

Chapitre 2

le pattern observateur

Conversation dans un box


Revenons au projet de Station Mto. Vos collgues ont dj commenc rflchir au problme...

Ann

Alors, comment allons-nous construire ce truc ?

Marie : Eh bien, a aide de savoir que nous utilisons le pattern Observateur. Anne : Oui... mais comment va-t-on lappliquer ? Marie : Mmm. Regardons de nouveau la dfinition :
Le pattern Observateur dfinit une relation entre objets de type un--plusieurs, de faon que, lorsque un objet change dtat, tous ceux qui en dpendent en soient notifis et soient mis jour automatiquement.

Marie : a a lair clair quand on y pense. Notre classe DonneesMeteo est le un, et le plusieurs, ce sont les lments qui affichent les diffrentes mesures. Anne : Cest vrai. La classe DonneesMeteo a videmment un tat... la temprature, lhumidit et la pression atmosphrique, et bien sr ces donnes changent. Marie : Oui, et quand ces donnes changent, il faut notifier tous les lments daffichage pour quils puissent faire ce quils doivent faire avec les mesures. Anne : Super, maintenant je crois que je vois comment on peut appliquer le pattern Observateur notre problme. Marie : Mais il y a encore deux ou trois choses que je ne suis pas sre davoir comprises. Anne : Par exemple ? Marie : Dabord, comment faire pour que les lments daffichage obtiennent les mesures ? Anne : Eh bien, en regardant de nouveau le diagramme du pattern Observateur, si nous faisons de lobjet DonneesMeteo le sujet et que les lments daffichage sont les observateurs, alors les affichages vont senregistrer eux-mmes auprs de lobjet DonneesMeteo pour obtenir les informations dont ils ont besoin, non ? Marie : Oui... Et une fois que la Station Mto est au courant de lexistence dun lment daffichage, elle peut simplement appeler une mthode pour lui transmettre les mesures. Anne : Il faut se souvenir que chaque affichage peut tre diffrent... et cest l que je crois quintervient une interface commune. Mme si chaque composant est dun type diffrent, ils doivent tous implmenter la mme interface pour que lobjet DonneesMeteo sache comment leur transmettre les mesures. Marie : Je vois ce que tu veux dire. Chaque affichage aura par exemple une mthode actualiser() que DonneesMeteo va appeler. Anne : Et actualiser() est dfinie dans une interface commune que tous les lments implmentent...
vous tes ici

55

concevoir la station mto

Concevoir la Station Mto


Comparez ce diagramme avec le vtre.

t. ce Suje . a f r e t in bler familire e r t o n Voici oit vous sem Elle d


<<interface>>

nts Tous nos composa nterface implmentent liin si, Sujet Observateur. A te rface dispose dune inparler quand le commune qui nu de mettre moment est ve eurs. jour les observat
observateurs

Crons galement une interface que tous les lments daffichage implmenteront. Il leur suffira dimplmenter la mthode actualiser().
<<interface>>

Sujet

Observateur
actualiser()
afficher()

<<interface>>

Affichage

enregistrerObservateur() supprimerObservateur() notifierObservateurs()

AffichageConditions
suje t

actualiser() afficher() { // afficher les mesures courantes}

AffichageTiers
actualiser() afficher() { // afficher autre chose en fonction des mesures }

DonneesMeteo enregistrerObservateur() supprimerObservateur() notifierObservateurs() getTemperature() getHumidite() getPression() actualiserMesures()

Ce composant affiche les mesures courante obtenues de lobjet s DonneesMeteo.

AffichageStats
actualiser() afficher() { // afficher moyenne, minimum et maximum }

DonneesMeteo nant implmente mainte linterface Sujet.

Cette classe mmorise les mesures min/moy/ max et les affiche.

AffichagePrevisions
actualiser() afficher() { // afficher la prvision }

Les dveloppeurs peuvent implmenter les interfaces Sujet et Observateur pour crer leur propre affichage.

prvision en Cette classe affiche une baromtre. fonction de ce quindique le


Ces trois lments daffichage devraient galement avoir un pointeur intitul sujet vers DonneesMeteo, mais le diagramme commencerait ressembler un plat de spaghettis.
56
Chapitre 2

le pattern observateur

Implmenter la Station Mto


Nous allons commencer notre implmentation en utilisant le diagramme de classes et en suivant les indications de Marie et dAnne (page 55). Vous verrez bientt dans ce chapitre que Java dispose de ses propres classes pour prendre en charge le pattern Observateur, mais nous allons quand mme mettre les mains dans le cambouis et crer les ntres pour linstant. Si vous pouvez souvent utiliser les classes Java standard, construire les vtres vous apporte plus de souplesse dans de nombreux cas (et ce nest pas si difficile). Commenons donc par les interfaces :

public interface public void public void public void }

Sujet { enregistrerObservateur(Observateur o); supprimerObservateur(Observateur o); notifierObservateurs();

Ces deux mthodes acceptent un Observateur en argument ; autrement dit, lObservateur enregistrer ou supprimer.

Cette mthode est appele pour notifier tous les observateurs que ltat de Sujet a chang.

public interface Observateur { public void actualiser(float temp, float humidite, float pression); }

Les valeurs de ltat que les Observateurs obtiennent du Sujet quand une mesure change

public interface Affichage { public void afficher(); }

Linterface Observateur tant implmente par tous les observateurs, ils doivent tous implmenter la mthode actualiser(). Ici, nous appliquons lide de Marie et Anne et nous transmettons les mesures aux observateurs.

Linterface Affichage ne contient quune mthode, afficher(), que nous appellerons quand un lment devra tre affich.

Musclez vos neurones

Marie et Anne ont pens que transmettre directement les mesures aux observateurs tait la mthode la plus simple pour mettre jour ltat. Pensez-vous que cela soit judicieux ? Indication : est-ce un point de lapplication susceptible de changer dans le futur ? Si cest le cas, le changement sera-t-il bien encapsul ou entranera-t-il des modifications dans de nombreuses parties du code ? Voyez-vous dautres faons de rsoudre le problme de la transmission de ltat mis jour aux observateurs ? Ne vous inquitez pas : nous reviendrons sur cette dcision de conception lorsque nous aurons termin limplmentation initiale. vous tes ici

57

implmenter la station mto

Implmenter linterface Sujet dans DonneesMeteo


Vous souvenez-vous de notre premier essai dimplmentation de la classe DonneesMeteo au dbut de ce chapitre ? Peut-tre voulez-vous vous rafrachir la mmoire ? Maintenant, il est temps dappliquer le pattern Observateur...
public class private private private private DonneesMeteo implements Sujet { ArrayList observateurs; float temperature; float humidite; float pression;

SOUVENEZ-VOUS : les listings ne contiennent pas dinstructions import et package. Tlchargez le code source complet sur le site web du livre. Vous trouverez lURL page xxxiii de lIntro. DonneesMeteo implmente maintenant linterface Sujet.

List pour Nous avons ajout une Array nous la contenir les observateurs et. crons dans le constructeur
Quand un observateur senregistre, nous lajoutons simplement la fin de la liste. De mme, quand un observateur veut se dsenregistrer nous le supprimons de la liste. Voici le plus intressant. Cest l que nous informons les observateurs de ltat du sujet. Comme ce sont tous des objets Observateur, nous savons quils implmentent tous actualiser() et nous savons donc comment les en notifier.

public DonneesMeteo() { observateurs = new ArrayList(); } public void enregistrerObservateur(Observateur o) { observateurs.add(o); } public void supprimerObservateur(Observateur o) { int i = observateurs.indexOf(o); if (i >= 0) { observateurs.remove(i); } }

Ici, nous implmentons linterface Sujet.

public void notifierObservateurs() { for (int i = 0; i < observateurs.size(); i++) { Observateur observateur= (Observateur)observateurs.get(i); observateur.actualiser(temperature, humidite, pression); nd } servateurs qua b o s le s n } io if o t Mt des us no public void actualiserMesures() { notifierObservateurs(); }

No de la Station nous recevons jour. mesures mises

public void setMesures(float temperature, float humidite, float pression) { this.temperature = temperature; this.humidite = humidite; Bon, nous voulions accompagner chaque exemplaire this.pression = pression; de ce livre dune jolie petite station mto mais actualiserMesures(); lditeur a refus. Au lieu de lire les donnes sur une } // autres mthodes de DonneesMeteo }

vraie station, nous allons donc utiliser cette mthode pour tester nos affichages. Ou bien, pour le plaisir, vous pouvez crire un programme qui rcupre des relevs sur le web.

58

Chapitre 2

le pattern observateur

Maintenant, construisons les affichages


Maintenant que nous avons amlior la classe DonneesMeteo, il est temps de construire les affichages. MtoExpress en a command trois : un pour les conditions actuelles, un pour les statistiques et un pour les prvisions. Jetons un coup dil au premier ; une fois que vous aurez une bonne ide de son fonctionnement, regardez le code des autres dans les fichiers que vous avez tlchargs. Vous verrez quil est trs similaire.

e implment e g a h obtenir ic f ir f o Cet a eur pour pouv t Observatgements de lobje les chan Meteo. Donnees

Il implmen e galement Affichage t c a r besoin de tou notre API va avoir implmenter s les lments pour cette interf ace.

public class private private private

AffichageConditions implements Observateur, Affichage { float temperature; float humidite; Sujet donneesMeteo; ttons

public AffichageConditions(Sujet donneesMeteo) { this.donneesMeteo = donneesMeteo; donneesMeteo.enregistrerObservateur(this); }

au constructeur Nous transme lobjet DonneesMeteo (le Sujet) et nous lutilisons pour enregistrer laffichage en tant quobservateur.

public void actualiser(float temperature, float humidite, float pression) { this.temperature = temperature; this.humidite = humidite; pele, nous Quand actualiser() est ap afficher(); ture et humidit et

sauvegardons tempra nous appelons afficher().

public void afficher() { System.out.println(Conditions actuelles : + temperature + degrs C et + humidite + % dhumidit); } }

fiche simplement les mesures La mthode afficher() af les plus rcentes. de temprature et dhumidit

Il ny athere pas are de no

Q: R:

Dumb Questions Questions Stupides


faons de concevoir laffichage des donnes. Nous les verrons quand nous aborderons le pattern Modle-VueContrleur.

Est-ce quactualiser() est le meilleur endroit pour appeler afficher() ?

Exact, mais si nous voulons plus tard nous supprimer nous-mme de la liste des observateurs, ce sera pratique davoir dj une rfrence au Sujet.

R:

Dans cet exemple simple, cela a un sens dappeler afficher() quand les valeurs changent. Mais vous avez raison : il y a de bien meilleures

Q:

Pourquoi avez-vous mmoris une rfrence au Sujet ? On dirait que vous ne lutilisez plus aprs le constructeur ? vous tes ici

59

tester la station mto

Mettre en route la Station Mto


1

crivons dabord un programme pour tester. La Station Mto est prte fonctionner : il ne manque plus quun peu de code pour coller tous les morceaux. Voici notre premire tentative. Nous y reviendrons plus loin dans louvrage et ferons en sorte que tous les composants soient facilement enfichables via un fichier de configuration. Pour linstant, voyons le fonctionnement global :
public class StationMeteo { public static void main(String[] args) { DonneesMeteo donneesMeteo = new DonneesMeteo();

Crons bjet dabord lo teo. DonneesMe

Si vous ne voulez pas tlcharger le code, vous pouvez mettre ces deux lignes en commentaire et lexcuter. }
}

AffichageConditions affichageCond = new AffichageConditions(donneesMeteo); AffichageStats affichageStat = new AffichageStats(donneesMeteo); AffichagePrevisions affichagePrev = new AffichagePrevisions(donneesMeteo); donneesMeteo.setMesures(26, 65, 1020); donneesMeteo.setMesures(28, 70, 1012); donneesMeteo.setMesures(22, 90, 1012);

Simuler les nouvelles mesures.

Crer les trois affichages et leur transmettre lobjet DonneesMeteo.

Excutez le code et admirez la magie du pattern Observateur


Fichier dition Fentre Aide Tempte

%java StationMeteo Conditions actuelles : 26 degrs C et 65.0 % dhumidit Temprature Moy/Max/Min = 26.0/26.0/26.0 Prvision : Amlioration en cours ! Conditions actuelles : 28 degrs C et 70.0 % dhumidit Temprature Moy/Max/Min = 27.0/28.0/26.0 Prvision : Le temps se rafrachit Conditions actuelles : 22 degrs C et 90.0 % dhumidit Temprature Moy/Max/Min = 25.0/28.0/22.0 Prvision : Dpression bien installe %

60

Chapitre 2

le pattern observateur

vos crayons Sharpen your pencil


Le PDG de MtoExpress, Jean-Loup Ragan, vient de vous appeler : ils ne peuvent pas commercialiser leur station sans un affichage de lhumidex. Voici les dtails : Lhumidex est un indice qui combine la temprature et lhumidit afin de dterminer la temprature apparente (la chaleur rellement ressentie). Pour le calculer, on prend la temprature, T, et lhumidit relative (HR) et on applique cette formule :

humidex = 16.923 + 1.85212 * 10-1 * T + 5.37941 * HR - 1.00254 * 10-1 * T * HR + 9.41695 * 10-3 * T2 + 7.28898 * 10-3 * HR2 + 3.45372 * 10-4 * T2 * HR - 8.14971 * 10-4 * T * HR2 + 1.02102 * 10-5 * T2 * HR2- 3.8646 * 10-5 * T3 + 2.91583 * 10-5 * HR3 + 1.42721 * 10-6 * T3 * HR + 1.97483 * 10-7 * T * HR3 - 2.18429 * 10-8 * T3 * HR2 + 8.43296 * 10-10 * T2 * HR3 - 4.81975 * 10-11 * T3 * HR3
Allez-y, tapez-la ! Non, cest une blague. Ne vous inquitez pas, vous navez pas besoin de taper cette formule ; il suffit de crer votre propre fichier AffichageHumidex.java et dy copier celle que vous trouverez dans humidex.txt. Attention, cette formule ne fonctionne quavec des degrs Fahrenheit. Vous devrez insrer avant une ligne pour la conversion en degrs Celsius : t = (t - 32) * 5 / 9; Vous pouvez tlcharger humidex.txt sur le site de ce livre

Vous pouvez tlcharger humidex.txt sur le site de ce livre

Comment a marche ? Ouvrez Mtorologie la tte la premire ou essayez de demander quelquun chez Mto France (ou encore essayez une recherche sur Google). Quand vous aurez termin, le rsultat devrait ressembler celui-ci :

Fichier dition Fentre Aide ArcEnCiel

g chan a i qu e i ce ffichag c i o V s la dan

%java StationMeteo Conditions actuelles : 26 degrs C et 65.0 % dhumidit Temprature Moy/Max/Min = 26.0/26.0/26.0 Prvision : Amlioration en cours ! Lhumidex est de 28,3 Conditions actuelles : 28 degrs C et 70.0 % dhumidit Temprature Moy/Max/Min = 27.0/28.0/26.0 Prvision : Le temps se rafrachit Lhumidex est de 30,5 Conditions actuelles : 22 degrs C et 90.0 % dhumidit Temprature Moy/Max/Min = 25.0/28.0/22.0 Prvision : Dpression bien installe Lhumidex est de 28,7 %

vous tes ici

61

face face : sujet et observateur

Face face :

Au programme ce soir : Un Sujet et un Observateur saffrontent sur la bonne faon de transmettre les informations sur ltat lObservateur.

Sujet
Je suis heureux que nous ayons enfin loccasion de bavarder en personne.

Observateur

Vraiment ? Je ne savais pas quon avait tant dimportance pour vous, nous autres observateurs. Eh bien, je fais mon travail, non ? Je vous tiens en permanence au courant... Ce nest pas parce que je ne sais pas vraiment qui vous tes que je ne me soucie pas de vous. Et dailleurs, je sais ce qui est le plus important vous implmentez linterface Observateur. Bon, bon, mais ce nest quune petite partie de ce que je suis. De toute faon, jen sais beaucoup votre sujet... Ah oui ? Par exemple ? Eh bien comme vous nous transmettez toujours vos changements dtat, nous savons toujours ce qui se passe en vous. Cest parfois un peu gnant... Oh ! Excuuusez-moi. Il faut que je transmette mon tat avec mes notifications pour que vous autres feignants dobservateurs sachez ce qui sest pass ! Attendez une minute ! Dabord nous ne sommes pas feignants. Cest juste que nous avons autre chose faire entre vos prcieuses notifications, Monsieur le Sujet. Et deuximement, pourquoi vous ne nous laissez pas nous adresser vous pour connatre votre tat au lieu de lenvoyer tout le monde ? Eh bien... Je suppose que cela pourrait marcher. Mais il faudrait que je mouvre encore un peu plus pour que vous puissiez obtenir cet tat. Ce serait quelque peu dangereux. Je ne peux pas vous laisser entrer et fouiner partout.

62

Chapitre 2

le pattern observateur

Sujet

Observateur
Pourquoi ne pas simplement crire des mthodes get publiques qui nous permettraient de retirer ltat dont nous avons besoin ?

Oui, je pourrais vous laisser tirer mon tat. Mais est-ce que ce ne serait pas moins pratique pour vous ? Si vous devez me contacter chaque fois que vous voulez quelque chose, il vous faudra une quantit dappels de mthodes pour obtenir tout ltat dont vous avez besoin. Cest pourquoi je prfre pousser... En procdant ainsi, vous avez tout ce quil vous faut dans une seule notification. Ne vous poussez pas du col comme a ! Il y a tellement de types dobservateurs diffrents que vous navez aucun moyen danticiper ce dont nous avons besoin. Laissez-nous venir vers vous pour obtenir votre tat. Comme a, si certains dentre nous nont besoin que dune petite partie de ltat, nous ne sommes pas forcs de lavoir en entier. Cela faciliterait aussi les modifications. Imaginons que vous voluez et que vous ajoutez quelque chose ltat. Si nous tirons, il devient inutile de modifier les appels des mthodes de mise jour sur chaque observateur. Il suffit de vous modifier pour permettre dautres mthodes get daccder ltat supplmentaire. Oui, je vois lintrt des deux techniques. Jai remarqu quil existe un pattern Observateur intgr Java et quil permet de tirer ou de pousser. Vraiment ? Je suppose que nous allons le voir bientt.... Parfait... Je vais peut tre voir un bon exemple qui ira dans votre sens et je changerai davis. Nous, nous mettre daccord sur quelque chose ? On peut toujours esprer.

vous tes ici

63

le pattern observateur de java

Utiliser le pattern Observateur de Java


Jusquici, nous avons crit notre propre code pour appliquer le pattern Observateur, mais Java le prend en charge nativement dans plusieurs de ses API. Le cas le plus gnral est constitu de linterface Observer et de la classe Observable du package java. util. Elles sont trs similaires nos interfaces Sujet et Observateur, mais elles vous apportent un certain nombre de fonctionnalits toutes prtes . Comme vous allez le voir, vous pouvez choisir de pousser ou de tirer pour mettre jour vos observateurs. Pour avoir une vue densemble de java.util.Observer et de java.util.Observable, tudions cette conception retravaille de la StationMeteo :

Avec le support intgr de Java, il suffit dtendre Observable et de lui indiquer quand notifier les observateurs. LAPI fait le reste votre place.

s morise tous m le b va r se b La classe Oteurs et leur envoie de vos observa ns votre place. notificatio
Observable est une CLASSE, non une interface. DonneesMeteo doit donc tendre Observable.
Observable
addObserver() deleteObserver() notifyObservers() setChanged()
suj et

bler evrait vous semmme d e ss a cl e t et C effet, cest la gramme familire. En re prcdent diaet t no ns celui a d ue q que son nom partir uf sa , es ss a cl e d changent ! de la mthode , nous implmentons ualiser() de maintenant bserver et act linterface Ot ). devient upda e(
<<interface>>

observateurs

Observer

update()

Nous avons omis linterface Affichage, mais tous les affichages continuent limplmenter.
AffichagePrevisions
update() afficher ()

AffichageGeneral
update() afficher()

AffichageStats
update() afficher ()

! getTemperature() abituel getHumidite() h n i t s y Ceci e bon, nous ne getPression() tenez rons dans u viend de... e nous pouvons secon Voici notre Sujet, rqu lObservable. maintenant appelebeso in des Nous navons plus rer(), supprimer() st mthodes enregi us hritons ce et notifier() : no la superclasse. comportement de
64

DonneesMeteo

ns apporter la Il y aura quelques modificatio ervateurs concrets, mthode update() dans les obs base... nous avons mais cest le mme concept de avec une mthode ne, une interface Observer commu Sujet. update() qui est appele par le

Chapitre 2

le pattern observateur

Comment fonctionne le pattern Obser vateur de Java


Le pattern Observateur intgr Java fonctionne un peu diffremment de limplmentation que nous avons choisie pour notre Station Mto. La diffrence la plus vidente rside dans le fait que DonneesMeteo (notre sujet) tend maintenant la classe Observable et hrite (entre autres) des mthodes addObserver(), deleteObserver() et notifyObservers(). Voici comment nous utilisons la version de Java :

Pour quun objet devienne Observateur...


Comme dhabitude, implmenter lInterface observateur (cette fois java.util. Observer) et appeler la mthode addObserver() de lobjet Observable. De mme, pour supprimer un observateur, il suffit dappeler deleteObserver().

Pour que lObservable envoie des notifications...


Tout dabord, il faut quil devienne Observable en tendant la superclasse java.util. Observable. Puis lon procde en deux tapes :
1 Vous devez dabord appeler la mthode setChanged()

pour signifier que ltat de votre objet a chang


2

Puis vous appelez lune des deux mthodes notifyObservers() :

soit notifyObservers()

soit

notifyObservers(Object arg)

version, Dans cette n ne un objet doest transmis arbitraire servateur chaque ob otification. lors de la n

Pour quun observateur reoive des notifications...


Il implmente la mthode update() comme auparavant, mais la signature de la mthode diffre lgrement :
update(Observable o, Object arg) objet donne

Le Sujet qui a envoy la notification est pass ici en argument.

Lobjet donne qui a t transmis notifyObservers(), ou null si aucun objet na t spcifi.

Si vous voulez pousser les donnes vers les observateurs, vous pouvez les transmettre sous forme dobjet donne la mthode notifyObservers(arg). Si non, lObservateur doit tirer les donnes de lobjet Observable qui lui a t transmis. Comment ? Retravaillons le code de la Station Mto et vous verrez. 65

vous tes ici

dans les coulisses

Attendez, avant de commencer, pourquoi a-ton besoin de cette mthode setChanged() ? Elle nexistait pas auparavant.

La mthode setChanged() sert signifier que ltat a chang et que notifyObservers(), quand elle est appele, doit mettre jour les observateurs. Si notifyObservers() est appele sans quon ait dabord appel setChanged(), les observateurs ne seront PAS notifis. Jetons un il dans les coulisses dObservable pour voir comment cela fonctionne :

Dans les coulisses


setChanged() { changed = true } notifyObservers(Object arg) { if (changed) { pour chaque observateur de la liste{ appeler update(this, arg) } changed = false } } notifyObservers() { notifyObservers(null) }

) La mthode setChanged( positionne un drapeau changed true. tifie les notifyObservers() ne no r du va observateurs que si la leu drapeau est TRUE.

de lae. e d o c o servabl Pseud Ob classe

Puis elle notifie les observateurs et remet le drapeau false.

Pourquoi est-ce ncessaire ? La mthode setChanged() est conue pour vous autoriser plus de souplesse dans la faon dont vous mettez jour les observateurs en vous permettant doptimiser les notifications. Par exemple, dans le cas de notre station mto, imaginez que nos capteurs soient si sensibles que les tempratures affiches varient constamment de quelques diximes de degr. Lobjet DonneesMeteo pourrait alors mettre des notifications en permanence. Mais si nous voulons mettre des notifications moins frquentes, nous nappellerons setChanged() que lorsque la temprature aura augment ou diminu dun demi degr. Vous nutiliserez peut-tre pas trs souvent cette fonctionnalit, mais elle existe en cas de besoin. Dans tous les cas, vous devez appeler setChanged() pour que les notifications fonctionnent. Vous pouvez galement utiliser la mthode clearChanged(), qui rinitialise le drapeau changed faux et la mthode hasChanged() qui vous indique la valeur courante du drapeau. 66

Chapitre 2

le pattern observateur

Retravailler la Station Mto avec le pattern intgr


Rcrivons dabord DonneesMeteo pour quelle tende java.util.Observable
Assurons-nous dimporter les bonnes classes Observer / Observable.
Nous navons plus besoin de mmoriser nos observateurs ni de grer leur ajout et leur suppression : la superclasse sen charge. Nous avons donc supprim les mthodes correspondantes.
4

Maintenant, nous tendons Observable.

import java.util.Observable; import java.util.Observer; public class private private private DonneesMeteo extends Observable { float temperature; float humidite; float pression;

Notre constructeur na pas besoin de crer une structure de donnes pour contenir les observateurs.

public DonneesMeteo() { } public void actualiserMesures() { //setChanged(); notifyObservers(); }

Remarquez que nous envoyons un objet donne avec lappel de notifyObservers(). Autrement dit, nous appliquons le modle TIRER.

public void setMesures(float temperature, float humidite, float pression) { this.temperature = temperature; this.humidite = humidite; this.pression = pression; 5 Nous appelons maintenant actualiserMesures(); setChanged() pour indiquer que } ltat a chang, avant dappeler public float getTemperature() { return temperature; } public float getHumidite() { return humidite; } 6 public float getPression() { return pression; } }

notifyObservers().

Ces mthodes ne sont pas nouvelles, mais comme nous allons tirer nous avons pens vous rappeler leur existence. Les observateurs les utiliseront pour accder ltat de lobjet DonneesMeteo.
vous tes ici

67

revoir laffichage des conditions actuelles

Rcrivons maintenant AffichageConditions

Nous importons de nouveau les bonnes classes Observer / Observable.


2

Nous implmentons maintenant linterface java.util.Observer

import java.util.Observable; import java.util.Observer; public class AffichageConditions implements Observer, Affichage { Observable observable; private float temperature; 3 private float humidite; public AffichageConditions(Observable observable) { this.observable = observable; observable.addObserver(this); } public void update(Observable obs, Object arg) { if (obs instanceof DonneesMeteo) { DonneesMeteo donneesMeteo = (DonneesMeteo)obs; this.temperature = donneesMeteo.getTemperature(); this.humidite = donneesMeteo.getHumidite(); afficher(); } }

Notre constructeur accepte maintenant un Observable et nous utilisons this pour enregistrer lobjet en tant quobservateur.
4

Nous avons modifi la mthode update() pour quelle accepte en argument la fois un Observable et des donnes optionnelles.

5 public void afficher() { System.out.println(Conditions actuelles : + temperature + degrs C et + humidite + % dhumidit); } }

Dans update(), nous nous assurons dabord que lobservable est de type DonneesMeteo, puis nous appelons ses mthodes get pour obtenir la temprature et lhumidit. Puis nous appelons afficher().

68

Chapitre 2

Le frigo
Exercice

le pattern observateur

La classe AffichagePrevisions a t dcoupe en fragments, et ceuxci ont t affichs dans le dsordre sur la porte du frigo. Pouvezvous les rorganiser pour que le programme fonctionne ? Certaines accolades sont tombes par terre et elles sont trop petites pour quon les ramasse. Nhsitez pas en ajouter autant quil faut !

le vab ser b O ( ons isi v e ePr hag ffic { A ) lic afficher(); pub rvable e s b o
observable.add Observer(this) ;

if (observable instanceof DonneesMeteo) {

ns implements public class AffichagePrevisio Observer, Affichage {


{ afficher() r public void he fic af ur // code po }

(); lle; ression e u t onAc getP essi sMeteo. r p on = nnee essi le = do r P e ier uel dern sionAct s pre
private float pressionActu private float elle = 1012 ; dernierePres sion;
DonneesMeteo donneesMeteo = (DonneesMeteo) observable;

, e observable te(Observabl da up id vo public { Object arg)


servable; import java.util.Ob server; Ob l. import java.uti

vous tes ici

69

excuter le test

Excuter le nouveau code


Par scurit, excutons le nouveau code...
Fichier dition fentre Aide DevoirALaMaison

%java StationMeteo Prvision : Amlioration en cours ! Temprature Moy/Max/Min = 26.0/26.0/26.0 Conditions actuelles : 26 degrs C et 65.0 % dhumidit Prvision : Le temps se rafrachit Temprature Moy/Max/Min = 27.0/28.0/26.0 Conditions actuelles : 28 degrs C et 70.0 % dhumidit Prvision : Dpression bien installe Temprature Moy/Max/Min = 25.0/28.0/22.0 Conditions actuelles : 22 degrs C et 90.0 % dhumidit %

Mmm, avez-vous remarqu une diffrence ? Regardez encore...


Vous voyez les mmes calculs, mais lordre du texte a mystrieusement chang. Que sestil pass ? Rflchissez une minute avant de poursuivre votre lecture...

Ne jamais dpendre de lordre dvaluation des notifications


Dans java.util.Observable, la mthode notifyObservers() est implmente de telle faon que les observateurs sont notifis dans un autre ordre que dans votre propre implmentation. Qui a raison ? Ni lun ni lautre ; nous avons simplement choisi de procder diffremment. Ce qui serait incorrect, en revanche, ce serait dcrire un programme qui dpende dune ordre de notification spcifique. Pourquoi ? Parce que si vous devez modifier les implmentations dObservable/Observer, cet ordre pourrait changer et votre application produirait des rsultats errons. Ce nest pas prcisment ce que lon considre comme un couplage faible.

70

Chapitre 2

le pattern observateur

Est-ce que java.util.Observable nenfreint pas le principe OO qui consiste programmer des interfaces et non des implmentations ?

La face cache de java.util.Obser vable


Oui, bien vu. Comme vous lavez remarqu, Observable est une classe, non une interface. Pis encore, elle nimplmente mme pas une interface. Malheureusement, limplmentation de java.util.Observable prsente un certain nombre de problmes qui limitent son utilit et ses possibilits de rutilisation. Non quelle soit inutile, mais elle prsente de gros dfauts auxquels il convient de faire attention.

Observable est une classe


Nos principes vous ont dj appris que ctait l une mauvaise ide, mais quel est exactement le problme ? Tout dabord, comme Observable est une classe, vous devez la sous-classer. Autrement dit, vous ne pouvez pas ajouter le comportement dObservable une classe existante qui tend dj une autre superclasse. Cela limite son potentiel de rutilisation (la principale raison a priori pour laquelle nous utilisons des patterns). Deuximement, comme il ny a pas dinterface Observable, vous ne pouvez mme pas crer vous-mme une implmentation qui fonctionne correctement avec lAPI Observer Java intgre. Pas plus que vous ne pouvez remplacer limplmentation de java.util par une autre (par exemple une nouvelle implmentation multithread).

Observable protge les mthodes cruciales


Si vous observez lAPI Observable, vous constatez que la mthode setChanged() est protge. Et alors ? Eh bien, cela signifie que vous ne pouvez pas appeler setChanged() tant que vous navez pas sous-class Observable. Autrement dit, vous ne pouvez mme pas crer une instance de la classe Observable et composer avec vos propres objets : vous tes oblig de sous-classer. Cette faon de procder viole un deuxime principe de conception...prfrer la composition lhritage.

Que faire ?
Observable peut correspondre vos besoins si vous pouvez tendre java.util.Observable. Dun autre ct, vous aurez peut-tre besoin de dvelopper votre propre implmentation Comme nous lavons fait au dbut de ce chapitre. Dans les deux cas, vous connaissez bien le pattern Observateur et vous tes bien plac pour travailler avec une API qui lutilise.

vous tes ici

71

observateur et swing

Autres endroits o se trouve le pattern Obser vateur dans le JDK


Limplmentation de Observer/Observable dans java.util nest pas le seul endroit o vous trouverez le pattern Observateur dans le JDK : les JavaBeans et lAPI Swing possdent galement leurs propres implmentations du pattern. ce stade, vous connaissez suffisamment bien Observateur pour explorer tout seul ces API, mais nous allons voir rapidement un exemple simple avec Swing, juste pour le plaisir.

ateur Si le pattern Observ us vo dans les JavaBeans linterface intresse, regardez ener. PropertyChangeList

Un peu de contexte...
Examinons un composant simple de lAPI Swing, le JButton. Si vous regardez rapidement la superclasse de JButton, AbstractButton, vous verrez quelle a un tas de mthodes add et remove. Ces mthodes vous permettent dajouter et de supprimer des observateurs,ou, comme on les appelle dans la terminologie Swing, des auditeurs, afin dtre lcoute des diffrents types dvnements qui se produisent dans le composant Swing. Par exemple, un ActionListener vous permet d couter tous les types dactions qui peuvent tre appliques un bouton, comme les clics. Vous trouverez diffrents types dauditeurs dans toute lAPI Swing.

Une petite application qui change la vie


Bon, notre application est extrmement simple. Nous avons un bouton qui dit Dois-je le faire ?. Quand vous cliquez sur ce bouton, les auditeurs (observateurs) rpondent la question leur guise. Nous implmentons ces deux auditeurs, nomms AuditeurAnge et AuditeurDmon. Voici comment lapplication se comporte :

Voici notre super inte

rface.

quand Et voil le rsultat uton. bo le nous cliquons sur


Fichier dition Fentre Aide GrosDilemme

Rponse du dmon Rponse de lange


72
Chapitre 2

%java ExempleObservateurSwing Allez, vas-y, fais-le ! Ne le fais pas, tu pourrais le regretter ! %

le pattern observateur

Et le code...
Cette application indispensable ncessite trs peu de code. Il suffit de crer un objet JButton, de lajouter un JFrame et de dfinir nos auditeurs. Pour ces derniers, nous allons utiliser des classes internes, technique trs courante en programmation Swing. Si vous ntes pas au point sur les classes internes ou sur Swing,vous pouvez revoir les chapitres sur les interfaces graphiques dans Java tte la premire.

public class ExempleObservateurSwing { JFrame cadre;

n Swing qui Simple applicatio r un cadre et se borne crebo uton. y insrer un

public static void main(String[] args) { ExempleObservateurSwing exemple = new ExempleObservateurSwing (); exemple.go(); } public void go() { cadre = new JFrame(); JButton bouton = new JButton(Dois-je le faire ?); bouton.addActionListener(new AuditeurAnge()); bouton.addActionListener(new AuditeurDemon()); cadre.getContentPane().add(BorderLayout.CENTER, bouton); // Dfinir les proprits du cadre ici }

Crer les objets ange et dmon (les observateurs) qui coutent les vnements du bouton.

class AuditeurAnge implements ActionListener { public void actionPerformed(ActionEvent event) { System.out.println(Ne le fais pas, tu pourrais le regretter!); } Voici les d } class AuditeurDemon implements ActionListener { public void actionPerformed(ActionEvent event) { System.out.println(Allez, vas-y, fais-le ); } } }

finitions de des observat classes classes inter eurs. Ce sont des obligatoire). nes (mais ce nest pas

Au lieu dupdate(), la mthode actionPerformed() est appele quand ltat du sujet (en loccurrence le bouton) change.

vous tes ici

73

votre bote outils de concepteur

Votre bote outils de concepteur


Flicitations, vous tes bientt la fin du chapitre 2.Vous venez dajouter quelques lments votre bote outils OO...

POINTS DIMPACT

Le pattern Observateur dfinit


entre des objets une relation de type un--plusieurs.

Les Sujets, alias Observables,

Bases de lOO
. ce qui varie e. Encapsulez n lhritag io it s o p m o c Prfrez la es, non des c a f r e t in s de Programmez ions. implmentat faiblement r le p u o c e d vous Efforcez- ui interagissent. les objets q

mettent jour les Observateurs via une interface commune.

Principes OO

Abstraction n Encapsulatio me Polymorphis Hritage

Les Observateurs sont


faiblement coupls, au sens o lObservable ne sait rien deux, en dehors du fait quils implmentent linterface Observer .

Vous pouvez pousser

uveau Voici notre no nez-vous principe. Souvet ions que les concep uples sont faiblement co plus souples plus beaucoup antes aux et plus rsist changements.

ou tirer les donnes de lObservable (tirer est considr comme plus correct).

Ne faites pas dpendre les


observateurs de lordre des notifications.

Java a plusieurs
implmentations du pattern Observateur, notamment la classe gnraliste Observable dans le package java.util. . Attention aux problmes potentiels de limplmentation de java.util.Observable.

O Patterns fO ille init une fam


au

lation une reie d it n u in ie c f g a , h t d c a r e l t S su pe ule pr au ce -pluschuarnsg t n e a n v , s r . e s s m b h y b t a O it e r e g o et t e s ed j tt chasnque un o b dalg entrereonbdjein r e penden rrithme dd les aon quel,alo o lg deux et i ent fermetou u q e d x u e c s s p tuimis jour nq ie t ent esn ie li o c t, a s e t e d ta Stratgd s i notif t. nd eie ntmm so np ed en varier in iq t toma uem
Nouveau pattern pour communiquer un tat un ensemble dobjets de manire faiblement couple. Nous navons pas vu tout le pattern Observateur - attendez un peu que nous parlions de MVC !

Nayez pas peur de crer


votre propre implmentation dObservable si ncessaire.

Swing utilise intensivement le


pattern Observateur, linstar de nombreux frameworks de cration dIHM.

Vous trouverez le pattern de


nombreux autres endroits, en particulier les JavaBeans et RMI.

74

Chapitre 2

le pattern observateur

Exercice

Principes de conception
Pour chaque principe de conception, dites comment le pattern Observateur lapplique.

Principe de conception
Identifiez les aspects de votre application qui varie et sparez-les de ce qui demeure inchang.

Principe de conception
Programmez des interfaces, non des implmentations.

Cette question est difficile. Indice : pensez la faon dont sujets et observateurs interagissent.
Principe de conception
Prfrez la composition lhritage.

vous tes ici

75

mots-croiss

Il est temps de redonner quelque chose faire votre cerveau droit ! Cette fois, tous les mots de la solution sont dans le chapitre 2.

X 7
1
8

2 4

X X
11

X X X
9

10

X
5
12 13

X
7
14

15

X
9

X
10

X
11

X
16

12

X
17

13

14 15
18

19

20 16

X
22

17

X 18
23

21

19
24

X X X
25

20

26

21

22

Horizontalement

Verticalement

Across
1. Programmez une _____________, non pour uneinterface implmentation. 1.pour Observable is a ______ not an 7. Package Java plein dobservateurs. 3. Devil and Angel are _______ to the button

Down
Programmez pour interface,and non pour une _______________ 2. 1. Ron was both anune Observer a __________ Envers deto pull. 3. 2. You want keep your coupling ________ 7. 3. He says you should go for it ___________ des changements. Les observateurs reoivent une 9. 4. _____ can manage your observers for you Le hros de ce chapitre. 10. Java framework with lots of Observers 5. Lun des couteurs de la page 72. 11. Weather-O-Rama's CEO named after this 6. La kind of classe stormDonneesMeteo __________ linterface Sujet. 13. like to be _________ 9.Observers Gre les observateurs votre place. when something new happens 12. Les observateurs sont __________ du sujet. 14. The WeatherData class _________ the 13. Elle excute du bytecode. Subject interface 15. On mesure la pression _____________. 16. He didn't want any more ints, so he removed 16. Gnratrice dvnements. himself 17. CEO almost forgot the ______ index display 20. Un auditeur _______. 19. Subject initially wanted to ____ all the data 21. Lo et Maud rencontrent un chasseur de _____. to Observer

4. Implement this method to get notified 8. Plus le couplage est _____, mieux cest. 5. Jill got one of her own 6. CurrentConditionsDisplay implements this 11. Peut tre locale, globale... interface 13. Tout le code ce livre est yourself crit en ____. 8. de How to get off the Observer list 12. forgot this if you're not getting notified 14. Observable estYou une ______, non une interface. when you think you should be 17. Inapplicable ou inconnu. 15. One Subject likes to talk to _____ observers 18. Contraire de push. 18. Don't count on this for notification 19. On limplmente. 19. Temperature, humidity and _______ 22. Peut tre rel non. 20. ou Observers are ________ on the Subject 21. Program to an not an 23. Ne comptez pas dessus pour les _______ notifications. implementation 24. Peut tre relative. 22. A Subject is similar to a __________
10. Ce type est vraiment primitif. 25. Indique le temps quil fait. 26. Lun des paramtres mesurs la station.

76

Chapitre 2

le pattern observateur

Exercisedes Solutions exercices solutions

vosyour crayons Sharpen pencil


Daprs notre premire implmentation, quels noncs suivants sont vrais ? (Plusieurs rponses possibles.)

A. Nous codons des implmentations


concrtes, non des interfaces.

D. Les lments daffichage nimplmentent pas


une interface commune.

B. Nous devons modifier le code pour


chaque nouvel lment daffichage.

E. Nous navons pas encapsul les parties qui


varient.

C. Nous navons aucun moyen dajouter


(ou de supprimer) des lments daffichage au moment de lexcution.

F. Nous violons lencapsulation de la classe


DonneesMeteo.

Principes de conception
Les lments qui varient dans le pattern Observateur sont ltat du Sujet et le nombre et le type des Observateurs. Ce pattern vous permet de faire varier les objets qui dpendent de ltat du Sujet, sans devoir modifier de Sujet. Cest ce quon appelle de la planification !

Principe de conception
Identifiez les aspects de votre application qui varient et sparez-les de ce qui demeure inchang.

Principe de conception
Programmez des interfaces, non des implmentations

Le Sujet et lObservateur utilisent tous deux des interfaces. Le Sujet mmorise les objets implmentant linterface Observateur, tandis que les observateurs senregistrent auprs de linterface Sujet et reoivent ses notifications. Comme nous lavons vu, cette technique permet de conserver une conception lgante et faiblement couple.

Principe de conception
Prfrez la composition lhritage.

Le pattern Observateur utilise la composition pour composer un nombre quelconque dObservateurs avec leur Sujet.Ces relations ne sont pas dfinies par une quelconque hirarchie dhritage. Non, elles sont dfinies par la composition au moment de lexcution !
vous tes ici

77

solutions des exercices

Le frigo
servable; import java.util.Ob server; import java.util.Ob

implements public class AffichagePrevisions Observer, Affichage {

private float pressionActu private float elle = 1012 ; dernierePres sion;


bservable public AffichagePrevisions(O observable) {

Solutions des exercices

DonneesMeteo donneesMeteo =(DonneesMeteo) observable;

observable.addO bserver(this);

able, vable observ update(Obser public void { g) ar Object


if (observable instanceof DonneesMeteo) {

dernierePression = pressionActuelle; pressionActuelle = donneesMeteo.getPression();


} } afficher();
1

{ afficher() public void ur afficher // code po }

I X N X T X E X R F X A C X E
14 10

X M

P X U X S X H X O
15

X L

X E

X M

X E

X A

X L X L

X A X S

X T X S D X E X P X E X N X D X A X N X T
22 12

26

A X T X M X O X S X P X H X E X R X I X Q X U X E

N X O X T X I X F X I X C X A X T X I X O
17

X T

X A

X T

X I

O X B S X E X R
7

X N
X W

X B

X L

X E

X I

X N

A X N X G X E X X A

J X V X M

13

X A

X V

16

X O

X U X R X E X M X T

X L X F X T X I X U

X L X A X C

V X A X T X E X U X R

11

X A

X R

X I

X R

X I

X S

18

19

X T X U
24

X E X J X U X A

E X C O X U X T X E
23

20

X U

X L X T X E X T X E X S
21

O X B X S X E X R X V X A X B X L X E

X L

I X M X P X L X E X M X E X N X T X E

X R X E

X D

X R
25

X D X R

X I X E

X T

X E

X O

X M

X P

X E

X R

78

Chapitre 2

3 Le pattern Dcorateur

h
g

g Dcorer

les objets

Je croyais que les vrais hommes sous-classaient tout. Ctait avant de dcouvrir le pouvoir de lextension au moment de lexcution et non celui de la compilation. Maintenant, regardez-moi !

Ce chapitre pourrait sappeler Les bons concepteurs se mfient de lhritage . Nous allons revoir la faon dont on
abuse gnralement de lhritage et vous allez apprendre comment dcorer vos classes au moment de lexcution en utilisant une forme de composition. Pourquoi ? Une fois que vous connatrez les techniques de dcoration, vous pourrez affecter vos objets (ou ceux de quelquun dautre) de nouvelles responsabilits sans jamais modifier le code des classes sous-jacentes.

nouveau chapitre

79

lhistoire de starbuzz

Bienvenue chez Starbuzz Coffee


Starbuzz Coffee sest fait un nom en devenant la plus importante chane de salons de caf . Si vous en voyez un au coin de la rue, tournez la tte : vous en verrez un autre. Comme ils se sont dvelopps trs rapidement, ils se prcipitent pour mettre jour leurs systmes de prise de commandes afin de ladapter leur offre. Quand ils ont commenc, ils ont conu leurs classes comme ceci...

se abstraite Boisson est une clasut es les sous-classe par todans le caf. boissons proposes

Boisson
description

La mthode cout() est abstraite ; les sous-classes doivent dfinir leur propre implmentation.

La variable dinstance description est dfinie dans chaque sous-classe et contient une description de la boisson, par exemple Excellent et cors. La mthode getDescription() retourne la description.

getDescription() cout() // Autres mthodes...

Colombia
cout()
cout()

Sumatra
cout()

Deca
cout()

Espresso

Chaque sous-classe implmente cout() pour retourner le cot de la boisson.

Les salons de caf de ce type emploient le vocabulaire italien du caf : espresso, capuccino, etc. Cest aussi pourquoi les serveurs et serveuses de la page 94 sont des baristas.

80

Chapitre 3

le pattern Dcorateur

En plus de votre caf, vous pouvez galement demander plusieurs ingrdients comme de la mousse de lait, du caramel, du chocolat, du sirop de vanille ou de noisette et couronner le tout de crme Chantilly. Starbuzz facturant chacun de ces supplments, ils ont vraiment besoin de les intgrer leur systme de commande. Voici leur premier essai...
Boisson
description getDescription() cout() // Autres mthodes...

ColombiaPlusLaitEtChocolat

cout()

ColombiaPlusLaitEtVanille cout() andCaramel HouseBlendWithWhipandMocha


cout()

ColombiaPlusLaitEtChocolatEtCaramel
cout()
cout()

SumatraPlusCaramel EtChocolat

EspressoPlusLait EtCaramel
cout()

EspressoPlusLait
cout()

SumatraPlusChantilly

DecaPlusChantilly
EspressoPlusLait
cout()

cout()

ColombiaPlusLaitEtChantilly cout() EspressoPlusChocolatEtVanille


SumatraPlusChantillyandSoy
cout()
cout()

cout() SumatraPlusCaramel

DecaPlusChantillyEtChocolat
EspressoWithMocha
EspressoWithSteamedMilk DecaPlusChantillyEtChocolat andSoy
cout()

SumatraPlusLaitEtChantilly
cout()

cout() DecafWithMocha

cout()

cout()

ColombiaPlusChantillyEtCaramel cout()
cost() ColombiaPlusCaramel
cout()

EspressoPlusChantillyEtChocolat cout() SumatraPlusChantilly


cost() SumatraPlusCaramelEtChocolat

DecaPlusLait
cout()

cost() EspressoPlusLaitEtVanille

DarkRoastWithSoy SumatraPlusLait
cout()

cost() DecaPlusCaramelEtChocolat

DarkRoastWithSoy

cout() ColombiaPlusLaitEtChantilly andWhip

cost()
cout()
cout()

DecaPlusCaramelEtChocolat cost() DecafWithSoy DecafWithSoyandMocha EspressoWhip cost() cout()


cout()

cost() EspressoPlusLaitEtChocolat
cout()

SumatraPlusChantillyEtChocolat
cout()

DecafWithWhip cout()

EspressoChantillyEspressoChantilly

SumatraPlusLait
SumatraPlusChocolat cost()
cout()

DecaPlusCaramelEtChocolat
EspressoPlusChantillyEtCaramel
cout()

cost()

DecaPlusLaitEtChantilly cost()
cout()

Eh bien ! Voil ce quon appelle une explosion combinatoire !

() calcule le cot Chaque mthode cout autres ingrdients du caf plus celui des de la commande.

vous tes ici

81

enfreindre les principes de conception

Musclez vos neurones

De toute vidence, Starbuzz sest fourr tout seul dans un vrai cauchemar. Que se passe-t-il quand le prix du lait augmente ? Que font-ils quand ils proposent un nouveau supplment de vanille ? Au-del du problme de maintenance, ils enfreignent lun des principes de conception que nous avons dj vus. Lequel ? Indication : Cest mme une infraction majeure ! Cest stupide. Pourquoi avonsnous besoin de toutes ces classes ? Pourquoi ne pas utiliser des variables dinstance et lhritage dans la superclasse pour mmoriser les ingrdients ? Eh bien, essayons. Commenons par la classe de base Boisson et ajoutons des variables dinstance pour reprsenter si chaque boisson contient ou non du lait, du caramel, du chocolat et/ou de la chantilly...
Boisson
description lait caramel chocolat chantilly getDescription() cout() aLait() setLait() aCaramel() setCaramel() aChocolat() setChocolat() aChantilly() setChantilly() // Autres mthodes...

Nouvelles valeurs boolennes pour chaque ingrdient. Maintenant, nous implmentons cout() dans Boisson (au lieu quelle demeure abstraite), pour quelle puisse calculer les cots associs aux ingrdients pour une instance de boisson donne. Les sous-classes redfiniront toujours cout(), mais elles appelleront galement la super-version pour pouvoir calculer le cot total de la boisson de base plus celui des supplments.

t et Ces mthodes lisenur boolennes le modifient les va s des ingrdients.

82

Chapitre 3

le pattern Dcorateur

Boisson

la superclasse La mthode cout() sde de tous les va calculer les cot que la mthode ingrdients, tandis ns les sous-classes cout() redfinie da nctionnalit va tendre cette fo ur ce type po et inclure les cotson. spcifique de boiss le ut() doit calculerde s Chaque mthode copu i lu ce r te is ajou on iss bo la de t co lant limplmentation ingrdients en appe rclasse. de cout() de la supe

Ajoutons maintenant les sous-classes, une pour chaque boisson figurant sur la carte :

Description lait caramel chocolat chantilly getDescription() cout() aLait() setLait() aCaramel() setCaramel() aChocolat() setChocolat() aChantilly() setChantilly() // Autres mthodes..

Colombia
cout()
cout()

Sumatra
cout()

Deca
cout()

Espresso

vos crayons Sharpen your pencil

crivez les mthodes cout() pour les classes suivantes (du pseudo Java suffit) :
public class Sumatra extends Boisson { public Sumatra() { description = Dlicieux et cors;

public class Boisson { public double cout() {

} public double cout() {

vous tes ici

83

impact du changement

Tu vois, cinq classes en tout. Je crois quon a trouv la solution. Pas si sr ! Si je rflchis la faon dont la conception pourrait voluer, je vois dj les problmes que cette approche peut poser.

vos crayons Sharpen your pencil


Quelles sont les exigences ou les autres facteurs qui pourraient changer et avoir un impact sur cette conception ?

Laugmentation du prix des ingrdients nous obligera modifier le code existant. De nouveaux ingrdients nous forceront ajouter de nouvelles mthodes et modifier la mthode cout() dans la superclasse. u ons v Nous pouvons avoir de nouvelles boissons. Pour certaines de ces boissons (th glac?), les v a l s st ingrdients ne seront peut-tre plus adapts, et pourtant la sous-classe Th continuera e noure 1, ce e m m Co chapit auvais hriter de la mthode aChantilly(). au trs m une e ! id Que se passe-t-il si le client veut un double supplment de chocolat ?
vous :

84

Chapitre 3

le pattern Dcorateur

Matre et disciple...
Matre : Il y a longtemps que nous ne nous sommes pas vus, petit scarabe. Est-ce que tu as profondment mdit sur lhritage ? Disciple : Oui, matre. Jai appris que lhritage tait puissant, mais que ce ntait pas la meilleure voie pour obtenir des conceptions souples et faciles maintenir. Matre : Ah, je vois que tu as fait des progrs. Mais dis-moi, scarabe, comment parviendras-tu la rutilisation si ce nest par lhritage ? Disciple : Matre, jai appris quil y avait dautres moyens d hriter dun comportement au moment de lexcution, grce la composition et la dlgation. Matre : Continue, scarabe... Disciple : Quand jhrite dun comportement en sous-classant, ce comportement est dfini statiquement au moment de la compilation. De plus, toutes les sous-classes doivent hriter du mme comportement. Mais si je peux tendre le comportement dun objet par la composition, je peux le faire dynamiquement lors de lexcution. Matre : Trs bien, scarabe, tu commences voir le pouvoir de la composition. Disciple : Oui. Cette technique me permet dajouter plusieurs nouvelles responsabilits aux objets, y compris des responsabilits auxquelles le concepteur de la superclasse navait mme pas pens. Et je nai pas besoin de toucher son code ! Matre : Et quas-tu appris sur leffet de la composition sur la maintenance de ton code ? Disciple : Jy arrive. En composant dynamiquement des objets, je peux ajouter de nouvelles fonctionnalits en crivant du code au lieu de modifier le code existant. Comme je ne touche pas au code existant, les risques dintroduire des bogues ou de provoquer des effets de bord inattendus sont significativement rduits. Matre : Trs bien. Cest assez pour aujourdhui, scarabe. Jaimerais que tu ailles mditer encore un peu sur ce sujet... Souviens-toi : le code doit tre ferm (au changement) comme la fleur de lotus le soir, et cependant ouvert ( lextension) comme la fleur de lotus le matin.

vous tes ici

85

le principe ouvert-ferm

Le principe Ouvert-Ferm
Scarabe est en train de dcouvrir lun des plus importants principes de conception :

Principe de conception
Les classes doivent tre ouvertes lextension, mais fermes la modification.

T R E V U O

z Entre

cest

Bienvenue ; nous sommes ouverts. Nhsitez pas tendre nos classes avec nimporte quel comportement de votre choix. Si vos besoins ou vos exigences voluent (et nous savons que ce sera le cas), il vous suffira de crer vos propres extensions

Dsols, nous sommes ferms. Oui, nous avons pass des heures crire ce code correctement et liminer les bogues. Nous ne pouvons donc pas vous permettre de Modifier le code existant. Il doit rester ferm aux modifications. Si a ne vous plat pas, adressez-vous au directeur.

He ures d ou vert ure Lu n Ma Me Je Ve

ferm

Notre but est de permettre dtendre facilement les classes pour incorporer de nouveaux comportements sans modifier le code existant. Quobtenons-nous si nous y parvenons ? Des conceptions rsistantes au changement et suffisamment souples pour accepter de nouvelles fonctionnalits rpondant lvolution des besoins.

86

Chapitre 3

Q: R:

questions stupides

Il ny a pas de

le pattern Dcorateur

Ouvert lextension et ferm la modification ? Cela semble trs contradictoire. Comment pouvons-nous avoir les deux ?

Excellente question. Il est vrai que cest contradictoire premire vue. Aprs tout, moins une chose est modifiable, plus elle est difficile tendre, non ? Mais vous allez voir quil existe en dveloppement OO tout un tas de moyens futs pour tendre un systme mme si on ne peut pas modifier le code sous-jacent. Pensez au pattern Observateur (au chapitre 2)... En ajoutant des observateurs, on peut tendre le sujet tout moment, sans ajouter de code celui-ci. Vous allez bientt dcouvrir quil existe dautres moyens dtendre un comportement grce un certain nombre dautres techniques OO.

Cest gnralement impossible. Crer des conceptions OO souples et ouvertes lextension sans modifier le code existant demande du temps et des efforts. Dhabitude, nous ne pouvons pas nous offrir ce luxe pour chaque partie de nos applications (et ce serait probablement peu rentable). Lapplication du principe Ouvert-Ferm introduit gnralement de nouveaux niveaux dabstraction, ce qui rend le code plus complexe. Vous devez vous concentrer sur les zones qui sont le plus susceptibles de changer et cest l quil faut appliquer ce principe.

R:

Q: R:

Comment savoir quels sont les points de variation les plus importants ?

Q: R:

OK, je comprends Observable. Mais comment faire dune faon gnrale pour concevoir quelque chose dextensible et pourtant ferm aux modifications ?

Cest en partie une question dexprience en conception de systmes OO, et cela demande galement de connatre le domaine dans lequel vous travaillez. Ltude dautres exemples vous aidera comprendre comment identifier ces points dvolution dans votre propre contexte.

De nombreux patterns nous offrent des modles de conception prouvs qui protgent votre code des modifications tout en fournissant des moyens dextension. Dans ce chapitre, vous allez voir un bon exemple demploi du pattern Dcorateur pour appliquer le principe Ouvert-Ferm.

Malgr la contradiction apparente, il existe des techniques qui permettent dtendre le code sans le modifier directement. Soyez attentif lorsque vous choisissez les points du code qui doivent tre tendus. Appliquer le principe Ouvert-Ferm PARTOUT est inutile, peu rentable et susceptible de dboucher sur un code complexe et difficile comprendre.
vous tes ici

Q:

Comment faire pour que toutes les parties de ma conception appliquent le principe Ouvert-Ferm ?

87

faites la connaissance du pattern dcorateur

Faites la connaissance du pattern Dcorateur

Bon, a suffit le club des concepteurs objet . Nous avons de vrais problmes ici ! Vous vous souvenez de nous ? Starbuzz Coffee ? Pensez-vous que vous pourriez appliquer certains de ces principes de conception pour nous aider vraiment ?

Bien. Nous avons vu que reprsenter notre boisson plus le schma de tarification des ingrdients au moyen de lhritage na pas trs bien fonctionn nous obtenons, une explosion de classes, une conception rigide, ou nous ajoutons la classe de base une fonctionnalit inapproprie pour certaines des sous-classes. Voici donc ce que nous allons faire. Nous allons commencer par une boisson et nous allons la dcorer avec des ingrdients au moment de lexcution. Si par exemple le client veut un Sumatra avec Chocolat et Chantilly, nous allons :
1 2 3 4

Prendre un objet Sumatra. Le dcorer avec un objet Chocolat. Le dcorer avec un objet Chantilly. Appeler la mthode cout() et nous appuyer sur la dlgation pour ajouter les cots des ingrdients.

Parfait. Mais comment fait-on pour dcorer un objet, et que vient faire ici la dlgation ? Indice : reprsentez-vous les objets dcorateurs comme des enveloppes . Voyons comment cela fonctionne...

88

Chapitre 3

le pattern Dcorateur

Construire une boisson avec des dcorateurs


1

Commenons par notre objet Sumatra.

cout()

Sumatra

ra que Sumautne s u o v z e n Souve e Boisson et a le le hrite d cout() qui calcu mthode la boisson. prix de

Le client veut du chocolat. Nous crons donc un objet Chocolat et nous enveloppons le Sumatra dedans.

ateur. t est un dcord Lobjet Chocola il qu core, en , et bj lo te l f re te Son type . (Par refl on ss oi B e un e .) nc pe loccurre est du mme ty il qu e ir d s on ul nous vo

cout()

cout()

Chocolat

Sumatra

une mthode t en m le a g c n o Chocolat a d au polymorphisme, nous n cout(). Grce er nimporte quelle Boisso une pouvons trait ocolat comme h C e d e p p lo es enve lat t un sous co o h C ue q ce Boisson (par son). type de Bois

Le client veut aussi de la Chantilly. Nous crons un dcorateur Chantilly et nous enveloppons Chocolat dedans.

cout()

cout()

cout()

hocola t Chan t i ll y

Sumatra

Chantilly est un dcorateur. Il reflte galement le type de Sumatra et possde une mthode cout().

Ainsi, un Sumatra envelopp de Chocolat et de Chantilly est toujours une Boisson. Nous pouvons lui faire tout ce que nous ferions avec un Sumatra, notamment appeler sa mthode cout().
vous tes ici

89

caractristiques de dcorateur

Il est temps de calculer le cot pour le client. Pour ce faire, nous appelons cout() sur le dcorateur le plus externe, Chantilly, et Chantilly va dlguer le calcul du cot lobjet quil dcore. Une fois quil aura le cot, il ajoutera le cot de Chantilly.
cout() sur Chocolat. 2 Chantilly appelle

(Vous verrez s comment dan s.) quelques page

elons Tout dabord, nous app r le plus cout() sur le dcorateu . illy ant Ch externe,

Chocolat appelle cout() sur Sumatra.

1,29 0,10

cout()

cout()

cout()

0,20

0,99

Chantilly
5

Choc olat

a Sumatr
4
Sumatra retourne son cot, 99 centimes.

5 Chantilly ajoute son total, 10 centimes, au rsultat de Chocolat et retourne le rsultat final 1,29 .

t, Chocolat ajoute son co 20 centimes, au rsultat le de Sumatra et retourne nouveau total, 1,19 .

Bien. Voici ce que nous savons jusqu maintenant...


Les dcorateurs ont le mme supertype que les objets quils dcorent. Vous pouvez utiliser un ou plusieurs dcorateurs pour envelopper un objet. Comme le dcorateur a le mme supertype que lobjet quil dcore, nous pouvons transmettre un
objet dcor la place de lobjet original (envelopp).

Le dcorateur ajoute son propre comportement soit avant soit aprs avoir dlgu le reste du travail
lobjet quil dcore.

Point-cl

Les objets pouvant tre dcors tout moment, nous pouvons les dcorer dynamiquement au
moment de lexcution avec autant de dcorateurs que nous en avons envie.

Voyons maintenant comment tout ceci fonctionne rellement en tudiant la dfinition du pattern Dcorateur et en crivant un peu de code.
90
Chapitre 3

le pattern Dcorateur

Le pattern Dcorateur : dfinition


Jetons dabord un coup dil la dfinition du pattern Dcorateur :
Le pattern Dcorateur attache dynamiquement des responsabilits supplmentaires un objet. Il fournit une alternative souple la drivation, pour tendre les fonctionnalits.

Si cet nonc dcrit bien le rle du pattern Dcorateur, elle ne nous donne pas beaucoup dindications sur la faon de lappliquer notre propre implmentation. Regardons le diagramme de classes, un peu plus rvlateur ( la page suivante, nous verrons la mme structure applique au problme des boissons).

Composant

Chaque composant peut tre utilis seul ou envelopp par un dcorateur.


composant

Le ComposantConcret est lobjet auquel nous allons ajouter dynamiquement un nouveau comportement. Il drive de Composant.
methodeA() methodeB()

methodeA() methodeB() // autres mthodes

ComposantConcret

Decorateur
methodeA() methodeB() // autres mthodes

Chaque dcorateur A-UN (enveloppe) un composant, ce qui signifie quil a une variable dinstance qui contient une rfrence un composant.
s implmentent Les dcorateurac ou classe la mme interf e mposant quils abstraite que le co vont dcorer.
DecorateurConcretB

// autres mthodes

DecorateurConcretA
Composant ObjetEnveloppe

Composant ObjetEnveloppe Object newState

une Le DecorateurConcret a lobjet variable dinstance pour nt que le quil dcore (le Composa Dcorateur enveloppe).

methodeA() methodeB() comportementAjoute() // autres mthodes

methodeA() methodeB() // autres mthodes

endre Les dcorateurs peuvent t ltat du composant.

Les Dcorateurs peuvent ajouter de nouvelles mthodes, mais on ajoute gnralement le nouveau comportement en effectuant un traitement avant ou aprs une mthode existant dans le composant.
vous tes ici

91

dcorer les boissons

Dcorons nos Boissons


Parfait. Cest dans ce cadre que nous allons travailler sur les boissons de Starbuzz...

de notre Boisson joue le rle omposant. classe abstraite, C

Boisson
description getDescription() cout() // autres mthodes

composant

Colombia
cout()
cout()

Sumatra

DecorateurIngredient
getDescription()

Espresso
cout()

Deca
cout()

ts omposan c e r t pe a y Les qu ts : un par t concre de caf

Lait
Boisson boisson cout() getDescription()
cout()

Chocolat
Boisson boisson cout()

Caramel
Boisson boisson
cout()

Chantilly
Boisson boisson

getDescription()

getDescription()

getDescription()

A
92
Chapitre 3

Et voici nos dcorateurs pour les ingrdients. Remarquez quils ne doivent pas seulement implmenter cout() mais aussi getDescription(). Nous verrons pourquoi dans un moment...

Musclez vos neurones

Avant daller plus loin, rflchissez la faon dont vous implmenteriez la mthode cout() pour le caf puis les ingrdients. Rflchissez galement limplmentation de la mthode getDescription() des ingrdients.

le pattern Dcorateur

Conversation dans un box


Petite mise au point : hritage vs. composition
Je suis un peu perdue... Je croyais quon nallait pas utiliser lhritage dans ce pattern, mais quon allait plutt sappuyer sur la composition.

Ma

rie

Anne : Quest-ce que tu veux dire ? Marie : Regarde le diagramme de classes. Le DecorateurIngredient tend toujours la classe Boisson. Cest de lhritage, a, non ? Anne : Exact. Je pense que le point vital, cest que les dcorateurs ont le mme type que les objets quils vont dcorer. Ici, on utilise lhritage pour obtenir la concordance de type, mais pas pour obtenir un comportement. Marie : OK. Je vois bien que les dcorateurs ont besoin de la mme interface que les composants quils enveloppent parce quils doivent prendre la place du composant. Mais quen est-il du comportement ? Anne : Quand on compose un dcorateur avec un composant, on ajoute un nouveau comportement. On nacquiert pas un nouveau comportement en lhritant dune superclasse mais en composant des objets ensemble. Marie : Oui, on sous-classe la classe abstraite Boisson pour avoir le type correct, pas pour hriter de son comportement. Le comportement provient de la composition des dcorateurs avec les composants de base ainsi que les autres dcorateurs. Anne : Cest cela. Marie : Ooooh, je vois. Et comme nous utilisons la composition, nous avons beaucoup plus de souplesse pour la faon de mlanger et dassortir les ingrdients et les boissons. Gnial. Anne : Oui. Si on sappuie sur lhritage, le comportement ne peut tre dtermin que statiquement au moment de la compilation. Autrement dit, nous navons pas dautre comportement que celui que la superclasse nous donne ou que nous redfinissons. Avec la composition, nous pouvons mlanger les ingrdients notre guise... au moment de lexcution. Marie : Si jai bien compris, on peut implmenter de nouveaux dcorateurs nimporte quand pour ajouter de nouveaux comportements. Si on sappuyait sur lhritage, il faudrait modifier le code existant chaque fois quon veut un nouveau comportement. Anne : Exactement. Marie : Encore une question. Si on na besoin de rien dautre que dhriter le type du composant, Pourquoi ne pas utiliser une interface au lieu dune classe abstraite pour la classe Boisson ? Anne : Souviens-toi. Quand on a reu ce code, Starbuzz avait dj une classe abstraite Boisson. Traditionnellement, le pattern Dcorateur spcifie un composant abstrait, mais en Java, de toute vidence, on pourrait utiliser une interface. Mais on essaie toujours de ne pas modifier le code existant. Si la classe abstraite fonctionne bien, inutile dy toucher.
vous tes ici

93

formation dcorateur

Nouvelle formation des baristas


Reprsentez-vous ce qui se passe quand un client demande une boisson double chocolat caramel et chantilly . Reportez-vous la carte pour les prix corrects, et tracez votre schma dans le format que nous avons dj utilis (page 90) :
cout() sur Chocolat. 2 Chantilly appelle
ons 1 Tout dabord, nous appel le plus cout() sur le dcorateur exter ne, Chantilly.

OK, Jai besoin de vous pour faire un double chocolat, caramel et chantilly.

Chocolat appelle cout() sur Sumatra.

1,29 0,10

cout()

cout()

0,20

0,99 Sumatra

cout()

eprsentait Le schma r Sumatra une boisson hantilly . chocolat c

Chantilly
6 Chantilly ajoute son total, 10 centimes, au rsultat de Chocolat et retourne le rsultat final 1,29 .

Chocolat

4 Sumatra retour ne son cot, 99 centimes.

5 Chocolat ajoute son cot, 20 centimes, au rsultat de Sumatra et retour ne le nouveau total, 1,19 .

vos your crayons Sharpen pencil

Tracez votre schma ici

Cafs Colombia Sumatra Deca Espresso

Starbuzz Coffee
0,89 0,99 1,05 1,99 0,10 0,20 0,15 0,10

Supplments Lait Chocolat Caramel Chantilly

94

fe Cof e S

bu tar zz

fee Cof S

z pouve ouble : vousr un d E C I IND ectionne amel et inant s conf olat car en comb eux dose choc tilly ramel, d tilly ! chan mbia, Ca et Chan Colo hocolat de C

Chapitre 3

u tarb zz

le pattern Dcorateur

crire le code de Starbuzz


Il est temps de traduire cette conception en code. Commenons par la classe Boisson, la conception dorigine de Starbuzz quil est inutile de modifier. Jetons-y un coup dil :

public abstract class Boisson { String description = Boisson inconnue; public String getDescription() { return description; } public abstract double cout(); }

ite e classe abstra Boisson est un : es d x mtho qui possde deu ) et cout(). n( getDescriptio

getDescription a dj t implmente pour nous, mais nous devons implmenter cout() dans les sous-classes.

Boisson est une classe trs simple. Implmentons galement la classe abstraite pour les ingrdients (dcorateurs) :

e elle doit tre , Dabord, commle avec une Boisson interchangeabs la classe Boisson. nous tendon
public abstract class DecorateurIngredient extends Boisson { public abstract String getDescription(); }

Nous allons aussi faire en sorte que les ingrdients (dcorateurs) rimplmentent tous la mthode getDescription(). Nous allons aussi voir cela dans une seconde...

vous tes ici

95

implmenter les boissons

Coder les boissons


Maintenant que nous nous sommes dbarrasss de nos classes de base, implmentons quelques boissons. Nous allons commencer par un Espresso. Souvenez-vous : il nous faut dfinir une description de la boisson spcifique et aussi implmenter la mthode cout().

public class Espresso extends Boisson { public Espresso() { description = Espresso; } public double cout() { return 1.99; } }

dabord la classe Nous tendonsuil sagit dune boisson. Boisson, puisq

Nous grons la description dans leez-vous constructeur de la classe. Souvention est que la variable dinstance descrip hrite de Boisson.

e nous Maintenant qu o. ss re sp E il n u d ulons le cot s ingrdients dans cette classe, Enfin, nous calc le in dajouter un Espresso : 1,99 . navons plus beso ner le prix d suffit de retour

public class Colombia extends Boisson { public Colombia() { description = Pur Colombia; } public double cout() { return .89; } }

Coffee Starbuzz
Cafs ia Colomb a r Sumat a Dec so Espres ments Suppl Lait at Chocol l e m a Car lly i t n a h C 0,89 0,99 1,05 1,99 0,10 0,20 0,15 0,10

Bien. Voici une autre Boisson. Il suffit de spcifier la description approprie, Pur Colombia , puis de retourner le prix correct : 89 centimes. Vous pouvez crer les deux autres classes Boissons (Sumatra et Deca) exactement de la mme faon.
96
Chapitre 3

le pattern Dcorateur

Coder les ingrdients


Si vous regardez de nouveau le digramme de classe du pattern Dcorateur, vous verrez que nous avons maintenant crit notre composant abstrait (Boisson), notre dcorateur abstrait (DecorateurIngredient) et que nous avons un composant concret (Colombia). Il est temps dimplmenter les dcorateurs concrets. Voici Chocolat :

e nd vous qud e ient te Chocolat est un dcorateur : nous Souveneze r g Chocolat avec un n I er ci r an u st e in t ns a lo : al r Nous tendons DecorateurIngredient. Deco oisson en utilisant rfrence une B Boisson. instance pour (1) Une variable ndque nous enveloppons. public class Chocolat extends DecorateurIngredient { contenir la boisso Boisson boisson; affecter cette ur po en oy m n U (2) us ce lobjet que nosm public Chocolat(Boisson boisson) { ettre variable dinstan an tr allons us no i, Ic this.boisson = boisson; s. on pp envelo us enveloppons au } la boisson que no r. teur du dcorateu uc tr ns co public String getDescription() {
return boisson.getDescription() + , Chocolat; } public double cout() { return .20 + boisson.cout(); }

de notre nt calculer le cot ab Nous devons maintena d uons ord lappel . lg d us No . at ol oc Ch boisson avec quil calcule son cot ur po ns ro co d us no e lobjet qu ocolat au rsultat. Ch de t co le ns to ou Puis nous aj
}

La description ne doit pas comprendre seulement la boisson disons Sumatra - mais aussi chaque ingrdient qui dcore la boisson, par exemple, Sumatra, Chocolat. Nous allons donc dlguer lobjet que nous dcorons pour lobtenir, puis ajouter Chocolat la fin de cette description.

la page suivante, nous allons instancier rellement la boisson et lenvelopper dans tous ses ingrdients (dcorateurs), mais dabord...
vos your crayons Sharpen pencil
crivez et compilez le code des autres ingrdients, Caramel et Chantilly. Vous en aurez besoin pour terminer et tester lapplication.

vous tes ici

97

tester les boissons

Le caf est ser vi


Flicitations. Il est temps de nous asseoir, de commander quelques cafs et de nous merveiller de la souplesse de la conception que vous avez cre en appliquant le pattern Dcorateur.

Voici un peu de code de test pour commander nos boissons :

public class StarbuzzCoffee { public static void main(String args[]) { Boisson boisson = new Espresso(); System.out.println(boisson.getDescription() + + boisson.cout());

s ngrdient i d s a p , . o t ess et son co er un espr Commandher sa description et affic

Sumatra. Crer un objet ocolat. Boisson boisson2 = new Sumatra(); Lenvelopper dans un Ch boisson2 = new Chocolat(boisson2); Lenvelopper dans un second Chocolat. boisson2 = new Chocolat(boisson2); boisson2 = new Chantilly(boisson2); Lenvelopper de Chantilly. System.out.println(boisson2.getDescription()
+ + boisson2.cout()); Boisson boisson3 = new Colombia(); boisson3 = new Caramel(boisson3); boisson3 = new Chocolat(boisson3); boisson3 = new Chantilly(boisson3); System.out.println(boisson3.getDescription() + + boisson3.cout());

c Enfin nous servir un Colombia ave . illy ant Ch et Caramel, Chocolat

} }

Maintenant, voyons le rsultat :


Fichier dition Fentre Aide NuageDeLait

Nous allons voir une bien meilleure faon de crer des objets dcors quand nous aborderons les patterns Fabrication et Monteur. Notez que Monteur est abord dans lannexe.

% java StarbuzzCoffee Espresso 1.99 Sumatra, Chocolat, Chocolat, Chantilly 1.49 File Edit Window Help CloudsInMyCoffee Colombia, Caramel, Chocolat, Chantilly 1.34 %

98

Chapitre 3

le pattern Dcorateur

Questions Stupides
Il y a quelque chose qui minquite un peu. On pourrait avoir du code qui teste un composant concret donn disons, Colombia et qui applique par exemple une remise. Une fois que jai envelopp Colombia dans les dcorateurs, cela ne fonctionnera plus.

il ny a pas de

Q:

Q:

Tout fait. Si vous avez du code qui sappuie sur le type du composant concret, les dcorateurs endommagent ce code. Tant que vous nutilisez que le type du composant abstrait, lemploi des dcorateurs reste transparent. Mais ds que vous utilisez le type des composants concrets, vous devez repenser la conception de votre application et votre usage des dcorateurs.

R:

Est-ce quil ne pourrait pas arriver que le client dune boisson finisse avec un dcorateur qui ne serait pas le dcorateur le plus externe ? Par exemple si javais un Sumatra avec Chocolat, Caramel et Chantilly, ce serait facile dcrire du code qui se termine par une rfrence Caramel au lieu de Chantilly, ce qui signifie quil ninclurait pas Chantilly dans la commande.

Q:

Les dcorateurs peuvent-ils connatre les autres dcorateurs de la chane ? Disons que jaimerais que ma mthode getDecription() affiche Chantilly, Double Chocolat au lieu de Chocolat, Chantilly, Chocolat . Cela impliquerait que mon dcorateur le plus externe connaisse tous les autres dcorateurs quil enveloppe.

On peut dire coup sr que le Pattern Dcorateur oblige grer plus dobjets, et quil y a donc un risque accru que des erreurs de codage introduisent le type de problme auquel vous faites allusion. Mais on cre gnralement des dcorateurs en utilisant dautres patterns, comme Fabrication et Monteur. Une fois que nous aurons abord ces patterns, vous verrez que la cration du composant concret avec son dcorateur est bien encapsule et vite ce genre de bogue.

R:

Les dcorateurs sont conus pour ajouter un comportement lobjet quils enveloppent. Si vous devez accder plusieurs couches de la chane de dcorateurs, vous commencez dtourner le pattern de son vritable objectif. Nanmoins, ce nest pas impossible. On peut imaginer un dcorateur AffichageAmlior qui analyse la description finale et affiche Chocolat, Chantilly, Chocolat sous la forme Chantilly, Double Chocolat . Notez que getDecription() pourrait retourner une ArrayList de descriptions pour faciliter le processus.

R:

vos your crayons Sharpen pencil


Nos amis de chez Starbuzz ont ajout des tailles leur carte. Vous pouvez maintenant commander un caf en taille normale, grande et venti (traduction : petit, moyen et grand). Comme Starbuzz a vu cela comme une partie intrinsque de la classe Caf, ils ont ajout deux mthodes la classe Boisson: setTaille() et getTaille(). Ils voudraient galement que le prix des ingrdients varie selon la taille. Par exemple, le Caramel coterait respectivement 10, 15 et 20 centimes pour un petit caf, un moyen et un grand. Comment modifieriez-vous les classes dcorateurs pour grer cette demande de changement ?

vous tes ici

99

dcorateurs dans les e/s java

Dcorateurs du monde rel : les E/S Java


Le nombre de classes du package java.io est... tourdissant. Si vous avez pouss un soupir la premire fois (et mme la deuxime et la troisime) que vous avez regard cette API, rassurez-vous, vous ntes pas le seul. Mais maintenant que vous connaissez le pattern Dcorateur, les classes dE/S devraient tre plus faciles comprendre, puisque le package java.io est largement bas sur Dcorateur. Voici un ensemble typique dobjets qui utilisent des dcorateurs pour ajouter des fonctionnalits la lecture de donnes dans un fichier :

Le fichier texte qui est lu

LineNumberInputStream est aussi un dcorateur concret. Il ajoute la capacit de compter le nombre de lignes en lisant les donnes.

BufferedInputStream est un dcorateur concret. BufferedInputStream ajoute un comportement de deux faons : il buffrise lentre pour amliorer les performances et il augmente linterface dune nouvelle mthode, readLine(), qui permet de lire une entre base de caractres une ligne la fois.

BufferedInputStream et LineNumberInputStream drivent tous deux de FilterInputStream, la classe qui joue le rle de dcorateur abstrait.

100

Chapitre 3

Li

ne Nu mberInputStrea

Bu

FileInputStream

ff

ered

e InputStr

sant qui est o p m o c le st eam e Java FileInputStrbibliothque dE/S de ment dcor. La eurs composants, notamtStream, u fournit plusiream, StringBufferInp s autres. e t u S FileInput putStream et quelq ByteArrayIn s de base dan t n sa o p m o c onnent un Tous nous d des octets. lequel on lit

le pattern Dcorateur

Dcoration des classes de java.io


co Voici notre
InputStream

mposant abst

rait

FilterInputStream est un dcorateur abstrait.


FilterInputStream

FileInputStream

StringBufferInputStream

ByteArrayInputStream

PushbackInputStream

BufferedInputStream

DataInputStream

LineNumberInputStream

Les InputStreams sont les composants concrets que nous allons envelopper dans les dcorateurs. Il y en a quelques autres que nous navons pas reprsents, comme ObjectInputStream.

Et enfin, voici tous nos dcora

teurs concrets.

Vous constatez que ce nest pas si diffrent de la conception de Starbuzz. Vous devez maintenant tre mme dtudier la documentation de lAPI java.io et de composer des dcorateurs avec les diffrents flots dentre. Et vous verrez que les flots de sortie sont conus de la mme manire. Et vous avez probablement dj dcouvert que les flots Reader/Writer (pour les donnes de type caractre) refltent troitement la conception des classes de flots, (avec quelques diffrences et quelques hiatus, mais de faon suffisamment proche pour comprendre ce qui se passe). Mais les E/S Java mettent galement en vidence les inconvnients du pattern Dcorateur : lapplication de celui-ci aboutit souvent un grand nombre de petites classes puisantes pour un dveloppeur qui essaie dutiliser une API base sur Dcorateur. Mais maintenant que vous savez comment ce pattern fonctionne, vous pouvez remettre les choses en perspective : quand vous utiliserez lAPI de quelquun dautre et quelle fera un usage intensif de Dcorateur, vous serez capable de comprendre comment ses classes sont organises et dutiliser des enveloppes pour obtenir le comportement que vous recherchez.

vous tes ici

101

crire votre propre dcorateur de/s

crire votre propre dcorateur dE/S Java


Bien. Vous connaissez le pattern Dcorateur et vous avez vu le diagramme de classes des E/S. Vous devez tre prt crire votre propre dcorateur pour les entres.
Pas de problme. Il suffit dtendre la classe FilterInputStream et de redfinir les mthodes read().

Que diriez-vous dcrire un dcorateur qui convertit toutes les majuscules en minuscules dans le flot dentre. Autrement dit, si nous lisons Je connais le Pattern Dcorateur, je suis le MATRE DU MONDE ! votre dcorateur crira je connais le pattern dcorateur, je suis le matre du monde !

s dimporter Noubliez pa is ici) java.io... (om

Dabord tendre FilterInputStream, le dcorateur abstrait de tous les InputStreams.

public class MinusculeInputStream extends FilterInputStream { public MinusculeInputStream(InputStream in) { super(in); } public int read() throws IOException { int c = super.read(); return (c == -1 ? c : Character.toLowerCase((char)c)); } public int read(byte[] b, int decal, int lg) throws IOException { int resultat = super.read(b, decal, lg); for (int i = decal; i < decal+resultat; i++) { b[i] = (byte)Character.toLowerCase((char)b[i]); Maintenant, nous devons implmenter } deux mthodes read(). Elles return resultat; acceptent un octet (ou un tableau } ets) et convertissent chaque

RAPPEL : nous ne fournissons pas les instruction import et package dans les listings. Tlchargez le code source complet sur le site web du livre. Vous trouverez lURL page xxxi de lIntro.
102
Chapitre 3

doct octet (qui reprsente un caractre) en minuscule si le caractre en question est une majuscule.

le pattern Dcorateur

Tester votre nouveau dcorateur


crivons rapidement un peu de code pour tester notre dcorateur :

public class TestEntree { public static void main(String[] args) throws IOException { int c; try { InputStream in = tStream et u p In e il F le new MinusculeInputStream( r un Cre abord avec new BufferedInputStream( is u p le dcorer, d m a Stre new FileInputStream(test.txt))); fferedInpuotut nouveau filtre, u B while((c = in.read()) >= 0) { t avec notre p am. System.out.print((char)c); uleIn utStre sc u in M } in.close(); } catch (IOException e) { e.printStackTrace(); } Je connais le Pattern dcorateur, je suis le MATRE } }

DU MONDE !

Il suffit dutiliser le flot pour lire les caractres jusqu la fin du fichier, puis dafficher.
Faisons le tourner :
Fichier dition Fentre Aide LoiDuDcorateur

fichier test.txt

re Vous devez c ce fichier.

% java TestEntree je connais le pattern dcorateur, je suis le matre du monde ! %

vous tes ici

103

interview du dcorateur

Interview
Cette semaine : les confessions dun Dcorateur DPTLP : Bienvenue, Pattern Dcorateur. Certains disent que vous navez pas trop le moral ces temps-ci ? Dcorateur : Oui, je sais que le monde me voit comme un design pattern prestigieux, mais, vous savez, jai mon lot de problmes comme tout un chacun. DPTLP : Peut-tre pouvez-vous nous dire ce qui ne va pas ? Dcorateur : Bien sr. Eh bien, vous savez que jai le pouvoir dajouter de la souplesse une conception, il ny a pas de doute l-dessus, mais jai aussi une face cache. Voyez-vous, je peux parfois obliger crer un tas de petites classes, et, loccasion, cela donne une conception que les autres peuvent avoir du mal comprendre. DPTLP : Pouvez-vous nous donner un exemple ? Dcorateur : Prenez les bibliothques dE/S de Java. Elles sont notoirement difficiles comprendre demble. Mais si les gens voyaient simplement les classes comme un ensemble denveloppes autour dun InputStream, la vie serait beaucoup plus facile. DPTLP : a na pas lair si mal. Vous tes toujours un grand pattern, et la solution nest rien dautre quun problme de formation, non ? Dcorateur : Ce nest pas tout, jen ai peur. Jai des problmes de typage : vous voyez, les gens prennent parfois un morceau de code client qui sappuie sur des types spcifiques et introduisent des dcorateurs sans avoir une rflexion densemble. Maintenant, jai un gros avantage : vous pouvez gnralement insrer des dcorateurs de faon transparente et le client na jamais besoin de savoir quil a faire avec un dcorateur. Mais, comme je vous lai dit, il y a des programmes qui dpendent de types spcifiques, et quand vous commencez introduire des dcorateurs, patatrac ! Rien ne va plus. DPTLP : Eh bien, je pense que tout le monde a compris quil faut tre prudent quand on insre des dcorateurs. Je ne vois l aucune raison de vous mettre martel en tte. Dcorateur : Je sais, jessaie de me dtacher. Mais jai un autre problme. Lintroduction de dcorateurs peut aussi accrotre la complexit du code ncessaire pour instancier un composant. Une fois que vous avez des dcorateurs, il ne suffit pas dinstancier le composant, il faut encore lenvelopper dans je ne sais combien de dcorateurs. DPTLP : Je vais interviewer les patterns Fabrication et Monteur la semaine prochaine. Jai entendu dire quils pouvaient aider rsoudre ce problme. Dcorateur : Cest vrai. Je devrais parler avec eux plus souvent. DPTLP : Bon, nous savons tous que vous tes un grand pattern qui permet de crer des conceptions souples et de rester fidle au Principe Ouvert-Ferm, alors relevez la tte et essayer de penser de faon positive ! Dcorateur : Je vais faire de mon mieux, merci. 104

Chapitre 3

le pattern Dcorateur

Votre bote outils de concepteur


Vous avez maintenant une autre corde votre arc et un principe et un pattern de plus dans votre bote outils.

Lhritage est une forme


dextension, mais ne constitue pas ncessairement le moyen dobtenir des conceptions plus souples.

POINTS DIMPACT

Nos conceptions doivent permettre


dtendre les comportements sans devoir modifier le code existant.

Principes OO

Abstraction n Encapsulatiome Polymorphis Hritage qui varie. e c z le su p a c En ritage. lation lh su p a c n e l z Prfre es faces, non d r e t in s e d Programmez s. ion t implmentat r faiblemen le p u o c e d s vou Efforcez- teragissent. ui in q les objets rtes t tre ouve n e iv o d s e ss la Les cla ais fermes m n o si n e t x le n. modificatio

Bases de lOO

On peut souvent employer la


composition et la dlgation pour ajouter des comportements au moment de lexcution.

Le pattern Dcorateur fournit


une solution de rechange au sous-classement pour tendre le comportement.

Le pattern Dcorateur fait appel un


ensemble de classes dcorateurs quon utilise pour envelopper des composants concrets.

Les classes dcorateurs refltent

Nous avons maintenant le Principe Ouvert-Ferm pour nous guider. Nous allons nous efforcer de concevoir notre systme pour que les parties fermes soient isoles de nos nouvelles extensions.

le type des composants quelles dcorent. (En fait, elles sont du mme type que les composants quelles dcorent, soit par hritage, soit via limplmentation dune interface.) Les dcorateurs modifient le comportement de leurs composants en ajoutant de nouvelles fonctionnalits avant et/ou aprs les appels de mthode (ou mme leur place). composant dans un nombre quelconque de dcorateurs.

O dfinit entreype ernvsatO Patt u eitrun am fn e nce de t e, dilale Obser fin pe


ou ationap drivt ts.. fonc ionn li

le chd qu d s, n e o nceapd d ie nf gb ua a e c s,u a h t c e a jhe Strat t t , t s o s a r t u n ires . ie e d s a r s t u . e lu e s n e g pora leplmn e n t b a a h e g c n n-m c a p uit t h u e c t s dalgor D j r e b s e tbilitn dhmpe d do in ne dno ed amique nn u n e u y r a q s s s r le n o e r lo t o p e u s i e u a it x j r r u q f o e et mnis d tous e a uex li tlgsde tt je bif m ni qu re o sio pc n u gie slioelutt o stc n e e n d t u Strat t n n t e ie e n o m e s m m a e m r d u e e natiqu e pratiq la les p e p l I d in m r o ie t r a u v a alternativ r tendre
des ern pour crerert-Ferm. t t pa r ie em pr Et voici notre i respectent le Principe Ouvvu un autre conceptions qu aiment ? Navons-nous pas ? Le premier, vr plique galement ce principe pattern qui ap

Vous pouvez envelopper un Les dcorateurs sont gnralement


transparents pour le client du composant, sauf si le client dpend du type concret du composant.

Les dcorateurs peuvent entraner


la cration de nombreux petits objets et leur abus risque de gnrer de la complexit.
vous tes ici

105

solutions des exercices

Solutions des exercices


public class Boisson { // dclarer des variable dinstances pour coutLait, // coutCaramel, coutChocolat et coutChantilly, et // des mthodes get et set pour lait, caramel, // chocolat et chantilly. public float cout() { float coutIngredient = 0.0; if (aLait()) { coutIngredient += coutLait; } if (aCaramel()) { coutIngredient += coutCaramel; } if (aChocolat()) { coutIngredient += coutChocolat; } if (aChantilly()) { coutIngredient += coutChantilly; } return coutIngredient; } } public class Sumatra extends Boisson { public Sumatra() { description = Excellent et cors; } public float cout() { return 1.99 + super.cout(); }

Nouvelle formation des baristas

double chocolat caramel et chantilly

cout() sur Chocolat 2 Chantilly appelle t t() sur un autre Chocola 3 Chocolat appelle cou Caramel. sur t() cou Chocolat appelle Nous appelons dabord 4 Puis 1 cout() sur le dcorateur le plment! Caramel ly. 5 Der nier sup plus externe, Chantil a. appelle cout() sur Colombi

tarbu

zz C

La mthode cout() de Colombia retourne 0,89 et elle est dpile. La mthode cout() de Caramel ajoute 0,15. Elle retourne le rsultat et elle est dpile.

offee
1.54 0,10

cout()

cout() cout()

cout()
Ca ra me

0,20

0,20

0,15

0,89
l

cout()
Co
lom bia

106

Chapitre 3

Ch an t
10 Enfin, le rsultat arrive la

illy

Ch o

Ch

cola t

oc ola t

t() La deuxime mthode cou . Elle de Chocolat ajoute 0,20 est retourne le rsultat et elle dpile.

mthode cout() de Chantilly qui ajoute 0,10 et nous obtenons le cot final : 1,54 .

t() de La premire mthode cou retourne le Chocolat ajoute 0,20. Elle . rsultat et elle est dpile

le pattern Dcorateur

Solutions des exercices


Nos amis de chez Starbuzz ont ajout des tailles leur carte. Vous pouvez maintenant commander un caf en taille normale, grande et venti (traduction : petit, moyen et grand). Comme Starbuzz a vu cela comme une partie intrinsque de la classe Caf, ils ont ajout deux mthodes la classe Boisson : setTaille() et getTaille(). Ils voudraient galement que le prix des ingrdients varie selon la taille. Par exemple, le Caramel coterait respectivement 10, 15 et 20 centimes pour un petit caf, un moyen ou un grand. Comment modifieriez-vous les classes dcorateurs pour grer cette demande de changement ?
public class Caramel extends DecorateurIngredient { Boisson boisson; public Caramel(Boisson boisson) { this.boisson = boisson; } public int getTaille() { return boisson.getTaille(); }

er ntenant propag sson oi b Nous devons mai la Taille() la mthode gets devons galement enveloppe.Noutte mthode dans la classe transfrer ce quelle sera utilise dans abstraite puis ients dcorateurs. tous les ingrd

public String getDescription() { return boisson.getDescription() + , Caramel; } public double cout() { double cout = boisson.cout(); if (getTaille() == Boisson.NORMAL) { cout += .10; } else if (getTaille() == Boisson.GRANDE) { cout += .15; } else if (getTaille() == Boisson.VENTI) { cout += .20; } return cout; }

Ici nous obtenons la taille (qui se propage tout du long jusqu la boisson concrte) et nous ajoutons le cot appropri.

vous tes ici

107

4 les patterns fabriques

Un peu de cuisine oriente objet


g g

Apprtez-vous confectionner des conceptions OO faiblement couples. Crer des objets ne se limite pas utiliser loprateur new. Vous allez apprendre
que linstanciation est une activit qui ne devrait pas toujours tre publique et qui peut souvent entraner des problmes de couplage. Et vous nen avez pas vraiment envie, nest-ce pas? Dcouvrez comment les patterns fabriques peuvent vous aider vous librer de dpendances embarrassantes. nouveau chapitre

109

rflchir new

Bon, jai dj lu trois chapitres et vous navez toujours pas rpondu ma question sur new. Nous ne sommes pas censs programmer une implmentation mais chaque fois que jutilise new, cest exactement ce que je fais, non?

Quand vous voyez new, pensez concret. Oui, quand vous utilisez new, vous instanciez sans doute possible une classe concrte: il sagit donc bien dune implmentation, non dune interface. Et cest une bonne question: vous avez appris que le fait de lier votre code une classe concrte peut le rendre plus fragile et plus rigide.
Canard canard = new Colvert();

Nous voulons utiliser interfaces pour cons des erver de la souplesse.

r une Mais nous devons creconcrte! instance dune classe

Quand vous avez tout un ensemble de classes concrtes apparentes, vous tes souvent oblig dcrire du code qui ressemble au fragment suivant:
Canard canard; if (dansLaMare) { canard = new Colvert(); } else if (aLaChasse) { canard = new Leurre(); } else if (dansLaBaignoire) { canard = new CanardEnPlastique(); }

sses Nous avons plusieurs clae nest canards diffrentes.Ccution que quau moment de lexnous avons nous saurons laquelle besoin dinstancier.

Nous avons ici plusieurs instanciations de classes concrtes et la dcision de la classe instancier est prise au moment de lexcution, en fonction dun certain nombre de conditions. Quand vous voyez un code comme celui-ci, vous savez que, lorsquil faudra apporter des modifications ou des extensions, vous devrez reprendre ce code et examiner ce quil faudra ajouter (ou supprimer). Ce type de code se retrouve souvent dans plusieurs parties de lapplication, ce qui rend la maintenance et les mises jour plus difficiles et plus sujettes lerreur. 110
Chapitre 4

les patterns fabriques

Mais il faut bien crer des objets un moment ou un autre, et Java ne nous donne quun moyen de crer un objet, non? Alors comment faire?

Quel est le problme avec new? Du point de vue technique, il ny a pas de problme avec new. Aprs tout, cest une partie fondamentale de Java. Le vrai coupable, cest notre vieil ami, le CHANGEMENT, et limpact quil a sur notre utilisation de new. En codant une interface, vous savez que vous vous isolez de la foule de changements qui pourraient survenir dans un systme en aval. Pourquoi? Si votre code sappuie sur une interface, il fonctionnera avec toute nouvelle classe qui implmente cette interface via le polymorphisme. Mais si vous avez du code qui utilise de nombreuses classes concrtes, vous tes en train de chercher les ennuis, parce que vous devrez modifier ce code chaque ajout de nouvelles classes concrtes. Autrement dit, votre code ne sera pas ferm la modification. Pour ltendre avec de nouveaux types concrets, vous devrez le rouvrir. Que pouvez-vous donc faire? Cest dans des situations comme cellesci quon peut se rabattre sur les principes de conception OO pour y chercher des indices. Souvenez-vous: notre premier principe concerne le changement et nous conseille didentifier les aspects qui varient et de les sparer de ceux qui demeurent les mmes.

Noubliez pas qu doit tre o une conception mais ferme uverte lextension voir le chapitrela modification; 3 pour une pe rvision. tite

Musclez vos neurones

Comment pourriez-vous prendre toutes les parties de votre application qui instancient des classes concrtes et les encapsuler ou les sparer du reste de lapplication?

vous tes ici

111

identifier ce qui varie

Identifier les aspects qui varient


Disons que vous possdez une boutique Objectville et que vous vendez des pizzas. Pour rester la pointe de la technologie, vous crirez peut-tre un programme comme celui-ci:
Pizza commanderPizza() { Pizza pizza = new Pizza(); pizza.preparer(); pizza.cuire(); pizza.couper(); pizza.emballer(); return pizza; }}

Si nous voulons de la souplesse, il nous faut une interface ou une classe abstra nous ne pouvons instancier direct ite, mais ement ni lune ni lautre.

Mais un seul type de pizza ne suffit pas...

Vous ajoutez donc du code qui dtermine le type de pizza appropri et qui passe ensuite sa ralisation:
Pizza commanderPizza(String type) { Pizza pizza; if (type.equals(fromage)) { pizza = new PizzaFromage(); } else if (type.equals(grecque) { pizza = new PizzaGrecque(); } else if (type.equals(poivrons) { pizza = new PizzaPoivrons(); } pizza.preparer(); pizza.cuire(); pizza.couper(); pizza.emballer(); return pizza; }

Maintenant, nous transmettons le ). type de pizza commanderPizza(

Selon le type de pizza, nous inst la bonne classe concrte et nous ancions laffectons chaque variable din pizza. Notez que chaque pizza stance implmenter linterface Pizza. doit

Une fois que nous avons une Pizza, nous la prparons (vous savez, taler la pte, mettre la sauce et ajouter garnitures et fromage). Puis nous la faisons cuire, nous la coupons et nous la mettons dans une bote Chaque sous-type de Pizza (PizzaFromage, PizzaPoivrons, etc.) sait comment se prparer lui-mme.!

112

Chapitre 4

les patterns fabriques

Mais la pression du march vous oblige ajouter dautres types de pizza


Vous vous rendez compte que vos concurrents ont ajout leur carte deux pizzas trs tendance: une pizza aux fruits de mer et une pizza vgtarienne. De toute vidence, vous devez suivre le mouvement et les proposer aussi. Et comme vous navez pas vendu beaucoup de pizzas grecques ces temps derniers, vous dcidez de les retirer de la carte:
Pizza commanderPizza(String type) {

S nest PA n. Ce code la modificatio e r ferm changez vot Si vous ous devrez le carte, v re ce code et reprend r. modifie

Pizza pizza; if (type.equals(fromage)) { pizza = new PizzaFromage(); } else if (type.equals(grecque) { pizza = new PizzaGrecque(); } else if (type.equals(poivrons) { pizza = new PizzaPoivrons(); } else if (type.equals(fruitsDeMer) { pizza = new PizzaFruitsDeMer(); } else if (type.equals(vegetarienne) { pizza = new PizzaVegetarienne(); } pizza.preparer(); pizza.cuire(); pizza.couper(); pizza.emballer(); return pizza; }

e. Voici ce qui vari pe ty le Comme de pizza change us avec le temps, vo de nallez pas cesser e. modifier ce cod

Voici ce qui ne devrait pas changer. En majeure partie, la prparation, la cuisson et lemballage dune pizza nont pas vari depuis des lustres. Ce nest donc pas ce code qui changera, mais seulement les pizzas sur lesquelles il opre.

De toute vidence, lobligation de savoir quelle classe concrte est instancie bousille votre mthode commanderPizza() et lempche dtre ferme la modification. Mais maintenant que nous savons ce qui varie et ce qui ne varie pas, il est probablement temps de lencapsuler.

vous tes ici

113

encapsuler la cration des objets

Encapsuler la cration des objets


Nous savons maintenant que nous ferions mieux dextraire la cration des objets de la mthode commanderPizza(). Mais comment? Eh bien nous allons prendre ce code et le placer dans un autre objet qui ne soccupera que dune seule chose: crer des pizzas.
if (type.equals(fromage)) { pizza = new PizzaFromage(); } else if (type.equals(poivrons) { pizza = new PizzaPoivrons(); } else if (type.equals(fruitsDeMer) { pizza = new PizzaFruitsDeMer(); } else if (type.equals(vegetarienne) { pizza = new PizzaVegetarienne(); }

Pizza commanderPizza(String type) { Pizza pizza;

trayons Tout dabord, nous ex jets de le code qui cre les ob izza(). la mthode commanderP
pizza.preparer(); pizza.cuire(); pizza.couper(); pizza.emballer(); return pizza; }

Quallo

ns-nou

s place

r ici?

la de dans un objet dont Puis nous plaons ce co de crer des pizzas. Si un seule responsabilit est une pizza soit cre, cest autre objet a besoin qusadresser. cet objet quil faut

Les Fabriques grent les dtails de la cration des objets. Une fois que nous avons une SimpleFabriqueDePizzas, notre mthode commanderPizza() devient simplement un client de cet objet. Chaque fois quelle aura besoin dune pizza, elle demandera la fabrique de pizzas de lui en faire une. Le temps nest plus o la mthode commanderPizza() devait savoir si la pizza tait aux poivrons ou aux fruits de mer. Maintenant, une seule chose lui importe: obtenir une pizza qui implmente linterface Pizza afin de pouvoir appeler preparer(), cuire(), couper() et emballer(). Il nous reste encore quelques dtails rgler, par exemple par quoi la mthode commanderPizza() remplace-t-elle le code qui cre les objets? Pour le savoir, nous allons implmenter une simple fabrique de pizzas... 114
Chapitre 4

ple F a b ri q u e

De P

izzas

Nous avons un nom pour ce nouvel objet: nous lappelons une Fabrique.

Si m

les patterns fabriques

Construire une simple fabrique de pizzas


Nous allons commencer par la fabrique elle-mme. Quallons-nous faire? Nous allons dfinir une classe qui encapsule la cration des objets pour toutes les pizzas. La voici...

SimpleFabriqueDePizzas. Voici notre nouvelle classe, la ire dans la vie: crer des Elle na quune seule chose fa pizzas pour ses clients.
public class SimpleFabriqueDePizzas { public Pizza creerPizza(String type) { Pizza pizza = null;

e rd un s la o b s a ns d za() dan que tou e o s s i n e dfi rPiz thod er d Nous hode creeest la m pour cr mt ique. C iliseront fabr lients ut ances. les c elles inst nouv

if (type.equals(fromage)) { pizza = new PizzaFromage(); } else if (type.equals(poivrons)) { pizza = new PizzaPoivrons(); } else if (type.equals(fruitsDeMer)) { pizza = new PizzaFruitsDeMer(); } else if (type.equals(vegetarienne)) { pizza = new PizzaVegetarienne(); } return pizza; } }

avons Voici le code que nous extrait de la mthode commanderPizza().

de pizza, tout Ce code est toujours paramtr par le type () dorigine. comme ltait notre mthode commanderPizza

Q: R:

questions stupides

Il ny a pas de

Quel est lavantage de procder ainsi? On dirait quon transfre simplement le problme un autre objet.

Q: R:

Jai vu une conception similaire dans laquelle une fabrique comme celle-ci est dfinie comme une mthode statique. Quelle est la diffrence?

Il faut vous rappeler une chose: la SimpleFabriqueDePizzas peut avoir plusieurs clients. Nous navons vu que la mthode commanderPizza(), mais il pourrait y avoir une classe CartePizzas qui utiliserait la fabrique pour accder la description et au prix des pizzas. Nous pourrions galement avoir une classe LivraisonADomicile qui grerait les pizzas diffremment de notre classe Pizzeria mais

qui serait galement un client de la fabrique. Donc, en encapsulant la cration des pizzas dans une seule classe, nous navons plus quun seul endroit auquel apporter des modifications quand limplmentation change. Noubliez pas que nous sommes galement sur le point de supprimer de notre code client les instanciations concrtes!

Dfinir une fabrique simple comme une mthode statique est une technique courante, et on la nomme souvent fabrique statique. Pourquoi employer une mthode statique? Parce que vous navez pas besoin dinstancier un objet pour utiliser une mthode de cration. Mais souvenez-vous que cette technique a un inconvnient: vous ne pouvez pas sous-classer ni modifier le comportement de la mthode de cration. vous tes ici

115

simple fabrique

Retravailler la classe Pizzeria


Il est maintenant temps darranger notre code client. Ce que nous voulons, cest nous reposer sur la fabrique pour quelle cre les pizzas notre place. Voici les modifications:

Nous donnons maintenant Pizzeria une rfrence une SimpleFabriqueDePizzas.


public class Pizzeria { SimpleFabriqueDePizzas fabrique; public Pizzeria(SimpleFabriqueDePizzas fabrique) { this.fabrique = fabrique; } public Pizza commanderPizza(String type) { Pizza pizza; pizza = fabrique.creerPizza(type); pizza.preparer(); pizza.cuire(); pizza.couper(); pizza.emballer(); return pizza; } // autres mthodes }

Nous transmettons la fabrique Pizzeria dans le constructeur.

Et la mthode commanderPizza() utilise la fabrique pour crer ses pizzas en transmettant simplement le type de la commande. Remarquez que nous avons remplac loprateur new par une mthode de cration de lobjet fabrique. Nous navons plus dinstanciations concrtes!

A
116
Chapitre 4

Musclez vos neurones

Nous savons que la composition va nous permettre (entre autres) de modifier dynamiquement le comportement au moment de lexcution parce que nous pouvons changer les implmentations. Comment pourrions-nous appliquer cela Pizzeria? Quelles implmentations de fabrique pourrions-nous changer?
Nous ne savons pas quoi vous pensez, mais nous pensons des fabriques de pizzas de style breton, alsacien et provenal (sans oublier la Corse).

les patterns fabriques

Fabrique Simple: dfinition


La Fabrique Simple nest pas vraiment un Design Pattern: cest plutt un idiome de programmation. Mais, comme son emploi est courant, nous lui avons dcern une mention honorable de Pattern Tte La Premire. Certains informaticiens confondent cet idiome avec le Pattern Fabrication. Ainsi, la prochaine fois quil y aura un silence embarrass entre vous et un autre dveloppeur, vous aurez un sujet intressant pour briser la glace. Ce nest pas parce que Fabrique Simple nest pas un VRAI pattern que nous nallons pas tudier comment il fonctionne. Jetons un coup dil au diagramme de classes de notre nouvelle Pizzeria:

1ere Tte tion Men rable o Hon

Mention Honorable

Voici la fabrique dans laquelle nous crons les pizzas. Ce doit tre la seule partie de notre application qui se rfre des classes Pizza concrtes.
Pizzeria
commanderPizza()

de la Et voici le produit a! fabrique: une pizz Nous avons dfini Pizza comme une classe abstraite avec quelques iles implmentations ut qui peuvent tre redfinies.
PizzaPoivrons

SimpleFabriqueDePizzas
creerPizza()
preparer() cuire() couper()

Pizza

nt de Ceci est le clieizzeria la fabrique. Pant par la passe mainten DePizzas pour SimpleFabriquestances de pizzas. obtenir des in

tion La mthode de cra est souvent dclare statiquement


PizzaFromage

emballer()

Et voil nos produits concrets. Cha cun doit implmenter linterface Pizza* qui signifie en loccurrence te (ce la classe abstraite Pizza) et ndre concret. Tant que cest le cas, iltre tre cr par la fabrique et tre peut retourn au client.

PizzaVegetarienne

PizzaFruitsDeMer

Considrez Fabrique Simple comme un chauffement. Nous allons bientt explorer deux patterns trs robustes qui sont tous deux des fabriques. Mais ne vous inquitez pas, nous nen avons pas fini avec la pizza!

*Juste un autre petit rappel: dans le contexte des design patterns, lexpression implmenter une interface ne veut PAS toujours dire crire une classe qui implmente une interface Java en utilisant le mot-cl implements dans sa dclaration. Dans son acception gnrale, elle signifie quune classe concrte qui implmente une mthode dun supertype (qui peut tre une classe OU une interface) est toujours considre comme implmentant linterface de ce supertype.
vous tes ici

117

pizzerias en franchise

Franchiser les pizzerias


Votre Pizzeria dObjectville a connu une telle russite que vous avez battu la concurrence plates coutures. Maintenant, chacun veut une Pizzeria proximit de chez lui. En tant que franchiseur, vous voulez vous assurer de la qualit de la production des franchises et vous voulez quils utilisent votre code. Mais quen est-il des diffrences rgionales? Chaque franchise peut souhaiter proposer diffrents styles de pizzas (Brest, Strasbourg, et Marseille par exemple), selon son emplacement gographique et les gots des amateurs de pizza locaux.

ab r

iqueDeP

Piz

qu

eD ePizzas

Nous avons vu une approche...


Si nous extrayons SimpleFabriqueDePizzas et que nous crons trois fabriques diffrentes, FabriqueDePizzasBrest, FabriqueDePizzasStrasbourg et FabriqueDePizzasMarseille, il nous suffit de composer Pizzeria avec la fabrique approprie et la franchise est prte dmarrer. Cest une approche possible. Voyons quoi elle ressemblerait...

118

Chapitre 4

St ra sbourg

ze r i a
i br Fa

iz z as

Vous voulez que toutes les boutiques en franchise exploitent votre code de Pizzeria pour prparer toutes les pizzas de la mme manire.
F

Lune des franchises veut une fabrique qui confectionne des pizzas style Brest: pte fine, sauce releve et juste un petit peu de fromage.

Brest

Une autre franchise veut une fabrique qui confectionne des pizzas style Strasbourg; ses clients aiment les pizzas avec une pte souffle, beaucoup de sauce et des tonnes de fromage.

les patterns fabriques

FabriqueDePizzasBrest fabriqueBrest = new FabriqueDePizzasBrest(); Pizzeria boutiqueBrest = new Pizzeria(fabriqueBrest); Puis boutiqueBrest.commander(Vgtarienne);

Ici, nous crons une fabrique pour faire des pizzas style Brest.

nous crons un Pizzeria et nous lui transmettons une rfrence la fabrique Brest. ... et quand nous faisons des pizzas, nous obtenons des pizzas brestoises.

FabriqueDePizzasStrasbourg fabriqueStrasbourg = new FabriqueDePizzasStrasbourg(); Pizzeria boutiqueStrasbourg = new Pizzeria(fabriqueStrasbourg); boutiqueStrasbourg.commander(Vgtarienne);

Pareil pour Strasbourg: nous crons une fabrique de pizzas style Strasbourg et nous crons une Pizzeria compose avec une fabrique Strasbourg. Quand nous crons des pizzas, elles ont le got de celles de Strasbourg.

Mais le contrle qualit vous semble lgrement insuffisant...


Vous avez donc test le concept de FabriqueSimple, et vous avez dcouvert que les franchises utilisaient bien votre fabrique pour crer des pizzas, mais quelles commenaient employer leur propres procdures maison pour le reste du processus: elles avaient un mode de cuisson un peu diffrent, oubliaient de couper la pizza et achetaient leurs botes dautres fournisseurs. Aprs avoir quelque peu repens le problme, vous constatez que ce que vous aimeriez rellement faire serait de crer une structure qui lie la boutique et la cration des pizzas, tout en permettant de conserver de la souplesse. Dans notre premier programme, avant la SimpleFabriqueDePizzas, le code de confection des pizzas tait bien li Pizzeria, mais la souplesse manquait lappel. Alors, comment faire pour avoir notre pizza et la manger?
Comme je fais des pizzas depuis des annes, jai pens que je pouvais ajouter mes propres amliorations aux procdures de Pizzeria...

attendez dun Ce nest pas ce que vous ulez PAS bon franchis. Vous ne vo pizzas. savoir ce quil met sur ses
119

vous tes ici

laisser les sous-classes dcider

Une structure pour la Pizzeria


Il existe un moyen de localiser toutes les activits de fabrication des pizzas dans la classe Pizzeria tout en laissant aux franchises la libert davoir leur propre style rgional. Voici ce que nous allons faire: nous allons remettre la mthode creerPizza() dans Pizzeria. Mais ce sera cette fois une mthode abstraite, puis nous crerons une sous-classe de Pizzeria pour chaque style rgional. Examinons dabord les modifications de Pizzeria:

Pizzeria est maintenant abstraite (vous allez voir pourquoi ci-dessous).


public abstract class Pizzeria {

public Pizza commanderPizza(String type) { Pizza pizza; pizza = creerPizza(type); pizza.preparer(); pizza.cuire(); pizza.couper(); pizza.emballer(); return pizza; } abstract creerPizza(String type); }

Maintenant, creerPizza() est de nouveau un appel une mthode de Pizzeria et non un objet fabrique. Ceci ne change pas...

Et nous avons transfr notre obj et fabrique cette mthode.

Notre mthode de fabrication maintenant abstraite dans Pizzer est ia.


Maintenant, notre boutique attend des sous-classes. Nous allons donc avoir une sous-classe pour chaque type rgional (PizzeriaBrest, PizzeriaStrasbourg, PizzeriaMarseille) et chaque sous-classe choisira ce qui constitue une pizza. Voyons un peu comment cela va fonctionner.

120

Chapitre 4

les patterns fabriques

Laisser les sous-classes dcider


Souvenez-vous: Pizzeria a dj un systme de commandes bien au point dans la mthode commanderPizza() et vous voulez tre certain quelle est cohrente entre toutes les franchises. Ce qui varie entre les pizzerias rgionales, cest le style de pizzas quelles fabriquent la pizza de Brest a une pte fine, celle de Strasbourg est paisse, etc. et nous allons remplacer toutes ces variations dans la mthode creerPizza() et la rendre responsable de la cration du bon type de pizza. Pour ce faire, nous laissons chaque sous-classe de Pizzeria dfinir quoi va ressembler la mthode creerPizza(). Ainsi, nous aurons un certain nombre de sous-classes concrtes de Pizzeria, chacune ayant ses propres variantes, mais toutes sinsrant dans la structure de Pizzeria et continuant utiliser notre mthode bien au point: commanderPizza().

Pizzeria
creerPizza() commanderPizza()

Chaque sous-classe redfinit la mthode creerPizza(), tandis que toutes les sous-classes utilisent la mthode commanderPizza() dfinie dans Pizzeria. Nous pourrions faire de commanderPizza() une mthode finale si nous voulions rellement appliquer ceci.

Si une franchise veut proposer des pizzas style Brest ses clients, elle utilise la sous-classe sa PizzeriaStyleBrest qui possde propre mthode creerPizza() qui sert crer ce style de pizzas.

PizzeriaStyleBrest
creerPizza()

PizzeriaStyleStrasbourg
creerPizza()

a() Souvenez-vous: puisque creerPizz les s tou ia, zer Piz s est abstraite dan NT IVE DO ia zer sous-types de Piz implmenter la mthode.

De mme, en utilisant la sous-classe PizzeriaStyleStrasbourg, nous obtenons une implmentation de creerPizza() avec les ingrdients propres au style Strasbourg.

public Pizza creerPizza(type) {

public Pizza creerPizza(type) { if (type.equals(fromage)) { pizza = new PizzaFromageStyleBrest(); } else if (type.equals(poivrons) { pizza = new PizzaPoivronsStyleBrest(); } else if (type.equals(fruitsDeMer) { pizza = new PizzaFruitsDeMerStyleBrest(); } else if (type.equals(vegetarienne) { pizza = new PizzaVegetarienneStyleBrest(); } }
}

if (type.equals(fromage)) { pizza = new PizzaFromageStyleStrasbourg(); } else if (type.equals(poivrons) { pizza = new PizzaPoivronsStyleStrasbourg(); } else if (type.equals(fruitsDeMer) { pizza = new PizzaFruitsDeMerStyleStrasbourg(); } else if (type.equals(vegetarienne) { pizza = new PizzaVegetarienneStyleStrasbourg(); }

vous tes ici

121

comment les sous-classes dcident-elles?

Je ne comprends pas. Les sousclasses de Pizzeria ne sont que dessousclasses. Comment peuvent-elles dcider quoi que ce soit? Je ne vois aucune logique de prise de dcision dans le code de PizzeriaStyleBrest....

Bon. Rflchissez du point de vue de la mthode commanderPizza(): elle est dfinie dans la classe abstraite Pizzeria, mais les types concrets ne sont crs que dans les sous-classes.

Pizzeria
creerPizza() commanderPizza()

ite finie dans la classe abstra commanderPizza() est ds-classes. La mthode e Pizzeria, pas dans les sou la sous-classe qui excut na donc aucune ide de i fabrique les pizzas. rellement le code et qu

Maintenant, pour aller un peu plus loin, la mthode commanderPizza() fait un tas de choses avec un objet Pizza (elle le prpare, le fait cuire, le coupe et le met dans une bote), mais comme Pizza est abstraite, commanderPizza() na aucune ide des classes concrtes qui sont impliques. Autrement dit, elle est dcouple!
Pizzeria
creerPizza() commanderPizza() pizza = creerPizza(); pizza.preparer(); pizza.cuire(); pizza.couper(); pizza.emballer();

Lorsque commanderPizza() appelle creerPizza(), lune de vos sous-classes entre en scne pour crer une pizza. Quelle sorte de pizza va-t-elle faire? Cest le choix de la boutique partir de laquelle vous passez commande, PizzeriaStyleBrest ou PizzeriaStyleStrasbourg, qui va le dterminer.
PizzeriaStyleBrest
creerPizza()

creerPizza() pour obtenir commanderPizza() appelle genre de pizza va-t-elle un objet Pizza. Mais quel mmanderPizza() ne peut pas recevoir? La mthode cocomment faire. Alors, qui dcider: elle ne sait pas dcide?

PizzeriaStyleStrasbourg
creerPizza()

Est-ce donc une dcision en temps rel que les sous-classes prennent? Non, mais du point de vue de commanderPizza(), si vous avez choisi un PizzeriaStyleBrest, cette sous-classe va dterminer quelle pizza sera cre. Ce ne sont donc pas les sous-classes qui dcident rellement cest vous qui avez dcid en choisissant votre boutique. En revanchent, elles dterminent bien la sorte de pizza qui sera fabrique.

122

Chapitre 4

les patterns fabriques

Crer une Pizzeria


tre une franchise a ses avantages. Vous profitez de toutes les fonctionnalits de Pizzeria pour rien. Il suffit aux boutiques rgionales de sous-classer Pizzeria et de fournir une mthode creerPizza() qui implmente leur style de Pizza. Nous allons nous occuper des trois grand styles de pizzas pour les franchiss. Voici le style Brest:

e Pizza, et la souscreerPizza() retourne un bilit de dterminer la classe a lentire responsa tancie Pizza concrte quelle ins

Comme PizzeriaBrest tend Pizzeria, elle hrite (entre autres) de la mthode commanderPizza().

public class PizzeriaBrest extends Pizzeria { Pizza creerPizza(String item) { if (choix.equals(fromage)) { return new PizzaFromageStyleBrest(); } else if (choix.equals(vegetarienne)) { return new PizzaVegetarienneStyleBrest(); } else if (choix.equals(fruitsDeMer)) { return new PizzaFruitsDeMerStyleBrest(); } else if (choix.equals(poivrons)) { return new PizzaPoivronsStyleBrest(); } else return null; } }

Nous devons implmenter creerPizza() puisquelle est abstraite dans Pizzeria.

Voici lendroit o nous crons nos classes concrtes. Pour chaque type de Pizza nous crons le style Brest.

* Notez que la mthode commanderPizza() de la superclasse ne dispose daucun indice sur la Pizza que nous crons: elle sait simplement quelle peut la prparer, la faire cuire la dcouper et lemballer!
Une fois nos sous-classes de Pizzeria construites, il va tre temps de songer commander une pizza ou deux. Mais auparavant, pourquoi ne pas essayer de construire les boutiques Style Strasbourg et Style Marseille page suivante?

vous tes ici

123

mthode de fabrique

vos crayons Sharpen your pencil


Nous en avons fini avec la classe PizzeriaBrest. Encore deux et nous serons prts accorder des franchises! crivez ici les implmentations de PizzeriaStrasbourg et de PizzeriaMarseille:

124

Chapitre 4

les patterns fabriques

Dclarer une mthode de fabrique


Rien quen apportant une ou deux transformations Pizzeria, nous sommes passs dun objet grant linstanciation de nos classes concrtes un ensemble de sous-classes qui assument maintenant cette responsabilit. Voyons cela de plus prs:
public abstract class Pizzeria {

public Pizza commanderPizza(String type) { Pizza pizza; pizza = creerPizza(type); pizza.preparer(); pizza.cuire(); pizza.couper(); pizza.emballer(); return pizza; } protected abstract Pizza creerPizza(String type); // autres mthodes

Les sous-classes nt de Pizzeria gre s objets linstanciation dens la notre place daizza(). mthode creerP
PizzeriaStyleBrest
creerPizza()

PizzeriaStyleStrasbourg
creerPizza()

Toute la responsabilit de linstanciation des Pizzas a t transfre une mthode qui se comporte comme une fabrique.

Code la loupe
Une mthode de fabrique gre la cration des objets et lencapsule dans une sous-classe. Cette technique dcouple le code client de la superclasse du code de cration des objets de la sous-classe.

abstract Produit fabrication(String type)

e Une mthode d re t ut fabrique pe non) paramtre (ou re pour choisir entriantes diffrentes va dun produit.

Comme une mtho fabrication est abde de on compte sur les straite, classes pour grer souscration des objetsla .

Une mthode de fabrication retourne un Produit quon utilise gnralement dans les mthodes dfinies dans la superclasse.

e isole le Une mthode de fabriqu perclasse, tel client (le code de la su e lui vite de devoir commanderPizza(): ellProduit concret qui connatre la sorte de est rellement cre.
vous tes ici

125

commander une pizza

Voyons comment tout ceci fonctionne: commandons des pizzas grce la mthode de fabrique de pizzas

Jaime les pizzas style Brest... Tu sais, une pte fine et croustillante avec un petit peu de fromage et une super sauce.

Je prfre le style Strasbourg, avec une bonne pte bien paisse et des tonnes de fromage.

Luc

Michel

Luc doit commander sa pizza dans une PizzeriaBrest.

Michel doit commander sa pizza une PizzeriaStrasbourg. La mthode de commande est la mme, mais la pizza sera diffrente!

Mais comment commandent-ils?


1 2

Tout dabord, Michel et Luc ont besoin dune instance de Pizzeria. Michel doit instancier une PizzeriaStrasbourg et Luc une PizzeriaBrest. Avec une Pizzeria leur disposition, Luc et Michel appellent tous deux la mthode commanderPizza() et lui transmettent le type de pizza dont ils ont envie (fromage, poivrons, etc.). Pour crer les pizzas, la mthode creerPizza() est appele. Elle est dfinie dans les deux sousclasses PizzeriaBrest et PizzeriaStrasbourg. Telles que nous les avons dfinies, PizzeriaBrest instancie une pizza style et PizzeriaStrasbourg une pizza style Strasbourg. Dans lun et lautre cas, la Pizza est retourne la mthode commanderPizza(). La mthode commanderPizza() na aucune ide du genre de pizza qui a t cr, mais elle sait que cest une pizza: elle la prpare, la fait cuire, la dcoupe et lemballe pour Luc et Michel.

126

Chapitre 4

les patterns fabriques

Voyons comment les pizzas sont rellement faites sur commande

Dans les coulisses

Suivons la commande de Luc: tout dabord, il nous faut une PizzeriaBrest:


Pizzeria pizzeriaBrest = new PizzeriaBrest();

Cre une instance de PizzeriaBrest.

Maintenant que nous avons une boutique, nous pouvons izze r ia B re s t prendre une commande:

izza() est La mthode commanderP Brest (la ria appele sur lobjet pizze zeria sexcute). mthode dfinie dans Piz

Puis la mthode commanderPizza() appelle la mthode creerPizza():


Pizza pizza = creerPizza(fromage);

Souvenez-vous que creerPizza(), la mthode de fabrication, est implmente dans la sous-classe. En loccurrence, elle retourne une PizzaFromageBrest.

Pizza

Enfin, nous avons une pizza brute et la mthode commanderPizza() finit de la prparer:
pizza.preparer(); pizza.cuire(); pizza.couper(); pizza.emballer();

izza() obtient La mthode commanderPactement de une Pizza, sans savoir exsagit. quelle classe concrte il

t dfinies Toutes ces mthodes son retourne e dans la pizza spcifiqu brique depuis la mthode de fa ns creerPizza(), dfinie da PizzeriaBrest.

creerPizza(fromage)

pizzeriaBrest.commanderPizza(fromage);

vous tes ici

127

les classes de pizza

Il ne nous manque quune chose: la PIZZA!


Notre Pizzeria ne va pas faire un tabac si on ny trouve pas de pizzas. Nous allons donc en implmenter:
lasse r par une c e c n e m m o c Nous allons izza dont toutes les abstraite P rtes driveront. pizzas conc
public abstract class Pizza { String nom; String pate; String sauce; ArrayList garnitures = new ArrayList();

pte, un Chaque Pizza a un nom, un type de dients. gr din type de sauce et un ensemble

void preparer() { System.out.println(Prparation de + nom); System.out.println(talage de la pte...); System.out.println(Ajout de la sauce...); System.out.println(Ajout des garnitures: ); for (int i = 0; i < garnitures.size(); i++) { System.out.println( + garnitures.get(i)); } } void cuire() { System.out.println(Cuisson 25 minutes 180); } void couper() { System.out.println(Dcoupage en parts triangulaires); } void emballer() { System.out.println(Emballage dans une bote officielle); } public String getNom() { return nom; }

La classe abstraite fournit des comportements par dfaut pour la cuisson, le dcoupage et lemballage. La prparation suit un certain nombre dtapes ordonnes selon une suite particulire.

RAPPEL: les listings ne contiennent pas dinstructions import et package. Vous pouvez tlcharger le code source complet sur le site du livre, dont lURL se trouve page xxxi de lIntro.
128
Chapitre 4

les patterns fabriques

Maintenant, il ne nous faut plus que quelques classes concrtes... Que diriez-vous de dfinir des pizzas au fromage style Brest et Strasbourg?
La Pizza brestoise a sa propre sauce marinara et une pte fine.

public class PizzaFromageStyleBrest extends Pizza { public PizzaFromageStyleBrest() { nom = Pizza sauce style brest et fromage; pate = Pte fine; sauce = Sauce Marinara; } } garnitures.add(Parmigiano reggiano rp);

Et une garniture, du parm

igiano reggiano!

public class PizzaFromageStyleStrasbourg extends Pizza { public PizzaFromageStyleStrasbourg() { nom = Pizza pte style Strasbourg et fromage; pate = Extra paisse; sauce = Sauce aux tomates cerise; } void couper() { System.out.println(Dcoupage en parts carres); } garnitures.add(Lamelles de mozzarella);

La Pizza Strasbourg a une sauce aux tomates cerise et une pte trs paisse.

La Pizza Strasbourg a de tonnes de mozzarella! s

La pizza Strasbourg redfinit galement la mthode couper() afin de dcouper des parts carres.

vous tes ici

129

fabriquons des pizzas

Nous avons assez attendu, il est temps pour quelques pizzas!


public class PizzaTestDrive { public static void main(String[] args) { Pizzeria boutiqueBrest = new PizzeriaBrest(); Pizzeria boutiqueStrasbourg = new PizzeriaStrasbourg();

ux us crons de Dabord, no iffrentes. boutiques d Puis nous en utilisons une pour la commande de Luc.

Pizza pizza = boutiqueBrest.commanderPizza(fromage); System.out.println(Luc a command une + pizza.getNom() + \n); pizza = boutiqueStrasbourg.commanderPizza(fromage); System.out.println(Michel a command une + pizza.getNom() + \n); }

Et lautre pour celle de Michel.

Fichier dition Fentre Aide PlusDeFromage

% java PizzaTestDrive Prparation de Pizza sauce style brest au fromage talage de la pte... Ajout de la sauce... Ajout des garnitures: Parmigiano reggiano rp Cuisson 25 minutes 180 Dcoupage en parts triangulaires Emballage dans une bote officielle Luc a command une Pizza sauce style brest et fromage Prparation de Pizza pte Strasbourg et fromage talage de la pte... Ajout de la sauce... Ajout des garnitures: Mozzarella en lamelles Cuisson 25 minutes 180 Dcoupage en parts carres Emballage dans une bote officielle Michel a command une Pizza pte Strasbourg et fromage

La prparation commence, les garnitures sont ajoutes, et les deux pizzas sont cuites, dcoupes et emballes. Notre superclasse na jamais eu besoin de connatre les dtails: la sous-classe set occupe de tout rien quen instanciant la bonne pizza.

130

Chapitre 4

les patterns fabriques

Il est enfin temps de rencontrer le pattern Fabrication


Tous les patterns de fabrique encapsulent la cration des objets. Le Design Pattern Fabrication (Factory Method) encapsule la cration des objets en laissant aux sous-classes le choix des objets crer. Observons les diagrammes de classes pour voir qui sont les acteurs de ce pattern:

Les classes Crateur (ou Facteur)


Voici notre classe Cra teur abstraite. Elle dfinit une mthode de fabr abstraite que les sousique implmentent pour pr -classes oduire des produits.

Pizzeria
creerPizza() commanderPizza()

Le crateur contient souvent du code qui dpend dun produit abstrait produit par une sous-classe. Le crateur ne sait jamais vraiment quel produit concret a t produit.

PizzeriaBrest

PizzeriaStrasbourg
creerPizza()

La mth est notr ode creerPizza fabricat e mthode de () produits ion. Elle produit . des

creerPizza()

oduisent Les classes qui pr nomment des produits se ncrets. des crateurs co

Comme chaque franchise a sa propre sous-classe de Pizzeria, elle est libre de crer son propre style de pizza en implmentant creerPizza().

Les classes Produit


Pizza

Les fabriques produisent des produits. Dans la Pizzeria, notre produit est une Pizza.

Voici les produits concrets toutes les pizzas qui sont produites par nos boutiques.

PizzaFromageStyleBrest
PizzaFromageStyleBrest

PizzaVegetarienneStyleStrasbourg PizzaVegetarienneStyleStrasbourg
PizzaPoivronsStyleStrasbourg

PizzaVegetarienneStyleBrest PizzaFruitsDeMerStyleBrest

PizzaFromageStyleStrasbourg

vous tes ici

131

crateurs et produits

Un autre point de vue: des hirarchies de classes parallles


Nous avons vu que nous obtenions une structure en combinant la mthode commanderPizza() avec une mthode de fabrique. Une autre faon de se reprsenter la structure fournie pas ce pattern consiste envisager la faon dont il encapsule la connaissance du produit dans chaque crateur. Voyons les deux hirarchies de classes parallles et les relations quelles entretiennent:

Les classes Produit


Pizza

Notez le paralllisme de ces deux hirarchies de classes: toutes deux ont des classes abstraites qui sont tendues par des classes concrtes.Ce sont celles-ci qui connaissent les implmentations spcifiques de Brest et Strasbourg.

Les classes Crateur


Pizzeria
creerPizza() commanderPizza()

PizzaPoivronsStyleBrest

PizzaVegetarienneStyleStrasbourg PizzaFruitsDeMerStyleStrasbourg

PizzeriaBrest
creerPizza()

PizzeriaStrasbourg
creerPizza()

PizzaFromageStyleBrest

PizzaFruitsDeMerStyleBrest
PizzaVegetarienneStyleBrest

PizzaPoivronsStyleStrasbourg PizzaFromageStyleStrasbourg

rest sur la e B a i r l rg izze le savoir zas sty P e asse trasbou savoir s l s z s c i t a p u l a L eriaS out le ire de La c psule to aire des a Pizz psule t de fa ourg. f c n e on de enca la faon Strasb fa st. sur as style Bre pizz

La mthode de

fabrication

ncapsulation est la cl de le

de ce savoir.

132

Chapitre 4

les patterns fabriques

Problme de conception
Il nous faut un autre type de pizza pour ces fous de Marseillais (fous dans le bon sens du terme bien sr). Reprsentez lautre ensemble de classes parallles dont vous avez besoin pour ajouter une rgion Marseille notre Pizzeria.

Pizzeria
creerPizza() commanderPizza()

Termin
PizzeriaBrest
creerPizza()

iagram ez le d

me ici

PizzeriaStrasbourg
creerPizza()

PizzaFromageStyleBrest
PizzaVegetarienneStyleBrest PizzaFruitsDeMerStyleBrest

PizzaVegetarienneStyleStrasbourg PizzaFruitsDeMerStyleStrasbourg PizzaPoivronsStyleStrasbourg

PizzaPoivronsStyleBrest

PizzaFromageStyleStrasbourg

Maintenant, inscrivez les cinq garnitures les plus bizarres que vous pouvez imaginer mettre sur une pizza. Ensuite, vous serez prt faire des affaires avec vos pizzas Marseille!

vous tes ici

133

fabrication: dfinition

Le Pattern Fabrication: dfinition


Il est temps de rvler la dfinition officielle du Pattern Fabrication (Factory Method):
Le pattern Fabrication dfinit une interface pour la cration dun objet, mais en laissant aux sous-classes le choix des classes instancier. Fabrication permet une classe de dlguer linstanciation des sous-classes.

eur rriez l ue u o p s u Vo der ce q , mais deman ix signifie ous Comme dans la dfinition officielle, vous entendrez souvent des dveloppeurs dire que la cho arions que v nant ce Fabrication laisse aux sous-classes le choix des classes instancier. Ils parlent de choix nous p ssez mainte eux! non seulement parce que le pattern permet aux sous-classes elles-mmes de dcider au connai n mieux qu moment de lexcution, mais aussi parce que la classe cratrice ne sait rien des produits rels patter
qui seront crs, choix qui appartient entirement la sous-classe utilise.

Comme toute fabrication, le pattern Fabrication nous offre un moyen dencapsuler linstanciation de types concrets. Si vous observez le diagramme de classes ci-dessous, vous constaterez que le Crateur abstrait vous fournit une interface dote dune mthode permettant de crer des objets, alias fabrication. Toutes les autres mthodes implmentes dans le Crateur abstrait sont crites pour oprer sur les produits crs par la fabrique. Seules les sous-classes implmentent rellement la mthode de fabrique et crent des produits.

Produit

Createur fabrication() uneOperation()

ts doivent Tous les produi mme interface, implmenter la ses qui utilisent pour que les clas sent se rfrer les produits puis n la classe linterface, no concrte.
ProduitConcret

t une classe qui Le Crateur es mentations de contient les impl des destines toutes les mthooduits, except la manipuler les pr rication. mthode de fab La mthode abstraite fabrication() est celle que toutes les sous-classes du Crateur doivent implmenter..

CreateurConcret fabrication()

Le CrateurConcret implmente la mthode fabrication(), la mthode qui cre rellement les produits.

Le CrateurConcret a la responsabilit de crer un ou plusieurs produits concrets. Cest la seule classe qui sache comment crer ces produits.
134
Chapitre 4

les patterns fabriques

Q: R:

questions stupides

Il ny a pas de

Quel est lavantage du Pattern Fabrication quand on na quun CrateurConcret?

Le Pattern Fabrication est utile mme si vous navez quun CrateurConcret parce que vous dcouplez limplmentation du produit de son utilisation. Si vous ajoutez dautres produits ou si vous modifiez limplmentation dun produit existant, le Crateur ne sera pas affect (parce que le Crateur nest fortement coupl aucun ProduitConcret).

Nous avons implment ce quon appelle une fabrication paramtre. Comme vous lavez remarqu, elle peut crer plusieurs objets en fonction de largument quon lui transmet. Toutefois, il est frquent quune fabrication ne produise quun seul objet et quelle ne soit pas paramtre.

R:

Q: R:

Q: R:

Vos types paramtrs ne semblent pas trs srs. Je ne transmets jamais quune chane de caractres, aprs tout! Que se passerait-il si je demandais une pizza aux poireaux?

Serait-il correct de dire que nos boutiques de Brest et de Strasbourg sont implmentes avec une Fabrique Simple? Cela en a tout lair.

Les deux patterns sont similaires mais utiliss de faon diffrente. Mme si limplmentation de chaque boutique concrte ressemble beaucoup une SimpleFabricationDePizzas, noubliez pas que les boutiques concrtes tendent une classe dans laquelle la mthode creerPizza() est dclare abstraite. Il appartient chaque boutique de dfinir le comportement de creerPizza(). Dans Fabrication Simple, la fabrication est un autre objet qui est combin avec la Pizzeria.

Sans aucun doute. Cela provoquerait ce quon appelle dans le mtier une erreur dexcution. ils existe plusieurs autres techniques plus sophistiques que vous pouvez appliquer pour mettre en uvre la scurit du type des paramtres, autrement dit pour assurer que les erreurs dans les arguments seront interceptes par le compilateur. Vous pouvez par exemple crer des objets qui reprsentent les types des paramtres, utiliser des constantes statiques ou, en Java5, employer des enums.

Q: R:

Q: R:

Est-ce que la mthode fabrication() et le Crateur sont toujours abstraits?

Je ne suis pas sr davoir bien compris la diffrence entre Fabrique Simple et Fabrication. Ils ont lair trs similaires, except que, dans Fabrication, la classe qui retourne la pizza est une sous-classe. Pouvezvous expliquer?

Non. Vous pouvez dfinir une mthode de fabrication par dfaut afin de crer un produit concret. Ainsi, vous disposez toujours dun moyen de crer des produits, mme sil ny a pas de sous-classes du Crateur.

Q:

Chaque boutique peut confectionner quatre sortes de pizzas diffrentes selon le type qui lui est transmis. Tous les crateurs concrets crent-ils plusieurs produits ou arrive-t-il quils nen fassent quun?

Vous avez raison: il est vrai que les sous-classes ressemblent beaucoup Fabrique Simple, mais Fabrique Simple ne sert quune fois tandis que Fabrication vous permet de crer une structure qui laisse aux sous-classes le choix de limplmentation qui sera utilise. Par exemple, la mthode commanderPizza() de la Fabrication fournit un cadre gnral pour crer des pizzas qui sappuis sur une mthode de fabrication pour crer les classes concrtes qui entrent en jeu dans la confection dune pizza. En sousclassant la classe Pizzeria, vous dcidez quels sont les produits concrets qui participent la cration de la pizza que commanderPizza() retourne. Comparez cette technique avec Fabrique Simple, qui vous fournit un moyen dencapsuler les objets, mais sans la souplesse de Fabrication parce quelle ne permet pas de faire varier les objets que vous crez. vous tes ici

135

matre et disciple

Matre et disciple...
Matre: Petit scarabe, dis-moi comment se passe ta formation. Disciple: Matre, jai continu tudier lencapsulation de ce qui varie... Matre: Continue... Disciple: Jai compris quon pouvait encapsuler le code qui cre les objets. Quand on a du code qui instancie des classes concrtes, cest un domaine vou en permanence au changement. Jai appris une technique nomme fabrications qui permet dencapsuler ce comportement dinstanciation. Matre: Et ces fabrications, que nous apportent-elles? Disciple: De nombreux avantages. En plaant tout le processus de cration dans une mthode ou dans un objet, jvite de dupliquer du code et de multiplier les oprations de maintenance. Cela signifie galement que les clients ne dpendent que des interfaces et non des classes concrtes ncessaires pour instancier les objets. Comme je lai appris, cela me permet de programmer pour une interface, non une implmentation, et cela rend mon code plus souple et plus extensible. Matre: Oui, Scarabe, tu commences avoir des rflexes objet. Astu des questions poser ton matre aujourdhui? Disciple: Matre, je sais quen encapsulant la cration des objets je code en mappuyant sur des abstractions et je dcouple mon code client des implmentations relles. Mais mon code de fabrication doit toujours utiliser des classes concrtes pour instancier des objets rels. Ne suis-je pas en train de mgarer? Matre: Scarabe, la cration des objets est une ralit de lexistence; si nous ne crons pas dobjets, nous ncrirons jamais un seul programme Java. Mais, grce la connaissance de cette ralit, nous pouvons concevoir nos programmes afin dencapsuler notre code de cration, tout comme nous enfermons nos brebis dans un parc, pour les empcher de sgarer elles aussi. Une fois que nous avons enferm ce code, nous pouvons en prendre soin et le protger. Mais si nous le laissons courir en libert, nous ne rcolterons jamais de laine. Disciple: Matre, je vois la vrit maintenant. Matre: Je savais que tu comprendrais. Va, maintenant et mdite sur la dpendance des objets.

136

Chapitre 4

les patterns fabriques

Une Pizzeria trs dpendante


vos crayons Sharpen your pencil
Faisons comme si vous naviez jamais entendu parler de fabrication OO. Voici une version de la Pizzeria qui nutilise pas de fabrication. Faites le compte du nombre dobjets concrets dont cette classe dpend. Si vous ajoutez des pizzas de style Marseille cette Pizzeria, de combien dobjets dpendra-t-elle alors?
public class PizzeriaDependante { public Pizza creerPizza(String style, String type) { Pizza pizza = null; if (style.equals(Brest)) { if (type.equals(fromage)) { pizza = new PizzaFromageStyleBrest(); } else if (type.equals(vegetarienne)) { Gre toutes les pizzas pizza = new PizzaVegetarienneStyleBrest(); de style Brest } else if (type.equals(fruitsDeMer)) { pizza = new PizzaFruitsDeMerStyleBrest(); } else if (type.equals(poivrons)) { pizza = new PizzaPoivronsStyleBrest(); } } else if (style.equals(Strasbourg)) { if (type.equals(fromage)) { pizza = new PizzaFromageStyleStrasbourg(); Gre toutes les } else if (type.equals(vegetarienne)) { pizzas de style pizza = new PizzaVegetarienneStyleStrasbourg(); Strasbourg } else if (type.equals(fruitsDeMer)) { pizza = new PizzaFruitsDeMerStyleStrasbourg(); } else if (type.equals(poivrons)) { pizza = new PizzaPoivronsStyleStrasbourg(); } } else { System.out.println(Erreur: type de pizza invalide); return null; } pizza.preparer(); pizza.cuire(); pizza.couper(); pizza.emballer(); return pizza; } }

Vous pouvez inscrire vos rponses ici:

nombre

nombre avec Mar

seille
137

vous tes ici

dpendances des objets

Problmes de dpendance des objets


Quand vous pour directement un objet, vous dpendez de sa classe concrte. Jetez un coup dil notre Pizzeria hyper dpendante de la page prcdente. Elle cre tous les objets pizza en plein dans la classe Pizzeria au lieu de dlguer une fabrication. Si nous traons un diagramme reprsentant cette version de Pizzeria et de tous les objets dont elle dpend, voici quoi il ressemble:

Si limplmentati classes change, noon de ces modifier Pizzer us devrons ia.

Cette version de Pizzeria dpend de tous ces objets pizza parce quelle les cre directement.

Comme toute modification des implmentations concrtes des pizzas affecte Pizzeria, nous disons que Pizzeria dpend des implmentations des pizzas.

St y r e l eB

rasbo

rest

rS tyleBr

yle B

ivr onsSt

Chaque nouvelle sorte de pizza que nous ajoutons cre une dpendance de plus pour Pizzeria.

aF ruitsD

ro mageS

138

Chapitre 4

ty leStr asbou

eM e

rg

sD eMerSt

yl eS t

est

aV n egetar

ie

tr

iv r

on

sS tyleS

as bourg

ur

Pi

st

ro mageS

eg e t a ri e

tyl e

nne StyleSt

Brest

ra sbourg
V za Piz

izzer a i

F za Piz

Pizza o P

Pi z

zz

aFrui

z Piz
o aP

z Piz

aF izz

les patterns fabriques

Le principe dinversion des dpendances


Une notion doit tre bien claire: la rduction des dpendances aux classes concrtes dans notre code est une bonne chose. En fait, nous avons un principe de conception OO qui formalise cette notion. Elle porte mme un beau nom bien ronflant: Principe dinversion des dpendances. Voici sa dfinition gnrale:

Principe de conception
Dpendez dabstractions. Ne dpendez pas de classes concrtes.

ue expression q Encore une employer pour vous pouvez r les cadres! impressionne de votre Le montant n dpassera augmentatio e que vous aura largement c re, et vous vous cot ce liv admiration de attirerez l dveloppeurs. vos collgues

premire vue, ce principe ressemble beaucoup Programmez pour une interface, non pour une implmentation. Mais sil est similaire, le Principe dinversion des dpendances affirme quelque chose de beaucoup plus fort propos de labstraction. Il suggre que nos composants de haut niveau ne doivent pas dpendre des composants de bas niveau, mais que les deux doivent dpendre dabstractions. Mais quest-ce que cela peut bien vouloir dire? Eh bien commenons par observer de nouveau le diagramme de la page prcdente. Pizzeria est notre composant de haut niveau, les implmentations de pizzas sont nos composants de bas niveau et il est clair que Pizzeria dpend des classes de pizza concrtes. Maintenant, ce principe nous dit que nous devons plutt crire notre code afin de dpendre dabstractions, non de classes concrtes. Cela vaut la fois pour nos modules de haut niveau et pour nos modules de bas niveau. Mais comment procder? Rflchissons comment nous appliquerions ce principe notre implmentation de Pizzeria hyper dpendante...

Un composant de haut niveau est une classe dont le comportement est dfini en termes dautres composants de bas niveau. Par exemple, Pizzeria est un composant de haut niveau parce que son comportement est dfini en termes de pizzas il cre tous les objets pizza diffrents, les prpare, les fait cuire, les dcoupe et les emballe, tandis que les pizzas quil utilise sont les composants de bas niveau.

vous tes ici

139

le principe dinversion des dpendances

Appliquer le principe
Maintenant, le principal problme de la Pizzeria hyper dpendante est quelle dpend de chaque type de pizza parce quelle instancie les types concrets dans sa mthode commanderPizza(). Si nous avons bien cr une abstraction, Pizza, nous crons nanmoins les pizzas concrtes dans ce code, ce qui nous empche dexploiter rellement les avantages de cette abstraction. Comment extraire ces instanciations de la mthode commanderPizza()? Eh bien, comme nous le savons, cest exactement ce que le pattern Fabrication nous permet de faire. Ainsi, aprs avoir appliqu Fabrication, nous obtenons le diagramme suivant:

Piz

z er i a

Pizza est une classe abstraite... une abstract ion

s Pizzeria ne dpend plu a, la zz Pi de e qu maintenant classe abstraite. rtes dpendent Les classes Pizza conc tion Pizza, parce galement de labstrac linterface Pizza quelles implmentent us employons (noublions pas que no ns gnral) dans la interface au se a. classe abstraite Pizz

.
Pizza

St yleBrest

nne re StyleB

omage

rest

eg giePizza

tyl u eS trasbo

tyleStr

oS tyleV

b as

e ru itsDe

Aprs avoir appliqu Fabrication, vous remarquerez que notre composant de haut niveau, Pizzeria, et nos composants de bas niveau, les pizzas, dpendent tous de Pizza, labstraction. Fabrication nest pas la seule technique qui permet de respecter le Principe dinversion des dpendances, mais cest lune des plus puissantes.

140

Chapitre 4

ro mage S

rg

ivr onsSt

aV egetar

vr onsStyl

eS tras bourg

yle B

ie

st

aF r

aF r

uitsDe

Me

rSt leBr y

es t

z Piz

z Piz

z Piz
o aP

z Piz

z Piz

o ur g

g ica Ch

aP

oi

F za Piz

F za Piz

les patterns fabriques

OK. Jai bien compris cette histoire de dpendances, mais pourquoi parle-t-on dinversion?

O est l inversion dans le Principe dinversion des dpendances? Dans lexpression principe dinversion des dpendances, le mot inversion indique quon inverse la faon dont vous pourriez penser votre conception OO. Regardez le diagramme de la page prcdente et remarquez que les composants de bas niveau dpendent maintenant dune abstraction de plus haut niveau. De plus, le composant de haut niveau est galement li la mme abstraction. Ainsi, le graphe de dpendances que nous avons trac il y a deux pages sest invers, et les deux types de modules dpendent maintenant de labstraction. Voyons galement quelle est la pense qui sous-tend le processus de conception classique et comment lintroduction de ce principe peut inverser notre faon denvisager la conception...

vous tes ici

141

inverser votre pense

Inverser votre pense...


Mmmm, Pizzeria prpare, cuit et emballe des pizzas. Ma boutique doit donc prparer plusieurs pizzas diffrentes: au fromage, aux poivrons, aux fruits de mer, etc...

Bien. Ainsi, vous devez implmenter une Pizzeria. Quelle est la premire pense qui vous vient lesprit?

Bien, une PizzaFromage une PizzaPoivrons et une PizzaFruitsDeMer ne sont jamais que des Pizzas. Elles doivent donc partager une interface Pizza.

OK. Vous commencez par le sommet et vous descendez jusquaux classes concrtes. Mais, comme vous lavez vu, vous ne voulez pas que votre boutique ait connaissance des types concrets de pizzas, parce quelle dpendrait alors de ces classes concrtes! Maintenant, inversez votre pense... Au lieu de commencer par en haut, commencez par les Pizzas et rflchissez ce que vous pouvez abstraire.

Trs bien! Vous pensez labstraction Pizza. Maintenant, revenez en arrire et rflchissez de nouveau la conception de Pizzeria.
Maintenant que jai une Pizza abstraite, je peux concevoir ma Pizzeria sans me soucier des classes pizzas concrtes.

Vous brlez. Mais, pour ce faire, vous devez vous appuyer sur une fabrication pour extraire ces classes concrtes de votre Pizzeria. Cela fait, vos diffrents types concrets ne dpendront que dune abstraction et votre boutique galement. Nous avons pris une conception dans laquelle la boutique dpendait de classes concrtes et nous avons invers ces dpendances (ainsi que votre mode de pense). 142
Chapitre 4

les patterns fabriques

Quelques conseils pour vous aider appliquer le principe...


Les lignes directrices suivantes peuvent vous aider viter les conceptions OO qui enfreignent le Principe dinversion des dpendances: Aucune variable ne doit contenir une rfrence une classe concrte.

Aucune classe ne doit driver dune classe concrte.

teur new, Si vous utilisez lopra une classe vous faites rfrencee fabrication concrte. Utilisez un oblme! pour contourner le pr Si vous sous-classez une classe concrte, vous dpendez de cette classe concrte. Sous-classez une abstraction, comme une interface ou une classe abstraite.
Si vous redfinissez implmente, votre une mthode ntait pas une abstclasse de base commencer. Ces m raction pour dans la classe de bathodes implmentes tre partages par se sont connues pour toutes les sousclasses.

Aucune classe ne doit redfinir une mthode

implmente dans une classe de base.

Mais, attendez Ces conseils sont impossibles suivre. Si je les applique tous, je ncrirai jamais un seul programme!

Vous avez entirement raison! Comme pour beaucoup de nos principes, il sagit de lignes directrices vers lesquelles vous devez tendre et non de rgles que vous devez appliquer tout le temps. De toute vidence, tout programme Java jamais crit viole au moins lun de ces principes! Mais si vous intriorisez ces conseils et que vous les avez toujours dans un coin de votre esprit quand vous concevez une application, vous saurez que vous enfreignez le principe et vous aurez une bonne raison de le faire. Par exemple, si vous avez une classe qui nest pas susceptible de changer et que vous le savez, ce ne sera pas la fin du monde si vous instanciez une classe concrte. Rflchissez: nous instancions des objets String en permanence sans y regarder deux fois. Est-ce que nous violons le principe? Oui. Est-ce que cest mal? Non. Pourquoi? Parce quil est trs improbable que la classe String change. Si en revanche une classe que vous avez crite est susceptible de changer, Vous disposez de techniques intressantes, telle Fabrication, pour encapsuler le changement.
vous tes ici

143

familles de garnitures

Pendant ce temps, de retour la Pizzeria...


La conception de la Pizzeria prend rellement forme: nous disposons dune structure souple et nous respectons de bons principes de conception. Maintenant, la cl du succs de votre boutique dObjectville a toujours rsid dans le fait quelle employait des garnitures fraches et de qualit. Or, avec la nouvelle structure, vous avez dcouvert que si vos franchises appliquaient bien vos procdures, certaines dentre elles substituaient des garnitures de qualit infrieure pour diminuer les cots et augmenter leurs marges. Vous savez que vous devez faire quelque chose, sous peine de voir endommager long terme la rputation de la marque dObjectville!

Pte
Poivrons

Assurer la cohrence de vos garnitures


Comment allez-vous donc veiller ce que chaque franchise utilise des garnitures de qualit? Vous allez construire une fabrique qui les produit et qui les leur expdie! Mais ce plan prose un problme: les franchises sont situes dans diffrentes rgions et ce qui est une sauce tomate Brest nest pas une sauce tomate Strasbourg. Ainsi, vous avez un ensemble de garnitures qui doit tre livr Brest et un ensemble diffrent qui est destin Strasbourg. Voyons cela de plus prs:

Fromage

Lgumes

Sauce

Carte de pizzas de Strasbourg


Pizza fromage Sauce aux tomates cerise, Mozzarella, Parmesan, Origan Pizza vgtarienne
Parmesan, Sauce aux tomates cerise, Mozzarella, Aubergines, pinards, Olives noires Parmesan, Sauce aux tomates cerise, Mozzarella, Moules Parmesan, Sauce aux tomates cerise, Mozzarella, Poivrons Aubergines, pinards, Olives noires,

Nous avons les mmes familles de produits (pte, sauce, fromage, lgumes, etc) mais des implmentations diffrentes selon la rgion.

Carte de pizzas de Brest


Pizza fromage
Sauce Marinara, Reggiano, Ail

Pizza vgtarienne

Sauce Marinara, Reggiano, Champignons, Oignons, Poivrons rouges

Pizza fruits de mer

Pizza fruits de mer Pizza poivrons

Sauce Marinara, Reggiano, Moules fraches

Pizza poivrons

Sauce Marinara, Reggiano, Champignons, Oignons, Poivrons rouges, Poivrons verts

144

Chapitre 4

les patterns fabriques

Familles de garnitures...
Brest utilise un ensemble de garnitures et Strasbourg un autre. tant donne la popularit de la boutique dObjectville, vous devrez probablement envoyer dici peu un autre ensemble de garnitures rgionales Marseille. Et aprs? Lille, peut-tre? Pour ce faire, vous allez devoir imaginer comment grer les familles de garnitures.

Strasbourg
MoulesSurgelees

SauceTomatesCerise

PateSoufflee

Mozzarella

Brest
MoulesFraiches

sont fabriques on a e ill tv ec bj O d as gi Toutes les pizz es composants, mais chaque r nts. sa m po m m co es d s partir diffrente de ce on ti ta en m pl une im
PateFine

SauceMarinara

Marseille
Reggiano
Calamars

SauceBruschetta

PateTresFine

Chaque famille se compose dun type de pte, un type de sauce, un type de fromage et une garniture de fruits de mer (plus quelques autres que nous navons pas reprsents comme les lgumes et les pices).

FromageDeChevre

, familles dingrdients s de t en tu ti ns co ns gio es. Au total, ces trois r ntant une famille complte de garnitur me pl im n chaque rgio
vous tes ici

145

fabriques dingrdients

Construire les fabriques dingrdients


Nous allons maintenant construire une fabrication pour crer nos garnitures. La fabrication sera responsable de la cration de chaque ingrdient de la famille. Autrement dit, la fabrication devra crer de la pte, de la sauce, du fromage, etc... Vous verrez bientt comment nous allons grer les diffrences rgionales. Mais commenons par dfinir une interface pour la fabrication qui va crer toutes nos garnitures :

public interface FabriqueIngredientsPizza { } public public public public public public Pate creerPate (); Sauce creerSauce(); Fromage creerFromage(); Legumes[] creerLegumes(); Poivrons creerPoivrons(); Moules creerMoules();

grdient, nous Pour chaque ine mthode de cration dfinissons un terface. dans notre in

Plein de nouvelles classes ici, une par ingrdient.

Si nous avions un mcanisme commun implmenter dans chaque ions instance de fabrication, nous aur pu crer la place une classe abstraite...

Voici ce que nous allons faire:


1

Construire une fabrique pour chaque rgion. Pour ce faire, vous allez crer une sousclasse de FabriqueIngredientsPizza qui implmente chaque mthode de cration. Implmenter un ensemble de classes ingrdients qui seront utilises avec la fabrique, comme Reggiano, PoivronsRouges et PateSoufflee. Ces classes peuvent tre partages entre les rgions en fonction des besoins. Puis nous assemblerons tout en incorporant nos fabriques dans lancien code de Pizzeria.

146

Chapitre 4

les patterns fabriques

Construire la fabrique dingrdients de Brest


Bien, voici limplmentation de la fabrique dingrdients de Brest. Celleci est spcialise dans la sauce Marinara, le parmigiano reggiano, les moules fraches...

st La fabrique dingrdients de Bre les implmente linterface pour toutes fabrications dingrdients

public class FabriqueIngredientsPizzaBrest implements FabriqueIngredientsPizza { public Pate creerPate() { return new PateFine(); } public Sauce creerSauce() { return new SauceMarinara(); } public Fromage creerFromage() { return new Reggiano(); } public Legume[] creerLegumes() { Legume legume [] = { new Ail(), new Oignon(), new Champignon(), new PoivronRouge() }; return legume; Pour les lgumes, nous retournons un } public Poivrons creerPoivrons() { return new PoivronsEnRondelles(); } public Moules creerMoules() { return new MoulesFraiches(); } }

dient de la Pour chaque ingr ons la version famille, nous cr Brest.

tableau de Lgumes. Ici, nous avons cod les lgumes en dur. Nous aurions pu trouver quelque chose de plus labor, mais comme cela najouterait rien lexplication du pattern, nous simplifions.

Comme Brest est sur la cte, nous avons des moules fraches. Strasbourg devra se contenter de moules surgeles.

Les meilleurs poivrons en rondelles. Ils sont partags entre Brest et Strasbourg. Noubliez pas den utiliser page suivante quand vous implmenterez vous-mme la fabrique de Strasbourg

vous tes ici

147

construire une fabrique

vos crayons Sharpen your pencil

crivez la Fabrique IngredientsPizzaStrasbourg. Vous pouvez rfrencer les classes ci-dessous dans votre implmentation:

Aubergine
Epinards OlivesNoires
PateSoufflee

PoivronsEnRondelles
SauceTomateCerise MoulesSurgelees
Mozzarella

148

Chapitre 4

les patterns fabriques

Retravaillons les pizzas...


Nos fabriques sont en place et prtes produire des ingrdients de qualit. Il ne nous reste plus qu retravailler nos Pizzas pour quelles nemploient que les ingrdients produits par les fabriques. Nous allons commencer par notre classe abstraite, Pizza::

public abstract class Pizza { String nom; Pate pate; Sauce sauce; Legume legume[]; Fromage fromage; Poivrons poivrons; Moules moules; abstract void preparer();

Chaque pizza contient un ensemble dingrdients utiliss dans sa prparation.

Nous avons maintenant rendu la mthode preparer() abstraite. Cest l que nous allons collecter les ingrdients ncessaires pour la pizza. Bien entendu, ils proviennent de la fabrication dingrdients.

void cuire() { System.out.println(Cuisson 25 minutes 180); }

void couper() { System.out.println(Dcoupage en parts triangulaires); } void emballer() { System.out.println(Emballage dans une bote officielle); } void setNom(String nom) { this.nom = nom; } String getNom() { return nom; } public String toString() { // code qui affiche la pizza } }

: demeurent les mmes Nos autres mthodes arer() a chang. seule la mthode prep

vous tes ici

149

dcoupler les ingrdients

Retravaillons les pizzas (suite)


Maintenant que nous disposons dune Pizza abstraite comme base, il est temps de crer les pizzas de stylesBrest et Strasbourg seulement cette fois les ingrdients viendront directement de la fabrication. Cest la fin de lpoque laquelle les franchiss lsinaient sur les ingrdients! Quand nous avons crit le code inspir de Fabrication, nous avions une classe PizzaFromageBrest et une classe PizzaFromageStrasbourg. Si vous observez ces deux classes, vous constatez quune seule chose diffre: lemploi dingrdients rgionaux. Les pizzas sont faites exactement de la mme manire (pte + sauce + fromage). Il en va de mme pour les autres pizzas: Vgtarienne, Fruits de mer, etc. Les tapes de la rparation sont les mmes, mais les garnitures sont diffrentes. Vous voyez donc que nous navons pas besoin de deux classes pour chaque pizza: la fabrique dingrdients va grer les diffrences rgionales pour nous. Voici la Pizza Fromage:

public }

class PizzaFromage extends Pizza { FabriqueIngredientsPizza fabriqueIngredients;

ire une Maintenant, pour fasoin dune be s pizza, nous avon nit les fabrication qui four ication ingrdients. Une fabr est donc transmise auue classe constructeur de chaq ns une Pizza et stocke da e. nc variable dinsta

public PizzaFromage(FabriqueIngredientsPizza fabriqueIngredients) { this.fabriqueIngredients = fabriqueIngredients; } void preparer() { System.out.println(Prparation de + nom); pate = fabriqueIngredients.creerPate(); sauce = fabriqueIngredients.creerSauce(); fromage = fabriqueIngredients.creerFromage(); }

Cest l que cest magique!

La mthode preparer() parcourt les tapes que de la cration dune pizza au fromage. Cha fois quelle a besoin dun ingrdient, elle demande la fabrication de le produire.

150

Chapitre 4

les patterns fabriques

Code la loupe
Le code de Pizza code utilise la fabrication avec laquelle il a t compos pour produire les ingrdients employs dans la pizza. Les ingrdients produits dpendent de la fabrication spcifique. La classe ne sen soucie pas: elle sait comment faire des pizzas. Elle est maintenant dcouple des diffrences rgionales et sera facile rutiliser quand il y aura des fabrications pour les Montagnes rocheuses, la Floride et au del.

sauce = fabriqueIngredients.creerSauce();

Nous affectons variable dinstance la Pizza une rfrenc de la sauce spcifiq e utilise dans cetteue pizza.

Voici notre fabrique dingrdients. La Pizza se moque de la fabrique utilise tant que cest une fabrique dingrdients.

() retourne la sauce n tio La mthode creerSauce n. Si cest une fabrica employe dans sa rgio us aurons une sauce dingrdients Brest, no creerSauce() retourne marinara. La mthode sa rgion. Si cest une la sauce employe dans ts Brest, nous aurons une fabrication dingrdien sauce marinara.

Vrifions galement le code de PizzaFruitsDeMer:

public class PizzaFruitsDeMer extends Pizza { FabriqueIngredientsPizza fabriqueIngredients; public PizzaFruitsDeMer(FabriqueIngredientsPizza fabriqueIngredients) { this.fabriqueIngredients = fabriqueIngredients; PizzaFruitsDeMer cache } rique void preparer() { System.out.println(Prparation de + nom); pate = fabriqueIngredients.creerPate(); sauce = fabriqueIngredients.creerSauce(); fromage = fabriqueIngredients.creerFromage(); fruitsDeMer = fabriqueIngredients.creerMoules(); }

galement une fab dingrdients.

Pour faire une pizza aux fruits de mer, la mthode preparer() se procure les bons ingrdients auprs de la fabrique locale.

Si cest la fabrique de Brest, ce seront des moules fraches; si cest celle de Strasbourg, elles seront surgeles.
vous tes ici

151

utiliser la bonne fabrication dingrdients

Revisitons nos pizzerias


Nous y sommes presque. Il suffit daller faire un tour rapide chez nos franchiss pour vrifier quils utilisent les bonnes Pizzas. Il faut galement leur donner une rfrence leur fabrique dingrdients locale:

public class PizzeriaBrest extends Pizzeria { protected Pizza creerPizza(String item) { Pizza pizza = null; FabriqueIngredientsPizza fabriqueIngredients = new FabriqueIngredientsPizzaBrest(); if (choix.equals(fromage)) { pizza = new PizzaFromage(fabriqueIngredients); pizza.setNom(Pizza au fromage style Brest); } else if (choix.equals(vegetarienne)) { pizza = new PizzaVegetarienne(fabriqueIngredients); pizza.setNom(Pizza vgtarienne style Brest); } else if (choix.equals(fruitsDeMer)) { pizza = new PizzaFruitsDeMer(fabriqueIngredients); pizza.setNom(Pizza aux fruits de mer style Brest); } else if (choix.equals(poivrons)) { pizza = new PizzaPoivrons(fabriqueIngredients); pizza.setNom(Pizza aux poivrons style Brest); } return pizza; } }

pose rest est com B e d est. e u iq t t La bou brique dingrdien s Br avec une fa ilise pour produire les yle Elle sera ut e toutes les pizzas st garnitures d Brest.
Nous transmettons maintenant chaque pizza la fabrique quil faut utiliser pour produire ses ingrdients. Revenez page prcdente et vrifiez que vous avez compris comment la pizza et la fabrique collaborent!

Pour chaque type de Pizza, nous instancions une nouvelle Pizza et nous lui transmettons la fabrique dont elle a besoin pour obtenir ses ingrdients.

A
Chapitre 4

Musclez vos neurones

Comparez cette version de la mthode creerPizza() avec celle de limplmentation de Fabrication que nous avons vue plus haut dans ce chapitre.

152

les patterns fabriques

Quavons-nous fait?
Nous avons apport toute une srie de modifications au code. Mais quavons-nous fait exactement? Nous avons fourni un moyen de crer une famille dingrdients pour les pizzas en introduisant un nouveau type de fabrication nomm Fabrique Abstraite. Une Fabrique Abstraite fournit une interface pour crer des familles dobjets. En crivant du code qui utilise cette interface, nous dcouplons ce code de la fabrique relle qui cre les produits. Cela nous permet dimplmenter toute une gamme de fabriques qui crent des produits destins diffrents contextes diffrentes rgions, diffrents systmes dexploitation ou diffrents look and feel. Comme notre code est dcoupl des produits rels, nous pouvons substituer diffrentes fabriques pour obtenir diffrents comportements (par exemple obtenir une sauce marinara au lieu dune sauce aux tomates cerise).
Une Fabrique Abstraite fournit une interface pour une famille de produits. Mais quest-ce quune famille? Dans notre cas, ce sont tous les lments ncessaires pour confectionner une pizza: pte, sauce, fromage et autres garnitures.

Dfinit linterface.

Fabrique abstraite dingrdients dObjectville


Fournit les ions implmentats. des produit

Brest

Strasbourg
partir de la fabrique abstraite, nous drivons une ou plusieurs fabriques concrtes qui produisent les mmes produits, mais avec des implmentations diffrentes.

Piz zeria
Nous crivons ensuite notre code pour quil utilise la fabrique pour crer des produits. En transmettant diffrentes fabriques, nous obtenons diffrentes implmentations de ces produits. Mais notre code client demeure identique.

ctionne Pizza confe rdients avec des ingr une produits pa ncrte. fabrique co

vous tes ici

153

commandons encore quelques pizzas

Luc et Michel ont encore envie de pizzas...


Luc et Michel ne sont jamais rassasis des pizzas dObject ville! Mais ce quils ignorent, cest lexistence des nouvelles fabriques dingrdients. Maintenant, quand ils passent commande...
Jadore toujours le style Brest

Dans les coulisses

Je men tiens Strasbourg

La premire partie du processus de commande na pas chang dun iota. Revoyons la commande de Luc:

Tout dabord, il nous faut une PizzeriaBrest:


Pizzeria pizzeriaBrest = new PizzeriaBrest();

Cre une instance de PizzeriaBrest.

Maintenant que nous avons une boutique, nous pouvons prendre une commande:
pizzeriaBrest.commanderPizza(fromage);

zz eriaBres

izza() est La mthode commanderPpizzeriaBrest. appele sur linstance de

La mthode commanderPizza() commence par appeler la mthode creerPizza():


Pizza pizza = creerPizza(fromage);

age) za(from creerPiz

154

Chapitre 4

pi

les patterns fabriques

partir de l, les choses changent, parce que nous utilisons une fabrique dingrdients

Dans les coulisses

Quand la mthode creerPizza() est appele, cest l que notre fabrique dingrdients entre en scne:

ents est choisie et La fabrique dingrdi eria puis transmise au instancie dans Pizz ue pizza. constructeur de chaq
ien tsBrest
iqueDIngr
preparer()

Pizza pizza = new PizzaFromage(maFabriqueDIngredients);

Cre une instance de Pizza qui est compose avec la fabrique dingrdients de Brest.
Pizza
Nous devons ensuite prparer la pizza. Une fois la mthode preparer()appele, on demande la fabrique de prparer les ingrdients:
void preparer() { pate = fabrication.creerPate(); sauce = fabrication.creerSauce(); fromage = fabrication.creerFromage(); }

ent conti

ab r

ine Pte f Marinara


Reggiano

us utilisons la Pour la pizza de Luc, no Brest. Nous fabrique dingrdients de nts de Brest. obtenons donc les ingrdie

Enfin, nous avons la pizza prpare en main et la mthode commanderPizza() la fait cuire, la dcoupe et lemballe.
vous tes ici

155

fabrique abstraite: dfinition

Le pattern Fabrique Abstraite: dfinition


Nous allons ajouter un nouveau pattern de fabrication notre famille de patterns. Celui-ci nous permet de crer des familles de produits. Voyons sa dfinition officielle:
Le pattern Fabrique Abstraite fournit une interface pour crer des familles dobjets apparents ou dpendants sans avoir spcifier leurs classes concrtes..

Nous avons vu que Fabrique Abstraite permet un client dutiliser une interface abstraite pour crer un ensemble de produits apparents sans connatre les produits concrets qui sont rellement crs (ni mme sen soucier). Ainsi, le client est dcoupl de tous les dtails des produits concrets. Observons le diagramme de classes pour voir la structure du pattern:

La FabriqueAbstraite dfinit linterface que toute les fabriques concrtes doivent implmenter: elle consiste en un ensemble de mthodes pour crer des produits.
<<interface>> FabriqueAbstraite
CreerProduitA() CreerProduitB()

Le Client est crit pour utiliser la fabrique abstraite puis compos au moment de lexcution avec une fabrique relle.
Client

Voici la famille de produits. Chaque fabrique concrte peut crer tout un ensemble de <<interface>> produits. ProduitAbstraitA

ProduitA2

ProduitA1

FabriqueConcrete1
CreerProduitA() CreerProduitB()

FabriqueConcrete2
CreerProduitA() CreerProduitB()

<<interface>> ProduitAbstraitB

Les fabriques concrtes implmentent les diffrentes familles de produits. Pour crer un produit, le client utilise lune de ces fabriques. Elle na donc jamais besoin dinstancier un objet produit.

ProduitB2

ProduitB1

156

Chapitre 4

les patterns fabriques

Voil un diagramme de classes relativement compliqu. Rapportons-le notre Pizzeria:

Les clients de la Fabrique Abstraite sont les deux instances de notre Pizzeria, PizzeriaBrest et PizzeriaStrasbourg.
PizzeriaBrest
creerPizza()

Linterface abstraite FabriqueIngredientsPizza est linterface qui dfinit la faon de crer une famille de produits apparents tout ce quil nous faut pour confectionner une pizza.
PateSoufflee

<<interface>> Pate

PateFine

<<interface>> FabriqueIngredientsPizza
creerPate() creerSauce() creerFromage() creerLegumes() creerPoivrons() creerMoules()

<<interface>> Sauce

SauceTomatesCerise

SauceMarinara

FabriqueIngredientsPizzaBrest
creerPate() creerSauce() creerFromage() creerLegumes() creerPoivrons() creerMoules()

FabriqueIngredientsPizzaStrasbourg
creerPate() creerSauce() creerFromage() creerLegumes() creerPoivrons() creerMoules()

<<interface>> Fromage

Mozzarella

ParmigianoReggiano

<<interface>> Moules

La tche des fabriques concrtes consiste fabriquer les ingrdients des pizzas. Chaque fabrique sait comment crer les objets qui correspondent sa rgion.

MoulesSurgelees

MoulesFraiches

Chaque fabrique produit une implmentation diffrente de la famille de produits.


vous tes ici

157

interview avec les patterns de fabrication

Jai remarqu que chaque mthode de la Fabrique Abstraite ressemblait en fait une Fabrication (creerPate(), creerSauce(), etc.). Chaque mthode est dclare abstraite et les sous-classes la redfinissent pour crer un objet. Est-ce que ce nest pas une Fabrication?

Y a-t-il une Fabrication tapie derrire la Fabrique Abstraite?


Bien vu! Oui, on implmente souvent les mthodes dune Fabrique Abstraite comme des Fabrications. Cest logique, non? La Fabrique Abstraite a pour tche de dfinir une interface permettant de crer une famille de produits. Chaque mthode de cette interface a la responsabilit de crer un produit concret, et nous implmentons une sous-classe de la Fabrique Abstraite pour fournir ces implmentations. Cest pourquoi les Fabrications constituent une faon naturelle dimplmenter vos mthodes de cration des produits dans vos fabriques abstraites.

Interview
Linterview de cette semaine:

Fabrication et Fabrique Abstraite


DPTLP : Oaouh, un interview avec deux patterns la fois! Cest une premire pour nous. Fabrication : Ouais, je crois que je naime pas tellement tre regroup avec Fabrique Abstraite, vous savez. Ce nest pas parce que nous sommes tous les deux des patterns de fabrication que je nai pas droit mes propres interviews. DPTLP : Ne vous fchez pas. Nous voulions vous interviewer ensemble pour que nos lecteurs sachent qui est qui sans doute possible. Vous prsentez des similitudes certaines et jai entendu dire que les gens vous confondent parfois. Fabrique Abstraite : Cest vrai, il est arriv quon me prenne pour Fabrication, et je sais que vous avez eu des problmes semblables, Fabrication. Nous excellons tous les deux dcoupler les applications des implmentations spcifiques, mais nous le faisons diffremment. Donc je vois bien pourquoi les gens nous confondent parfois. Fabrication : Eh bien, cela continue mabasourdir. Aprs tout, jutilise des classes pour crer et vous utilisez des objets. Cest totalement diffrent! 158
Chapitre 4

les patterns fabriques

DPTLP : Pouvez-vous nous en dire plus, Fabrication? Fabrication : Bien sr. Fabrique Abstraite et moi crons tous les deux des objets cest notre travail. Mais moi, jutilise lhritage... Fabrique Abstraite : ...et moi la composition. Fabrication : Exactement. Cela veut dire que pour crer des objets en utilisant une Fabrication, vous devez tendre une classe et redfinir une mthode de fabrication. DPTLP : Et que fait cette mthode de fabrication? Fabrication : Elle cre des objets bien sr! Je veux dire que tout lintrt du Pattern Fabrication rside dans le fait que vous utilisez une sous-classe pour effectuer la cration votre place. De cette faon, les clients nont besoin de connatre que le type abstrait quils utilisent, et la sousclasse soccupe du type concret. Autrement dit, je permets de dcouper les clients des types concrets. Fabrique Abstraite : Et moi aussi, seulement je procde autrement. DPTLP : Continuez, Fabrique Abstraite... Vous avez dit quelque chose propos de composition. Fabrique Abstraite : Je fournis un type abstrait pour crer une famille de produits. Les sous-classes de ce type dfinissent le mode de cration des produits. Pour utiliser la fabrique, vous en instanciez une et vous lui transmettez du code qui est crit selon le type abstrait. Ainsi, comme pour Fabrication, mes clients sont dcoupls des produits concrets quils utilisent. DPTLP : Oh, Je vois. Un autre avantage est donc que vous regroupez des produits apparents. Fabrique Abstraite : Cest cela. DPTLP : Que se passe-t-il si vous devez tendre cet ensemble de produits apparents, par exemple pour en ajouter un? Est-ce que cela ne vous oblige pas modifier votre interface? Fabrique Abstraite : Cest vrai; mon interface doit changer quand on ajoute de nouveaux produits, et je sais que les gens naiment pas a.... Fabrication : <ricanement> Fabrique Abstraite : Quest-ce qui vous fait ricaner, Fabrication?

Fabrication : Oh, allons, cest toute une affaire! Modifier votre interface signifie que vous devez modifier linterface de chaque sous-classe! Cela fait quand mme beaucoup de travail. Fabrique Abstraite : Oui, mais jai besoin dune grosse interface parce que jai lhabitude de crer des famille entires de produits. Vous qui ne crez quun seul produit, vous navez pas vraiment besoin dune grosse interface. Une seule mthode vous suffit. DPTLP : Fabrique Abstraite, Jai entendu dire que vous utilisiez des fabrications pour implmenter vos fabriques concrtes Fabrique Abstraite :Oui, je ladmets, mes fabriques concrtes implmentent une Fabrication pour crer leurs produits. Dans mon cas, elles sont utilises uniquement pour crer des produits... Fabrication : ...tandis que dans le mien, jimplmente gnralement le code dans le crateur abstrait qui utilise les types concrets que les sous-classes crent. DPTLP : On dirait que vous tes vraiment excellents dans ce que vous faites. Je suis sr que les gens aiment avoir le choix. Aprs tout, les fabriques sont si utiles quils voudront les utiliser dans toutes sortes de situations diffrentes. Vous encapsulez tous les deux la cration des objets pour que les applications demeurent faiblement couples et moins dpendantes des implmentations, ce qui est rellement formidable, que vous utilisiez une Fabrication ou une Fabrique Abstraite. Puis-je vous demander chacun un mot dadieu? Fabrique Abstraite : Merci. Souvenez-vous de moi, Fabrique Abstraite, et utilisez moi chaque fois que vous avez besoin de crer des familles de produits et que vous voulez garantir que vos clients crent des produits qui vont ensemble. Fabrication : Et je suis Fabrication; utilisez-moi pour dcoupler votre code client des classes concrtes que vous devez instancier, ou si vous ne connaissez pas davance toutes les classes concrtes dont vous allez avoir besoin. Pour mutiliser, il suffit de me sous-classer et dimplmenter ma mthode de fabrication!

vous tes ici

159

comparaison des patterns

Fabrication et Fabrique Abstraite compars


Fournit une interface abstraite pour crer UN produit.
oisit quelle Chaque sous-classe chncier. classe concrte insta
Pizzeria est implmente sous forme de Fabrication parce que nous voulons pouvoir fabriquer un produit qui varie selon la rgion. Avec une Fabrication, chaque rgion a sa propre fabrique ner concrte qui sait comment confection des pizzas appropries au lieu.

Pizzeria
creerPizza()

Boutique de Brest La Fabrication

PizzeriaBrest
creerPizza()

PizzeriaStrasbourg
creerPizza()

La Fabrication

Boutique de Strasbourg

La sous-classe PizzeriaBrest ninstancie que les pizzas style Brest.

Voici le produit de la Pizzeria. Les clients ne sappuient que sur ce type abstrait.

La sous-classe PizzeriaStrasbourg ninstancie que les pizzas style Strasbourg.

Pizza

PizzaFromageStyleBrest PizzaPoivronsStyleBrest

PizzaFruitsDeMerStyleBrest

Les sous-classes sont instancies par les Fabrications. Brest

PizzaFromageStyleStrasbourg PizzaPoivronsStyleStrasbourg

PizzaFruitsDeMerStyleStrasbourg PizzaVegetarienneStyleStrasbourg

PizzaVegetarienneStyleBrest

Strasbourg

La mthode creerPizza() tant paramtre par le type de pizza, vous pouvez retourner plusieurs types de produits pizza..
160
Chapitre 4

les patterns fabriques

<<interface>> FabriqueIngredientsPizza

Fournit une interface e abstraite pour crer un famille de produits

creerPate() creerSauce() creerFromage() creerLegumes() creerPoivrons() creerMoules()

sous zza est implmente FabriqueIngredientsPi straite parce que nous forme de Fabrique Abdes familles de produits (les avons besoin de crer sous-classe implmente les ingrdients). Chaque t ses propres fournisseurs ingrdients en utilisan rgionaux.
Chaque sous-classe concrte cre une famille de produits

Brest

FabriqueIngredientsPizzaBrest
creerPate() creerSauce() creerFromage() creerLegumes() creerPoivrons() creerMoules()

FabriqueIngredientsPizzaStrasbourg
creerPate() creerSauce() creerFromage() creerLegumes() creerPoivrons() creerMoules()

Strasbourg Dans une Fabrique Abstraite, les mthodes pour crer les produits sont souvent implmentes laide dune Fabrication...

...par exemple, la sous-classe choisit le type de pte...


<<interface>> Pate

... ou le type de fruitsDeMers.

<<interface>> Moules

PateSoufflee

PateFine

MoulesSurgelees

MoulesFraiches

<<interface>> Sauce

SauceTomatesCerise

SauceMarinara

Chaque ingrdient reprsente un produit qui est cr par la Fabrication dans la Fabrique Abstraite.

<<interface>> Fromage

Mozzarella

Reggiano

Les sous-classes produits crent des ensembles parallles de familles de produits. Ici, nous avons une famille Brest et une famille Strasbourg.
vous tes ici

161

votre bote outils de concepteur

Votre bote outils de concepteur


Dans ce chapitre, nous avons ajout deux patterns de plus dans votre bote outils: Fabrication et Fabrique Abstraite. Ces deux patterns encapsulent la cration des objets et vous permettent de dcoupler votre code des types concrets.

Tous les patterns de fabrication


encapsulent la cration des objets.

POINTS DIMPACT

Fabrique Simple nest pas un


authentique design pattern, mais un moyen simple de dcoupler vos clients des classes concrtes.

Abstraction n Encapsulatio e. ri va ce qui orphisme Encapsulez ritage.Polym lh n io t la ncapsu itage Prfrez le des Hr
es, non des interfac ez m am gr ro P ions. implmentat aiblement de coupler f us vo z . ce Effor qui interagissent les objets re ouvertes doivent tm s se as s la cl es L mais fer e lextension n.. modificatio ctions. Ne tes. des abstra s concr Dpendez pa as s des cl se ez d s. en as p d ncrete cl se depend on co

Principes OO

Bases de lOO

Fabrication sappuie sur


lhritage: la cration des objets est dlgue aux sous-classes qui implmentent la mthode de fabrication pour crer des objets.

Fabrique Abstraite sappuie sur

Nous avons un nouveau principe qui nous conseille dutiliser des abstractions quand cest possible.

la composition: la cration des objets est implmente dans las mthodes exposes dans linterface fabrique.

Tous les patterns de fabrique


favorisent le couplage faible en rduisant la dpendance de votre application aux classes concrtes.

O Patterns O lation ille une ree fa it ef inm init-und esurs,

f siournit e d de n lu uc unr eps. c une an h t ie hu ct a a v tg t lep r ab e e F a s gs psu ne a Str y c e t O ha ir e ca le e it r b d a t s, u t a e r e n e s je g m e t t b n h s a a o m je b it h r b l n c o A o u r p e et ec lg p e e t bj u u u trr in es. r s o dae t nD e sq d u iq n d s r emiq r n lcr es lor eu eam da ill r , it nn asnbs m eil e hn F pd f b it q y re a d es o d e d n lg le r aq o a ux i o e p i u ft u s u ded q jo a ere cifie t s ur f x sp e t u m po n e e r is e c e r e d li m p oi s c ac u f san ie o jdeat n e tb d g ie ,ter tt in sans av n t t so o st n a io t e t n t e m Str u d m s lu da en o i s p n if d e t e otsuou n p n d in t npa nt r so e ie la les re ie m vare ea nIl ap .nc p nt t me ra eiv .e .r p ut t es.u iq nm e tiq t e r re o a d co t n lut n s r e ailuis se e t as alt leura s clion pour terface t drivtionnalits..rication - Dfinit un, emin is a Fab cration dun objet ix ednes fonc cho pour la us-classes le ation permet so x u a t n ic laissa ancier. Fabr tion classes instde dlguer linstancia e ss une cla sses. des sous-cla

erns Ces nouveaux patt Lintention de Fabrication est ux de encapsulent tous jets de permettre une classe de la cration des obr des dlguer linstanciation ses et dbouchent sudcouples et sous-classes. conceptions plus Lintention de Fabrique Abstraite plus souples.
est de crer des familles dobjets apparents sans avoir dpendre de leurs classes concrtes.

Le Principe dinversion des


dpendances nous pousse viter de dpendre des types concrets et nous appuyer sur des abstractions.

Les fabriques constituent une


technique puissante pour utiliser des abstractions, non des classes concrtes

162

Chapitre 4

les patterns fabriques

Ctait un long chapitre. Prenez une part de pizza et relaxez-vous en faisant ces mots-croiss. Tous les mots de la solution sont dans ce chapitre.
1 2 4

8 9 11 12 15 16 13 14 10

21 17 18

19

20

Horizontalement 1. Permet de crer des familles dobjets apparents (deux mots). 6. Peut tre fine ou paisse. 7. Objet. 8. Nom de labstraction dont dpendent les pizzas concrtes. 9. On en met sur certaines pizzas. 12. Tous les patterns de fabrication permettent d__________ la cration des objets. 15. Pas forcment idal pour crer un objet. 16. La plupart des patterns de conception visent les viter. 18. Non spcialis. 19. Peut tre immdiate ou diffre.

Verticalement 1. Dlgue linstanciation aux sous-classes. 2. Vrai ou faux. 3. Mise en uvre. 4. Dans Fabrication, choisit la classe instancier (mot compos). 5. Sorte de parmesan. 8. Qui peut prendre plusieurs formes. 10. Strasbourg, les moules sont _________. 11. Il doit tre aussi faible que possible. 13. Mieux vaut dpendre dune ___________ que dune classe concrte. 14. Java en est un. 17. Marinara ou tomate.

vous tes ici

163

solutions des exercices

Solutions des exercices


vos crayons Sharpen your pencil
Nous en avons fini avec la classe PizzeriaBrest. Encore deux et nous serons prts accorder des franchises! crivez ici les implmentations de PizzeriaStrasbourg et de PizzeriaMarseille:

t comme celles de Brest... Ces deux boutiques sont exactemenfrentes sauf quelles crent des pizzas dif

public class PizzeriaStrasbourg extends Pizzeria { protected Pizza creerPizza(String choix) { if (choix.equals(fromage)) { return new PizzaFromageStyleStrasbourg(); } else if (choix.equals(vegetarienne)) { return new PizzaVegetarienneStyleStrasbourg(); } else if (choix.equals(fruitsDeMer)) { return new PizzaFruitsDeMerStyleStrasbourg(); } else if (choix.equals(poivrons)) { return new PizzaPoivronsStyleStrasbourg(); } else return null; } }

Ces deux boutiques sont exactement comme celles de Brest... sauf quelles crent des pizzas diffrentes

public class PizzeriaMarseille extends Pizzeria { protected Pizza creerPizza(String choix) { if (choix.equals(fromage)) { return new PizzaFromageStyleMarseille (); } else if (choix.equals(vegetarienne)) { return new PizzaVegetarienneStyleMarseille(); } else if (choix.equals(fruitsDeMer)) { return new PizzaFruitsDeMerStyleMarseille(); } else if (choix.equals(poivrons)) { return new PizzaPoivronsStyleMarseille(); } else return null; } }

et pour la boutique de Marseille, nous crons des pizzas style Marseille.

164

Chapitre 4

les patterns fabriques

Solution du problme de conception


Il nous faut un autre type de pizza pour ces fous de Marseillais (fous dans le BON sens du terme bien sr). Reprsentez lautre ensemble de classes parallles dont vous avez besoin pour ajouter une rgion marseillaise notre Pizzeria.

Pizzeria
creerPizza() commanderPizza()

il vous faut pour Voici tout ce qu ia californienne, les crer une Pizzer ia concrte et er z z pi se as cl la seille. pizzas style Mar
PizzeriaMarseille
creerPizza()

PizzeriaBrest
creerPizza()

PizzeriaStrasbourg
creerPizza()

PizzaFromageStyleBrest PizzaPoivronsStyleBrest

PizzaFromageStyleStrasbourg PizzaPoivronsStyleStrasbourg

PizzaFromageStyleMarseille PizzaPoivronsStyleMarseille

PizzaFruitsDeMerStyleBrest

PizzaFruitsDeMerStyleStrasbourg PizzaVegetarienneStyleStrasbourg

PizzaFruitsDeMerStyleMarseille PizzaVegetarienneStyleMarseille

PizzaVegetarienneStyleBrest

Maintenant, inscrivez les cinq garnitures les plus bizarres que vous pouvez imaginer mettre sur une pizza. Ensuite, vous serez prt faire des affaires avec vos pizzas Marseille!

s Voici no Pure dananas suggestions... Salami Curs de palmier Sardines Amandes grilles
vous tes ici

165

solutions des exercices

Une Pizzeria trs dpendante


vos crayons Sharpen your pencil
Faisons comme si vous naviez jamais entendu parler de fabrication OO. Voici une version de la Pizzeria qui nutilise pas de fabrication. Faites le compte du nombre dobjets concrets dont cette classe dpend. Si vous ajoutez des pizzas de style Marseille cette Pizzeria, de combien dobjets dpendra-t-elle alors?
public class PizzeriaDependante { public Pizza creerPizza(String style, String type) { Pizza pizza = null; if (style.equals(Brest)) { if (type.equals(fromage)) { pizza = new PizzaFromageStyleBrest(); } else if (type.equals(vegetarienne)) { pizza = new PizzaVegetarienneStyleBrest(); } else if (type.equals(fruitsDeMer)) { pizza = new PizzaFruitsDeMerStyleBrest(); } else if (type.equals(poivrons)) { pizza = new PizzaPoivronsStyleBrest(); }

Gre toutes les pizzas de style Brest

} else if (style.equals(Strasbourg)) { if (type.equals(fromage)) { pizza = new PizzaFromageStyleStrasbourg(); } else if (type.equals(vegetarienne)) { pizza = new PizzaVegetarienneStyleStrasbourg(); } else if (type.equals(fruitsDeMer)) { pizza = new PizzaFruitsDeMerStyleStrasbourg(); } else if (type.equals(poivrons)) { pizza = new PizzaPoivronsStyleStrasbourg(); } } else { System.out.println(Erreur : type de pizza invalide); return null; } pizza.preparer(); pizza.cuire(); pizza.couper(); pizza.emballer(); return pizza; } }

Gre toutes les pizzas de style Strasbourg

Vous pouvez inscrire vos rponses ici:


166
Chapitre 4

nombre

12

avec Marseille

les patterns fabriques

vos crayons Sharpen your pencil


crivez la FabriqueIngredientsPizzaStrasbourg. Vous pouvez rfrencer les classes ci-dessous dans votre implmentation:
public class FabriqueIngredientsPizzaStrasbourg implements FabriqueIngredientsPizza { public Pate creerPate() { return new PateSoufflee(); } public Sauce creerSauce() { return new SauceTomatesCerise(); } public Fromage creerFromage() { return new Mozzarella(); }

public Legume[] creerLegumes() { Legume legume [] = { new OlivesNoires(), new Epinards(), new Aubergines() }; return legume ; } public Poivrons creerPoivrons() { return new PoivronsEnRondelles(); } public Moules creerMoules() { return new MoulesSurgelees(); }

Aubergine
Epinards OlivesNoires
PateSoufflee

PoivronsEnRondelles
SauceTomatesCerise MoulesSurgelees
Mozzarella

vous tes ici

167

solution des mots-croiss

Solution des mots-croiss


1

F A B R I C A T I O

B S O

R E G G

O O L E E N

M
6

P L E

U S C L

M E N T A
8

A N
9 10

Z
11

A S

O L Y M O R P H
17

L
13

S
14

S
12

U N C A B S T R A P S U L E R G E L E E S A N G A G E
18

O U P L A
21

15

16

T I O N

G E N D A N

C T

A Q U C E E

19

I O

20

168

Chapitre 4

5 le Pattern Singleton

Des objets uniques en leur genre g


g
Elle est ABSOLUMENT UNIQUE, je te dis. Regarde-moi ces lignes, ces courbes, ce chssis. Elle est blouissante! Cest moi que tu parles ou cest la voiture? Oh, et puis quand est-ce que je pourrai rcuprer mon gant de cuisine?

Notre prochain arrt est le Pattern Singleton, notre passeport pour la cration dobjets uniques en leur genre dont il nexiste quune seule instance. Vous allez srement tre ravi dapprendre que, parmi tous les patterns, le
Singleton est le plus simple en termes de diagramme de classes. En fait, ce diagramme ne contient quune seule classe! Mais ne vous mprenez pas: malgr sa simplicit du point de vue de la conception des classes, nous allons rencontrer pas mal de bosses et de nids de poule dans son implmentation. Alors, bouclez vos ceintures. nouveau chapitre

169

un et un seul Quest-ce dire? Tout un chapitre pour apprendre instancier UN SEUL OBJET ! Il ny a QUUN objet et UN SEUL.

Dveloppeur : quoi cela sert-il? Gourou : Il existe de nombreux objets dont il ne faut quun seul exemplaire: pools de threads, caches, botes de dialogue, objets qui grent des prfrences et des paramtres de registre, objets utiliss pour la journalisation et objets qui servent de pilotes des priphriques comme les imprimantes et les cartes graphiques. En ralit, pour bon nombre de ces types dobjets, des instanciations multiples provoqueraient toutes sortes de problmes: le comportement des programmes serait incorrect, les ressources satures ou les rsultats incohrents. Dveloppeur : Daccord, il y a peut-tre des classes qui ne doivent tre instancies quune fois, mais est-ce que cela justifie un chapitre entier? Ne peut-on pas simplement appliquer une convention ou employer des variables globales? En Java, je pourrais le faire avec une variable statique. Gourou : plus dun titre, le Pattern Singleton est une convention qui garantit quun seul objet est instanci pour une classe donne. Si vous en avez une meilleure, le monde entier aimerait la connatre. Mais souvenez-vous que tous les patterns sont des mthodes prouves, et celui-ci nchappe pas la rgle. Outre le fait dassurer quun seul objet sera cr, le Pattern Singleton nous fournit un point daccs global, tout comme une variable globale, mais sans les inconvnients. Dveloppeur : Quels inconvnients? Gourou : Eh bien, prenons un exemple. Si vous affectez un objet une variable globale, vous devez crer cet objet quand lapplication est lance*. Daccord? Mais si lobjet consomme beaucoup de ressources et que votre application narrte pas de lutiliser? Comme vous allez le voir, le Pattern Singleton vous permet de ne crer vos objets que lorsquils sont ncessaires. Dveloppeur : Cela na quand mme pas lair si difficile. Gourou : Si vous matrisiez bien les mthodes et les variables de classe statiques ainsi que les modificateurs daccs, a ne lest pas. Mais, dans tous les cas, il est intressant de voir le fonctionnement dun Singleton, et, malgr sa simplicit apparente, le code du Singleton est difficile comprendre. Demandez-vous simplement: comment faire pour empcher linstanciation de plusieurs objets? Ce nest pas vident, nest-ce pas?
*Cela dpend en fait de limplmentation. Certaines JVM creront ces objets la demande.

170

Chapitre 5

le pattern singleton

Le Petit Singleton
Petit exercice socratique dans le style du Petit Lispien

Comment creriez-vous un seul objet?

new MonObjet();

Et si un autre objet voulait crer un MonObjet? Pourrait-il de nouveau appeler new sur MonObjet?

Oui, bien sr.

Tant que nous avons une classe, pouvons-nous toujours linstancier une ou plusieurs fois?

Oui. Enfin, seulement si cest une classe publique.

Et sinon?

Eh bien, si ce nest pas une classe publique, seules les classes du mme package peuvent linstancier. Mais elles peuvent toujours linstancier plusieurs fois. Non, je ny avais jamais pens, mais je suppose que cest possible puisque cest une dfinition lgale.

Mmm, intressant Saviez-vous que vous pouviez crire ceci?

public MaClasse { private MaClasse() {} }

Et cela signifie?

Je suppose que cest une classe qui ne peut pas tre instancie parce quelle a un constructeur priv.

Bien. Y a-t-il UN objet qui pourrait utiliser le constructeur priv?

Euh, je pense que le code de MaClasse est le seul qui pourrait lappeler. Mais cela na pas beaucoup de sens.

vous tes ici

171

crer un singleton

Pourquoi?

Parce quil me faudrait une instance de la classe pour lappeler, mais je ne peux pas avoir dinstance parce quaucune autre classe ne peut la crer. Cest le problme de luf et de la poule: je peux utiliser le constructeur depuis un objet de type MaClasse, mais je ne pourrai jamais instancier cet objet parce quaucun autre objet ne peut utiliser new MaClasse(). MaClasse est une classe qui a une mthode statique. Nous pouvons appeler la mthode statique comme ceci:
MaClasse.getInstance();

OK. Ctat juste une ide. Quest-ce que cela signifie?

public MaClasse { public static MaClasse getInstance() { } }

Pourquoi avez-vous utilis MaClasse au lieu dun nom dobjet?

Eh bien, getInstance() est une mthode statique ; autrement dit une mthode de CLASSE. On doit utiliser le nom de la classe pour rfrencer une mthode statique. Oui, bien sr.

Trs intressant. Et si nous rsumions le tout? Maintenant, est-ce que je peux instancier MaClasse?
public MaClasse { private MaClasse() {} public static MaClasse getInstance() { return new MaClasse(); } }

Et voyez vous une deuxime faon dinstancier un objet?

MyClass.getInstance();

Pouvez vous terminer le code de sorte quUNE SEULE instance de MaClasse soit jamais cre? 172
Chapitre 5

Oui, je crois... (Vous trouverez le code page suivante.)

le pattern singleton

Dissquons limplmentation du Pattern Singleton


Renommons Maclasse en Singleton
public class Singleton { private static Singleton uniqueInstance; // autres variables dinstance private Singleton() {} public static Singleton getInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } // autres mthodes }

.
ons une Nous ave statique qui ue variabl t notre uniq contien e de la classe instanc on. Singlet
Notre constructeur est dclar private: seul Singleton peut instancier cette classe!

Regardez !
Si vous tes en train de feuilleter ce livre, ne vous prcipitez pas pour taper ce code. Nous verrons plus loin dans ce chapitre quil pose quelques problmes.

La mthode getInstance() nous fournit un moyen dinstancier la classe et den retourner une instance.

Bien sr, Singleton est une classe normale: elle possde dautres variables dinstances et dautres mthodes.

null, nous r duniqueInstance est e leu va la Si dinstanc uniqueInstance contient notre navons pas encore cr ezsouven ce; instan UE UNIQ et, si elle nexiste pas, nous crons vous que cest une variable une instance de Singleton via statique. son constructeur priv et nous laffectons uniqueInstance. que si nous navons jamais Notez if (uniqueInstance = = null) { besoin de linstance, elle ne sera uniqueInstance = new MaClasse(); jamais cre; cest ce quon nomme } instanciation la demande.
return uniqueInstance;

Code la loupe

Parvenus l, nous avons une instance et nous la retournons.

Si uniqueInstance ntait pas null, cest quelle avait t cre prcdemment. Nous allons directement linstruction return.
vous tes ici

173

interview avec singleton

Interview
Linterview de cette semaine :

Confessions dun Singleton


DPTLP : Aujourdhui, nous avons le plaisir de vous prsenter un interview avec un objet Singleton. Pourquoi n e pas commencer par nous parler un peu de vous? Singleton: Eh bien, je suis totalement unique. Je nexiste qu un seul exemplaire! DPTLP : Un seul? Singleton : Oui, un seul. Je suis bas sur le Pattern Singleton, qui garantit qu un moment donn il ny a quune seule instance de moi. DPTLP : Nest-ce pas un peu du gaspillage? Quelquun a pris la peine de dvelopper une classe part entire et maintenant elle ne sert qu crer un seul objet? Singleton : Pas du tout! Il y a de la puissance dans le UN. Disons que vous avez un objet qui contient des paramtres de registre. Vous ne voulez pas que plusieurs copies de cet objet et de ses valeurs courent et l ce serait le chaos. En utilisant un objet comme moi, vous tes sr que chaque objet de votre application utilise la mme ressource globale. DPTLP : Dites nous-en plus Singleton : Oh, je suis bon toutes sortes de choses. tre seul a parfois ses avantages, savez-vous. On memploie souvent pour grer des pools de ressources, comme des pools de connexions ou de threads. DPTLP : Pourtant, unique en votre genre Vous devez vous sentir trs seul. Singleton : Comme je suis le seul dans mon genre, je suis toujours trs occup, mais ce serait bien si plus de dveloppeurs me connaissaient. Beaucoup de programmeurs crent des bogues parce quils ont plusieurs copies dobjets qui se promnent partout et ils ne sen rendent mme pas compte. DPTLP : Mais, si je puis me permettre, comment savez-vous que vous tes le seul? Personne ne peut-il crer un nouveau vous avec un oprateur new? Singleton : Nan! Je suis vraiment unique. DPTLP : Est-ce que les dveloppeurs feraient le serment de ne pas vous instancier plusieurs fois? Singleton : Bien sr que non. Pour dire la vrit eh bien, ceci prend un tour un peu trop personnel, mais, voyez-vous, je nai pas de constructeur public. DPTLP : PAS DE CONSTRUCTEUR PUBLIC! Oh, pardon, pas de constructeur public? Singleton : Cest cela. Mon constructeur est dclar priv. DPTLP : Mais comment est-ce possible? Comment pouvez-vous mme TRE instanci? Singleton : Voyez-vous, pour obtenir un objet Singleton, vous ne linstanciez pas, vous en demandez simplement une instance. Ma classe a donc une mthode statique nomme getInstance(). Appelez-la et je me montre immdiatement, prt me mettre au travail. En fait, je suis peut-tre dj en train daider dautres objets quand vous faites appel moi. DPTLP : Eh bien, M. Singleton, malgr votre simplicit, cela ne doit pas tre vident de faire tout ce travail. Merci de vous tre confi nous, et nous esprons vous revoir bientt!! 174
Chapitre 5

le pattern singleton

La fabrique de chocolat
Chacun sait que toutes les fabriques de chocolat modernes ont des bouilleurs assists par ordinateur. La tche du bouilleur consiste contenir un mlange de chocolat et de lait, le porter bullition puis le transmettre la phase suivante o il est transform en plaquettes de chocolat. Voici la classe contrleur du bouilleur industriel de Bonchoco, SA. Si vous tudiez le code; vous constatez quils ont essay trs soigneusement dviter les catastrophes, par exemple de vider deux mille litres de mlange qui na pas bouilli, de remplir un bouilleur dj plein ou de faire bouillir un bouilleur vide!
public class BouilleurChocolat { private boolean vide; private boolean bouilli; public BouilleurChocolat() { vide = true; bouilli = false; }

Ce code nest excut que si le bouilleur est vide!

public void remplir() { if (estVide()) { vide = false; bouilli = false; // remplir le bouilleur du mlange lait/chocolat } } public void vider() { if (!estVide() && estBouilli()) { // vider le mlange vide = true; } } public void bouillir() { if (!estVide() && !estBouilli()) { // porter le contenu bullition bouilli = true; } } public boolean estVide() { return vide; } public boolean estBouilli() { return bouilli; } }

le bouilleur, il doit Pour pouvoir remplir il est plein, nous tre vide. Une fois quteurs vide et positionnons les indica bouilli.

Pour pouvoir vider le bouilleur, il faut quil soit plein (non vide) ET quil ait bouilli. Une fois quil est vid, nous repositionnons vide true.

Pour pouvoir faire bouillir le mlange, il faut que le bouilleur soit plein et quil nait pas dj bouilli. Une fois quil a bouilli, nous positionnons bouilli true.

vous tes ici

175

un bouilleur singleton

Musclez vos neurones

Bonchoco a fait un travail honorable en essayant dviter les problmes, nest-ce pas votre avis? Pourtant, vous souponnez probablement quen lchant deux instances de BouilleurChocolat dans la nature, on sexpose des catastrophes. Que pourrait-il se passer si on crait plusieurs instances de BouilleurChocolat dans une application?

vos crayons Sharpen your pencil

Pouvez-vous aider Bonchoco amliorer sa classe BouilleurChocolat en la transformant en singleton?

public class BouilleurChocolat { private boolean vide; private boolean bouilli;

private BouilleurChocolat() { vide = true; bouilli = false; }

public void remplir() { if (estVide()) { vide = false; bouilli = false; // remplir le bouilleur du mlange lait/chocolat } } // reste du code de BouilleurChocolat... }

176

Chapitre 5

le pattern singleton

Le Pattern Singleton: dfinition


Maintenant que vous avez limplmentation classique de Singleton en tte, il est temps de vous installer confortablement, de dguster une barre de chocolat et dtudier de plus prs les subtilits du Pattern Singleton. Commenons par la dfinition concise du pattern:

Le Pattern Singleton garantit quune classe na quune seule instance et fournit un point daccs global cette instance.

Jusque l, pas de surprises. Mais dcomposons un peu plus:

Que se passe-t-il rellement? Nous prenons une classe et nous lui faisons
grer une seule instance delle-mme. Nous empchons galement toute autre classe den crer une autre instance. Pour obtenir une instance, vous devez passer par la classe elle-mme.

Nous fournissons galement un point daccs global cette instance: chaque


fois que vous en avez besoin, il suffit de demander la classe et celle-ci vous retournera cette instance unique. Comme vous lavez constat, nous pouvons avoir une implmentation telle que le Singleton soit cr la demande, ce qui est particulirement important pour les objets gourmands en ressources. Bien. Voyons le diagramme de classes:

atique, Csee. t s t s e ) ( tance thode de clas de getInss o e m h t m a L ce t uneer cette mthod e u q ie if n qui sig vez donc accd e votre code en Vous pouorte quel point d ance(). Cest de nimp Singleton.getInstcder une variutilisant si facile que dac ton nous apporte tout aus ale, mais le Singlee la linstanciation able glob avantages, comm dautres ande. la dem

Singleton static uniqueInstance // Autres attributs du Singleton... static getInstance() // Autres mthodes du Singleton...

La variable de classe uniqueInstance contient notre seule et unique instance de Singleton.

vre Une classe mettant en uplu s est n le Pattern Singleto sse cla e un st quun Singleton: ce son propre de gnraliste qui poss de mthodes. ensemble dattributs et
vous tes ici

177

le problme des threads

QG? Al l o le Houston, Nous avons un problme...


On dirait que le bouilleur de chocolat nous a laisss tomber. Bien que nous ayons amlior le code en utilisant un Singleton classique, la mthode remplir() de BouilleurChocolat sest dbrouille pour commencer remplir le bouilleur alors quun lot de chocolat et de lait y bouillait dj! Cela fait deux mille litres de lait (et de chocolat) gchs! Que sest-il pass!?

Nous ne savons pas ce qui sest pass! Le nouveau code avec Singleton fonctionnait trs bien. Notre seule ide, cest que nous avons ajout quelques optimisations au Contrleur pour quil puisse utiliser plusieurs threads..

N NTIO ATTE
olat Choc ant l l i bou

Lajout de threads pourrait-il tre la cause du problme? Nest-il pas vrai quune fois que nous avons affect la variable dinstance uniqueInstance la seule instance de BouilleurChocolat, tous les appels de getInstance() devraient retourner la mme instance? Daccord?

178

Chapitre 5

le pattern singleton

Vous tes la JVM

Nous avons deux threads, chacun excutant ce code. Votre tche consiste jouer le rle de la JVM et de dterminer sil existe un cas dans lequel deux threads pourraient semparer de diffrents objets bouilleur. Indication: Il suffit dobserver la squence doprations BouilleurChocolat bouilleur = de la mthode getInstance() BouilleurChocolat.getInstance(); remplir(); et la valeur bouillir(); duniqueInstance pour vider(); voir les chevauchements possibles. Utilisez les fragments de code pour comprendre comment le code peut sintercaler pour crer deux objets bouilleur.
public static BouilleurChocolat getInstance() { if (uniqueInstance == null) { uniqueInstance = new BouilleurChocolat();
}

Vrifiez bien votre rponse page 188 avant de tourner la page!

Thread Un

Thread Deux

Valeur de uniqueInstance

return uniqueInstance;

vous tes ici

179

multithread et singleton

Grer le multithread
Nos malheurs avec le multithread sont facilement limins si nous faisons de getInstance() une mthode synchronise:

public class Singleton { private static Singleton uniqueInstance; // autres variables dinstance private Singleton() {}

synchronized En ajoutant le mot-cl obligeons chaque getInstance(), nous n tour avant thread attendre so ode. Autrement dit, dentrer dans la mthent pas entrer dans deux threads ne peuv mps. la mthode en mme te

public static synchronized Singleton getInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } // autres mthodes } Je suis daccord que cela rgle le problme. Mais la synchronisation est coteuse. Est-ce que ce nest pas un autre problme?

Bien vu, et cest mme pratiquement pire quavant: la seule fois que la synchronisation sert quelque chose, cest lors du premier passage dans la mthode. Autrement dit, une fois que nous avons affect la variable uniqueInstance une instance de Singleton, nous navons plus besoin de synchroniser cette mthode. Aprs la premire passe, la synchronisation est une surcharge totalement inutile!

180

Chapitre 5

le pattern singleton

Peut-on amliorer le multithread?


Pour la plupart des applications Java, nous devons de toute vidence garantir que le Singleton fonctionne en prsence de plusieurs threads. Mais la synchronisation de la mthode getInstance() semble passablement coteuse. Que faire alors? Plusieurs options se prsentent nous...

1. Ne rien faire si la performance getInstance() nest pas vitale pour votre application
Cest vrai. Si lappel de la mthode getInstance() ne cause pas de surcharge substantielle votre application, ny pensez plus. Synchroniser getInstance() est simple et efficace. Mais noubliez pas quune mthode synchronise peut diminuer les performances par un facteur de 100. Si une partie fort trafic de votre code commence utiliser getInstance(), vous devrez peut-tre reconsidrer la question.

2. Prfrer une instance cre au dmarrage une instance cre la demande


Si votre application cre et utilise toujours une instance du Singleton ou si les aspects de surcharge de la cration et de lexcution du Singleton sont trop onreux, vous pouvez prfrer le crer au dmarrage, comme ceci:
public class Singleton { private static Singleton uniqueInstance = private Singleton() {} public static Singleton getInstance() { return uniqueInstance; } }

Allez de lavant et crez une instance du Singleton new Singleton(); un initialiseur statique. Ce code est garanti sans problme par rapport aux threads! dj une Comme nous avons ffit de instance, il nous su la retourner.

En adoptant cette approche, nous nous reposons sur la JVM pour crer lunique instance du Singleton quand la classe est charge. La JVM garantit que linstance sera cre avant quun thread quelconque naccde la variable statique uniqueInstance.

vous tes ici

181

verrouillage double vrification

3. Utilisez le verrouillage double vrification pour rduire lusage de la synchronisation de getInstance()


Avec le verrouillage double vrification, nous commenons par vrifier si une instance est cre, et si non, ALORS nous synchronisons. De cette faon, nous ne synchronisons que la premire fois: exactement ce que nous voulons. Examinons le code:
public class Singleton { private volatile static Singleton uniqueInstance; private Singleton() {}

public static Singleton getInstance() { if (uniqueInstance == null) { synchronized (Singleton.class) { if (uniqueInstance == null) { Notez que nous ne uniqueInstance = new Singleton(); synchronisons que le } premier passage! } } return uniqueInstance; Une fois dans le bloc, vrifier de } nouveau. Si la valeu

Sil Chercher une instance. ns ny en a pas, entrer da un bloc synchronis.

r de la variable est toujours null, crer une instance. Le mot-cl volatile garantit que plusieurs threads grent correctement la variable uniqueInstance quand elle est initialise une instance de Singleton.

Si lutilisation de la mthode getInstance() pose un problme de performances, cette faon dimplmenter le Singleton peut rduire la surcharge de manire spectaculaire.

.
Regardez !

le Le verrouillage doub onne qu cti fon ne n tio ca rifi v partir de Java5!

uses la version1.4, de nombre Malheureusement, jusqu atile vol t-cl mo du lmentations JVM contiennent des imp le c ave e ect orr inc sation permettant une synchroni r ation. Si vous devez utilise ific vr ble dou e illag rou ver des tho m s utre da , envisagez une autre JVM que Java5 . ton gle Sin re vot pour crire

182

Chapitre 5

le pattern singleton

Pendant ce temps, la chocolaterie...


Pendant que nous tions en train de diagnostiquer les problmes du multithread, le bouilleur a t nettoy et il est prt repartir. Mais dabord, nous devons les rsoudre, ces problmes de multithread. Nous disposons e plusieurs solutions, chacune ayant ses avantages et ses inconvnients. Laquelle allons-nous employer?

vos crayons Sharpen your pencil


Pour chaque solution, dcrivez son applicabilit au problme du code de BouilleurChocolat: Synchroniser la mthode getInstance():

Utilisation de linstanciation au dmarrage:

Verrouillage double vrification:

Flicitations!
ce stade, la Fabrique de chocolat est un client heureux et Bonchoco est content que nous ayons appliqu notre expertise au code du bouilleur. Quelle que soit la solution que vous ayez choisie pour rsoudre le problme de multithread, le bouilleur doit maintenant fonctionner sans autres msaventures. Flicitations. Non seulement vous avez russi chapper deux tonnes et quelques de chocolat bouillant dans ce chapitre, mais vous avez galement vu tous les problmes potentiels du Singleton.
vous tes ici

183

questions et rponses sur singleton

questions stupides

Il ny a pas de

Q: R:

Pour un pattern si simple qui ne se compose que dune seule classe, ce Singleton semble avoir bien des problmes.

Q: R:

Est-ce que je ne peux pas me contenter de crer une classe dont toutes les mthodes et les variables sont statiques? Ne serait-ce pas identique un Singleton?

Q: R:

Et les chargeurs de classes? Jai entendu dire quil y avait un risque que deux chargeurs de classes finissent par avoir chacun leur propre instance de Singleton.

Bon, nous vous avions averti! Mais ne laissez pas ces problmes vous dcourager. Mme si les Singletons peuvent tre dlicats implmenter correctement, la lecture de ce chapitre vous aura permis dtre bien informs sur les techniques possibles et vous saurez en utiliser chaque fois que vous aurez besoin de contrler le nombre dinstances que vous crez.

Oui, si votre classe est autonome et ne dpend pas dune initialisation complexe. Toutefois, en raison de la faon dont Java gre les initialisations statiques, cela peut donner un vrai gchis, surtout si plusieurs classes sont impliques. Ce scnario aboutit souvent des bogues subtils et difficiles dtecter, qui sont lis lordre des initialisations. moins quil ny ait une raison imprative dimplmenter votre singleton de cette faon, il est de beaucoup prfrable de rester dans le monde objet.

Oui, cest vrai, puisque chaque chargeur de classe dfinit un espace de nommage. Si vous avez deux chargeurs de classes ou plus, vous pouvez charger la mme classe plusieurs fois (une par chargeur). Maintenant, si cette classe savre tre un Singleton, comme nous avons plusieurs versions de la classe, nous avons galement plusieurs instances du Singleton. Faites donc bien attention si vous utilisez plusieurs chargeurs de classes et des Singletons. Une faon de contourner ce problme est de spcifier le chargeur de classe vous-mme.

Les rumeurs selon lesquelles des Singletons auraient t dvors par des ramasse-miettes sont tout fait exagres
Avant Java 1.2, un bogue permettait au ramasse-miettes de collecter prmaturment les Singletons sils navaient pas de rfrence globale. Autrement dit, vous pouviez crer un Singleton, et, si la seule rfrence au Singleton tait dans le Singleton lui-mme, celui-ci tait collect et dtruit par le ramasse-miettes. Cela provoquait des bogues complexes: aprs le ramassage du Singleton, lappel de getInstance() suivant produisait un Singleton flambant neuf. Dans de nombreuses applications, cela provoquait des comportements tranges, comme des tats retrouvant mystrieusement leur valeur initiale ou des connexions rseau rinitialises. Ce bogue a t corrig depuis Java1.2, et les rfrences globales ne sont plus requises. Si, pour une raison quelconque, vous utilisez encore une JVM antrieure Java1.2 JVM, sachez que ce problme existe. Sinon, vous pouvez dormir sur vos deux oreilles, vos Singletons ne seront pas prmaturment dtruits.

184

Chapitre 5

le pattern singleton

Q:

On ma toujours appris quune classe devait faire une chose et une seule, et quune classe faisant deux choses tait considre comme une mauvaise conception OO. Est-ce que Singleton nest pas en contradiction avec cette optique?

Q: R:

Je voulais sous-classer mon Singleton, mais jai eu des problmes. Est-ce quon a le droit de sous-classer un Singleton?

Q: R:

Je ne comprends toujours pas vraiment pourquoi les variables globales sont pires quun Singleton.

Cest ce quon appelle le principe Une classe, une responsabilit. Oui, cest vrai, le Singleton nest pas seulement responsable de la gestion de son unique instance (et de la fourniture dun point daccs global), il doit galement assurer le rle principal quil joue dans votre application. En ce sens, on peut certainement soutenir quil assume deux responsabilits. Nanmoins, il nest pas difficile de voir quil est utile quune classe gre sa propre instance et que cela simplifie coup sr la conception globale. En outre, beaucoup de dveloppeurs connaissent bien le pattern Singleton, car il est largement utilis. Cela dit, certains prouvent le besoin dextraire les fonctionnalits du Singleton.

R:

OLun des problmes du sous-classement dun Singleton est que son constructeur est priv. On ne peut pas tendre une classe dont le constructeur est priv. Vous devez donc commencer par modifier le constructeur pour quil soit public ou protg. Mais alors, ce nest plus vraiment un Singleton, puisque dautres classes peuvent linstancier. Si vous dcidez nanmoins de modifier le constructeur, il y a un autre problme. Limplmentation du Singleton est base sur une variable statique: si vous sousclassez directement, toutes vos classes drives partageront la mme variable dinstance. Ce nest probablement pas ce que vous aviez en tte. Pour que le sous-classement fonctionne, il est donc ncessaire dimplmenter une sorte de registre dans la classe de base. Avant de mettre en uvre un tel schma, demandez-vous ce que vous gagnez rellement sousclasser un Singleton. Comme la plupart des patterns, le Singleton nest pas ncessairement cens tre une solution qui peut sinsrer dans une bibliothque. De plus, le code du Singleton est facile ajouter nimporte quelle classe existante. Enfin, si vous utilisez un grand nombre de Singletons dans votre application, vous devez examiner de trs prs votre conception. Les Singletons sont faits pour tre consomms avec modration.

En Java, les variables globales sont essentiellement des rfrences statiques des objets. Utiliser des variables globales de cette manire prsente un certain nombre dinconvnients. Nous en avons dj mentionn un: la question de linstanciation la demande contre linstanciation au dmarrage. Mais noublions pas lintention du pattern: garantir quune classe na quune seule instance et fournir celle-ci un point daccs global. Une variable globale satisfait la seconde proposition, mais pas la premire. Les variables globales ont galement tendance encourager les dveloppeurs polluer lespace de nommage avec une foule de rfrences globales de petits objets. Ce nest pas le cas des Singletons, bien quon puisse en abuser.

vous tes ici

185

votre bote outils de concepteur

Votre bote outils de concepteur


Vous venez dajouter un nouveau pattern votre bote outils. Singleton met votre disposition une autre mthode pour crer des objets, en loccurrence des objets uniques. POINTS DIMPACT

Le Pattern garantit quil existe


au plus une instance dune classe donne dans une application.

Principes OO

Bases de lOO
Abstraction n Encapsulatio

Le Pattern Singleton fournit


galement un point daccs global cette instance.

ce qui varie. Encapsulez e lhritage. Polymorphism apsulation nc le ez r f Pr non des itage interfaces, Hr es d ez m Program ions. implmentat aiblement de coupler f us vo z ce nt. Effor ui interagisse les objets q ouvertes doivent tre Les classes mais fermes la lextensionn. modificatio ions. Ne . es abstract Dpendez d s des classes concrtes Quand dpendez pa une

Java implmente le Pattern


Singleton en utilisant un constructeur priv, une mthode statique combine une variable statique.

Examinez vos contraintes de


performances et de ressources et choisissez soigneusement une implmentation de Singleton approprie pour les applications multithread (et il faut considrer que toutes les applications sont multithread!).

O Patterns O lation ille une ree fa it ef inm init-und esurs,

ntir vous devez gara se stance dune clas qu seule inut re t e dans vo donne sexc optez le Singleton. application, ad

Attention au verrouillage
double vrification. Il nest pas fiable en cas de threads multiples dans les versions antrieures Java2, version5.

f e dsi de n uc unr pulu farcf c -e re an ee h t ie hu tn ct a a v in tg t lep r ab e e su n ae s g s a Str y c a it t s. O e ace he ir es in t cn le ep a r b in d t s, u f t a e e n e D je g m e t b n u h a a o m je it h r b n it r l n c o n o u in r p io e c e e lg p t e t f .n sse na uu a tr sd D daenD t e sq urec ic m nis d sanlg r d r ,n e,a n iq l lo t eu n b e d rq m , in n a it m je io e e e a sna hn b il p t o le b it is a y r a a n d t s d ic m u e r e o e b ap ux ia o nd iuix ue je fF uo sla s io u u b qf ded qt F t jo ere ot t q s x e a n u r m it u n e c e o r is e t d c h e d li n m c la p s n c a u r t t r io s le ie o a e u n e e t g t d o s j d g ie point p e a , b tta n eset r ix o c n n u io la et huor t cso n tss co la m Str te ut n m sso esn r lu da le a ue le un ou i oe d g s rm so psa n if eit f in p ss t e x u S o n p la n a n c u d io t e in t t la sc t n a r so n m e ic ie a r ie so r is m e nce. b st p e x r la at a vare u u in e nIl n . a F st p t io . r. Fabe le t nse t riq u t niv e a eioin ie a m tt sa ic c n n r e r n t is . p u e a n s t la c iq u n st e le ia t e c in q a is l n il a t m a r b ie o a s d st c t lo e n lut n n g in io a ss sses s e aualt l t e lar t st p r ia ce y e c in u n t r a g e u de dl lguer linst o c epd la n c c a io d t ss a la c iv d r e e n d u d tu e s.. ssss t lala ca li n nue es. nsio c cs so fon es. ss la de c su so s de

Soyez prudent si vous utilisez


plusieurs chargeurs de classes. Cela pourrait mettre en chec limplmentation du Singleton et crer plusieurs instances de la mme classe.

Si vous travaillez avec une

ation de Comme vous lavez vu, malgr sa simplicit apparente, limplment tre, chapi ce lu avoir Singleton comporte de nombreux dtails. Mais aprs t. dser vous serez prt appliquer Singleton, mme en plein
186
Chapitre 5

JVM antrieure 1.2, vous devrez crer un registre de Singletons pour battre de vitesse le ramasse-miettes.

le pattern singleton

Installez-vous confortablement, ouvrez cette bote de chocolats quon vous a envoye pour avoir rsolu le problme du multithread, et accordez vous un moment de loisir avec ces petits motscroiss. Tous les mots sont dans ce chapitre.
1 2 3 6 4 5

9 11 12

10

13

14

15

16

17 18

19

Horizontalement 1. Garantit quune classe na quune seule instance. 6. Le point daccs fourni par le Singleton est _____. 7. Selon certaines rumeurs, il leur arrive de manger des Singletons (mot compos). 8. Certains objets doivent obligatoirement tre _________. 11. Peut tre noir ou au lait. 14. Mlang au chocolat. 15. Le constructeur du Singleton est _____. 18. Page 169, elle est unique en son genre. 19. Une solution possible au problme du multithread.

Verticalement 2. Cration dobjet. 3. Matre penser. 4. Le Singleton est embarrass: il na pas de __________ public. 5. Le Singleton nen a quune. 9. Le Singleton assure quil nen existe quune. 10. Le fabricant de chocolat de ce chapitre. 12. Lun des principes OO. 13. getInstance() est une mthode ________. 16. Nouveau mot-cl de Java5, rserv jusquici. 17. Nest donc pas priv. 18. Condition pralable au remplissage.

vous tes ici

187

solutions des exercices

Solutions des exercices

Vous tes la JVM

Thread un
public static BouilleurChocolat getInstance() {

Thread Deux

Value de uniqueInstance
null

public static BouilleurChocolat getInstance() { if (uniqueInstance == null) { if (uniqueInstance == null) { uniqueInstance = new BouilleurChocolat();

null

Oh oh, cela na pas lair terrible!

null

<object1>

return uniqueInstance; uniqueInstance = new BouilleurChocolat();

<object1>

<object2>

<object2> return uniqueInstance;

vos your crayons Sharpen pencil

Deux objets diffrents sont retourns! Nous avons deux instances de BouilleurChocolat!!!

Pouvez-vous aider Bonchoco amliorer sa classe BouilleurChocolat en la transformant en singleton?

public class BouilleurChocolat { private boolean vide; private boolean bouilli; private static BouilleurChocolat uniqueInstance; private BouilleurChocolat() { empty = true; boiled = false; } public static BouilleurChocolat getInstance() { if (uniqueInstance == null) { uniqueInstance = new BouilleurChocolat(); } return uniqueInstance; } public void remplir() { if (estVide()) { vide = false; bouilli = false; // remplir le bouilleur dun mlange lait/chocolat } } // reste du code de BouilleurChocolat ... }

188

Chapitre 5

le pattern singleton

Solutions des exercices

vos your crayons Sharpen pencil


Pour chaque solution, dcrivez son applicabilit au problme du code de BouilleurChocolat: Synchroniser la mthode getInstance() :

Technique simple dont le fonctionnement est garanti. Comme il semble que nous nayons aucune contrainte de performance avec le bouilleur, ce serait un bon choix.
Utiliser linstanciation la demande :

Comme nous allons toujours instancier le bouilleur dans notre code, linitialisation statique de linstance ne poserait pas de problme. Cette solution fonctionnerait aussi bien que la mthode synchronise, bien quun peu moins vidente pour un dveloppeur familier du pattern standard.
Utiliser le verrouillage double vrification:

Comme nous navons pas de contraintes de performance, le verrouillage double vrification semble excessif. En outre, nous devons utiliser au moins Java 5.

vous tes ici

189

solution des mots-croiss

Solutions des mots-croiss

N
6

C O N S T B A

C L

N S T A N C I A T I O N
9 8 7

G T

L E

A S S E

R U

I
11

10

B O L A

C T E U
15

N S T A N C E
18 13 14

12

H E R

O C T H O C O P

I T A G

16

V O L

T A T V I D E O I Q U E
19

17

A T I L S E R

U B L

I C

190

Chapitre 5

6 le pattern Commande

g Encapsuler
Ces botes aux lettres pour documents top secrets ont rvolutionn lindustrie de lespionnage. Je nai qu dposer ma requte et des gens disparaissent, des gouvernements sont renverss du jour au lendemain, et en plus elles servent mme de dpt de pressing. Inutile de se demander o, quand et comment! a marche, tout simplement!

linvocation

Dans ce chapitre, nous allons envisager lencapsulation un tout autre niveau: nous allons encapsuler linvocation des mthodes. Oui, en encapsulant les appels de mthodes, nous allons pouvoir
cristalliser des traitements afin que lobjet qui les invoque nait pas besoin de savoir comment il sont excuts: il suffit quil utilise nos mthodes cristallises pour obtenir un rsultat. Nous pourrons mme faire des choses drlement futes avec ces appels de mthodes encapsuls, par exemple les sauvegarder pour la journalisation ou les rutiliser pour implmenter des annulations dans notre code.

nouveau chapitre

191

maisons de rve

Maisons de rve, SARL. 1221, avenue de lIndustrie, Bureau 2000 99999 Futurville

Bonjour, n, PDG de Jai rcemment reu de Jean-Loup Raga version de MtoExpress, une documentation et une extensible. Je mto n statio elle nouv leur de tion dmonstra de votre qualit la par n ssion dois dire que jai t si impre demander vous de rais jaime que elle logici re tectu archi systme de de concevoir une API pour notre nouveau distance. Pour programmation dquipements mnager s heureux de vous remercier de vos services, nous seron socit. notre de ites gratu ns vous offrir des stock optio un prototype de Vous trouverez ci-joint pour information dispose de sept Elle . naire ution notre tlcommande rvol pouvant tre deux n chacu , ables amm progr nts ceme empla propre son dant poss et ent diffr eil affect un appar quipe dun interrupteur. La commande est galement bouton Annulation global. ble de classes Je joins galement sur CD-ROM un ensem isseurs pour fourn ents diffr par s cre t Java qui ont ages, clair s: tique domo contrler des quipements et autres audio ts emen quip zis, jacuz rs, lateu venti ions que appareils contrlables similaires. Nous aimer la tlcommande, vous criviez une API pour programmer tre affect au de sorte que chaque emplacement puisse dquipements. ble ensem dun ou contrle dun quipement commander ions puiss nous que tant impor est quil Notez e, mais disqu le sur nt lleme actue nt les appareils figura aient proposer aussi tous ceux que les fournisseurs pourr lavenir. u sur la station tant donn le travail que vous avez effect que notre incus conva es somm nous ress, de MtoExp tlcommande sera une russite! Dans lattente de recevoir votre conception Cordialement, X. Disse, PDG Bill X-10 Thompson, CEO .

192

Chapitre 6

le pattern Commande

Matriel gratuit! Examinons la commande distance...


Il y a des interrupteurs marche et arrt pour chacun des sept emplacements.
emplacements Nous avons sept s pouvons affecter un programmer. Nou t et le commander avec appareil diffren les boutons.

Marc he

Arr t

Ces deux boutons contrler lapparservent mnager mmoris eil lemplacement1... ... et ces deux-ci contrlent celui de lemplacement2... ... et ainsi de suite.

ivez Sortez votre Bic et inscr ils. re pa ap ici les noms des

Annu ler

Voici le bouton Annulation global qui annule laction du dernier bouton press.

vous tes ici

193

Les classes des fournisseurs de domotiques

Jetons un coup dil aux classes des fournisseurs


Voyons les classes qui se trouvent sur le CD-ROM. Elles devraient nous donner une ide des interfaces des objets que nous devons contrler avec la tlcommande.

ControleAppareil
marche() arret()

Stereo
marche()

Plafonnier
marche() arret() attenuer()

arret() setCd() setDvd() setRadio() setVolume()

TV
marche() arret() selectionnerCanal() setVolume()

LumiereExterieur
marche() arret()

ControleRobinet
valeurOuvert() valeurFerme()

Ventilateur
rapide()

Jacuzzi
bouillonner()

LumiereJardin
setNuit() setJour() modeManuel() modeAuto()

moyen() lent() arret() getVitesse()

PorteGarage
ouvrir() fermer() stop() lampeAllumee() lampeEteinte()

marche() arret() setTemperature()

Thermostat
setTemperature()

Arrosage
eauMarche() eauArret() marche() arret()

Alarme
Lampe
armer() desarmer()

Certes, nous avons un bel ensemble de classes, mais on dirait que les industriels nont pas dploy beaucoup defforts pour proposer des interfaces communes. Pis encore, il parat quelles seront de plus en plus nombreuses lavenir. Dvelopper une API pour la tlcommande va tre une tche intressante. Attelons-nous la conception.

194

Chapitre 6

le pattern Commande

Conversation dans un box


Vos collgues sont dj en train de rflchir la conception de lAPI de la tlcommande...
Bien, nous avons une autre conception raliser. premire vue, nous avons une commande simple avec des boutons marche et arrt, mais les classes des fournisseurs sont trs diversifies.

Anne

Marie : Oui, je pensais quon aurait un ensemble de classes avec des mthodes comme marche() et arret(), mais ici nous avons des mthodes comme attenuer(), setTemperature(), setVolume(), setDirection(). Anne : Mais ce nest pas tout! Nous pouvons nous attendre recevoir lavenir dautres classes propritaires, avec des mthodes probablement tout aussi htrognes. Marie : Je pense quil est important de voir le problme comme une sparation des attributions: la commande doit savoir comment interprter les appuis sur les boutons, mais elle na pas besoin de savoir grand-chose en domotique, ni sur la faon de mettre en marche un jacuzzi. Anne : Lide semble bonne. Mais si la commande est passive et ne sait qumettre des requtes gnriques, comment allons nous la concevoir pour quelle puisse appeler une action qui va par exemple allumer une lampe ou ouvrir une porte de garage? Marie : Je ne sais pas, mais il ne faut pas que la commande ait besoin de connatre les dtails des classes propritaires. Anne : Quest-ce que tu veux dire? Marie : Il ne faut pas que la commande consiste en une suite dinstructions du type if emplacement1 = = Lampe, then lampe.marche(), else if emplacement1 = Jacuzzi then jacuzzi.marche(). Nous savons que cest une mauvaise pratique de conception. Anne : Je suis daccord. Il faudrait modifier le code chaque fois quun fournisseur crirait une nouvelle classe. Il y aurait potentiellement plus de bogues et plus de travail pour nous!

vous tes ici

195

le pattern commande devrait marcher Je suis daccord. Il faudrait modifier le code chaque fois quun fournisseur crirait une nouvelle classe. Il y aurait potentiellement plus de bogues et plus de travail pour nous!

Marie: Oui? Tu peux nous en dire plus? Jol: Le pattern Commande permet de dcoupler lauteur dune requte daction de lobjet qui effectue rellement laction. En loccurrence, le demandeur serait la tlcommande et lobjet qui effectue laction serait une instance de lune de nos classes propritaires. Anne: Comment est-ce possible? Comment peut-on les dcoupler? Aprs tout, quand jappuie sur un bouton, la tlcommande doit allumer une lumire. Jol: Vous pouvez le faire en introduisant des objets de commande dans votre conception. Un objet de commande encapsule une requte (par exemple allumer une lampe) un objet spcifique (par exemple la lampe du sjour). Si on mmorise un objet de commande pour chaque bouton, quand on appuie sur celui-ci, on demande lobjet de commande deffectuer un certain travail. La tlcommande na aucune ide de la nature de ce travail: cest simplement un objet de commande qui sait comment parler au bon objet pour que le travail soit fait. Et voil comment la tlcommande est dcouple de lobjet Lampe! Anne: Oui, on dirait bien que nous sommes dans la bonne direction. Marie: Pourtant, je narrive pas encore me faire une ide du fonctionnement du pattern. Jol: tant donn que les objets sont si dcoupls, il est un peu difficile dimaginer comment le pattern fonctionne rellement. Marie: Voyons au moins si mon ide est juste: en appliquant ce pattern, nous pourrions crer une API dans laquelle ces objets de commande seraient chargs aux emplacements qui correspondent aux boutons, ce qui permettrait au code de la tlcommande de demeurer trs simple. Et les objets de commande encapsuleraient la procdure pour effectuer une tche dautomatisation dans lobjet qui en a besoin. Jol: Oui, il me semble. Je pense aussi que ce pattern peut vous tre utile pour ce bouton Annulation, mais je nai pas encore tudi cette partie. Marie: Cela semble vraiment encourageant, mais je crois quil me reste du travail pour saisir rellement le pattern. Anne: Moi aussi.

196

Chapitre 6

le pattern Commande

Pendant ce temps, la caftria..., ou, Une brve introduction au pattern Commande


Comme la dit Jol, le pattern Commande est un peu difficile comprendre la seule lecture de sa description. Mais nayez crainte, nous avons des amis prts nous aider. Vous souvenez-vous de notre sympathique caftria du chapitre1? Il y a bien longtemps que nous navons pas vu Alice, Flo et le chef, mais nous avons de bonnes raisons dy retourner (enfin en plus de la cuisine et de la conversation grandiose): la caftria va nous aider comprendre le pattern Commande. Faisons donc un petit dtour par la caftria et tudions les interactions entre les clients, la serveuse, les commandes et le cuisinier. Grce ces interactions, vous allez comprendre les objets impliqus dans le pattern Commande et avoir une petite ide du fonctionnement du dcouplage. Aprs quoi, nous allons torcher lAPI de la tlcommande. Entrons la caftria dObjectville...

Caftria dObjectville

s visite Rendez-nou

Bien, nous savons tous comment la caftria fonctionne:


rger au Hambu ake Milk-sh e fromag

2 La Serveuse 1

Vous, le Client, passez votre Commande la Serveuse.

prend la Commande, la place sur le comptoir et dit a marche!

3 Le Cuisinier lit la Commande et prpare

votre repas.
vous tes ici

197

la caftria

tudions les interactions un peu plus en dtail


...et puisque nous sommes la caftria dObjectville, rflchissons aussi aux objets et aux appels de mthodes impliqus!

t compose dune La Commande es et des lments feuille de papier inscrits dessus. du menu qui sont
ege msa oe re fh writahu C erge gu rb Baum H e hak Malt S ake Milk-sh

Je prendrai un Hamburger au fromage et un milk-shake

pas

ser

Co

mm

and

e()

rt pa D
p re n

d re C

omm

ande

()

Le client sait ce quil veut, et il passe une commande

La Serveuse prend la Commande. termin, elle appelle sa mthode Quand elle a pour lancer la prparation de la faireMarcher() Commande.
Ma rc h er ()

mande es La Com t tout contien ructions les inst ires pour ncessa er le repas. prpar onne des Elle d au Cuisinier ordres es mthodes avec d que telles amburger(). faireH
198
Chapitre 6

e fromag rger au Cheese Hambu ge rewith ur ak sh B e ilk M Malt Shak

re fai

faireHamburger(), faireMilkshake()

Le Cuisinier suit les instructions de la Commande et confectionne le repas.

su

lta

le pattern Commande

Rles et responsabilits de la Caftria dObject ville


Une Commande encapsule une requte pour prparer un repas.
Reprsentez-vous la Commande comme un objet, un objet qui fait fonction de requte pour prparer un repas. Comme tout autre objet, il peut tre transmis, de la Serveuse au comptoir, ou la prochaine Serveuse qui prendra son poste. Il possde une interface qui ne contient quune seule mthode, faireMarcher(), qui encapsule les actions ncessaires pour prparer le repas. Il possde galement une rfrence lobjet qui doit le prparer (en loccurrence le Cuisinier). Il est en capsul au sens o la Serveuse na pas besoin de savoir ce que contient la commande, ou mme qui prpare le repas: il suffit quelle pose la commande sur le passe-plats et quelle crie a marche!

Comm

ande

)h{er(){ apr(c rU deeM ir r a o f id o id v o ; r(); c )e bliv uc ge r(g rb ur pubpli m e au HB ak e .m ir k a o o .f ; c ) r ); ( Sh cuisinie ook.mir ilakksehake( ak eeM a c .f r ie cuisin } }

Bon, cest soccupera vrai, dans la vie re la comman it probablement de lle, une serveuse sommes Ode et de qui la prp ce quil y a sur La tche de la Serveuse consiste prendre les a bjectville.. . bienvenuere, mais nous Commandes et invoquer leur mthode faireMarcher(). au club!
La Serveuse a la belle vie: prendre la commande du client et continuer servir les autres clients jusquau moment o elle retourne au comptoir et o elle invoque la mthode faireMarcher() pour faire prparer le repas. Nous le savons dj: Objectville, la Serveuse est totalement indiffrente ce qui est inscrit sur la commande ou qui va la prparer. Une seule chose lintresse: les commandes possdent une mthode faireMarcher() quelle peut appeler pour que le travail soit fait.
Ne me demandez pas de cuisiner. Je me contente de prendre les commandes et de crier a marche!.

Maintenant, tout au long de la journe, la mthode prendreCommande() de la Serveuse reoit en argument diffrentes commandes de diffrents clients, mais peu lui importe. Elle sait que toutes les commandes prennent en charge la mthode faireMarcher() et quelle peut lappeler chaque fois quelle a besoin de faire prparer un repas.

Le Cuisinier a les connaissances requises pour prparer le repas.


Le Cuisinier est lobjet qui sait rellement comment prparer des repas. Une fois que la Serveuse a invoqu la mthode faireMarcher(), le Cuisinier prend le relais et implmente toutes les mthodes ncessaires pour confectionner des repas. Remarquez que la Serveuse et le Cuisinier sont totalement dcoupls: la Serveuse a des Commandes qui encapsulent les dtails du repas, et elle se contente dappeler une mthode sur chaque commande pour quelle soit prpare. De mme, le Cuisinier reoit ses instructions de la Commande et na jamais besoin de communiquer directement avec la Serveuse.

La serveuse et moi, on est dcoupls, vous pouvez le dire. Elle nest mme pas mon type!

vous tes ici

199

la caftria: un modle du pattern Commande

B o n . Nous avons une Caftria et une Serveuse qui est dcouple du Cuisinier par une Commande. Et alors? Venons-en au fait!

Patience, on y arrive... Reprsentez-vous la Caftria comme le modle dun design pattern qui nous permet de sparer un objet qui met des requtes de ceux qui les reoivent et qui les excutent. Par exemple, dans lAPI de la tlcommande, nous devons sparer le code qui est appel quand on appuie sur un bouton des objets qui excutent les requtes, les instances des classes spcifiques aux fournisseurs. Que se passeraitil si chaque emplacement de la tlcommande contenait un objet tel que la Commande de la Caftria? Lorsquun bouton serai press, nous nous bornerions appeler lquivalent de la mthode faireMarcher()sur cet objet, et la lumire sallumerait sans que la tlcommande connaisse les dtails de la faon dallumer une lampe ni des objets ncessaires pour ce faire. Maintenant, passons la vitesse suprieure et voyons comment tout ce discours sur la Caftria correspond au pattern Commande...

Musclez vos neurones


e omage frees h Ch witau er er rgurg Bumb Ha Shake ke lt sha Ma Milk-

Not

ne pose du ments e est com des l ssus. mmand La Co de papier et crits de feuille nu qui sont ins pass du me

drai un Je pren au fromage ger Hambur milk-shake et un

erCo

mm

ande

()

Avant de poursuivre, consacrez quelques instants tudier le diagramme de la page 198, ainsi que les rles et les responsabilits de la Caftria pour bien comprendre les objets et les relations de la Caftria dObjectville. Ceci fait, tenez-vous prt vous attaquer au pattern Commande!

rt pa D

sait ce Le client , et il passe quil veut de une comman


prendreCom mande()

elle a la Commande. Quand () La Serveuse prend sa mthode faireMarcher ande. termin, elle appelle ation de la Comm pour lancer la prpar

ande omm utes La C ient to ions cont instruct pour les ssaires repas. nce arer le des prp donne uisinier Elle es au C thodes ordr des m avec s que rger(). telle eHambu fair
Malt

age from er au Cheese burg ke r with Ham -sha Burge Shake Milk

Note

Le Cuisinier suit les instructions de la Commande et confectionne le repas.


er(), fair eM ilkshak e()

fa

ire
faireHa mburg

ar

ch

er

()

200

Chapitre 6

rsu

lta

le pattern Commande

De la Caftria au pattern Commande


Parfait. Nous avons pass suffisamment de temps la Caftria dObjectville pour bien connatre tous les personnages et leurs responsabilits. Nous allons maintenant retravailler le diagramme de la Caftria et le comparer au pattern Commande. Vous verrez que tous les acteurs sont les mmes: seul leur nom a chang.

fournit Lobjet Commandeuter(), une mthode, execactions qui encapsule des ur les et quon appelle po epteur. invoquer sur le Rc

executer { public void action1(); recepteur. action2(); recepteur. }

Les actions et le Rcepteur sont associs dans lobjet Commande.

c re a

action1() action2() ...

te C

om
1

man

Recepteu

dOb

jec t

()

Le client est responsable de la cration de lobjet de commande. le Celui-ci est constitu dun ensemb dactions sur un rcepteur.
creer Objet Commande()

executer()

rt pa D

Co mmand

e
Le client appelle setCommande() un objet Invocateur et lui transm sur lobjet de commande. Lobjet est et mmoris jusqu ce quon en ait besoin.
mma nd()
setCommande()

Client
3

s e t Co
2

Chargement de lInvocateur
1 2 Le client cre un objet de commande. Le client appelle setCommande() pour mmoriser lobjet de commande dans linvocateur. Plus tard... le client demande linvocateur dexcuter la commande. Note: comme vous le verrez plus loin, une fois la commande charge dans linvocateur, on peut lutiliser et sen dbarrasser, ou bien la conserver et la rutiliser plusieurs fois.

nt n mome lle la u a , r i appe aven Dans l lInvocateur ) de lobjet donn, de executer( mtho ommande.... de la c
executer()

In vocateu
ex e t cu e()

Co mmand

action1() action2() ...

ce qui a pour effet dinvoquer les actions sur le Rcepteur.

action1(), action2() Re

cepteur

vous tes ici

201

qui fait quoi?

Qui fait quoi ?


g

Reliez les objets et les mthodes de la Caftria leurs homologues dans le pattern Commande.

Caftria Serveuse Cuisinier faireMarcher() Commande Client prendreCommande()

Command Pattern Commande executer() Client Invocateur Rcepteur setCommande()

202

Chapitre 6

le pattern Commande

Notre premier objet de commande


Ne serait-il pas temps de construire notre premier objet de commande? Allons-y! crivons un peu de code pour la tlcommande. Comme nous ne savons pas encore comment nous allons concevoir lAPI de la tlcommande, une dmarche ascendante pourra peut-tre nous aider...

Implmenter linterface Commande


Commenons par le commencement: tous les objets de commande implmentent la mme interface, laquelle ne contient quune seule mthode. Dans le cas de la Caftria, nous avions nomm cette mthode faireMarcher(); toutefois, on se contente gnralement de la nommer executer(). Voici linterface Commande :
public interface Commande { public void executer(); }

Simple. Il suffit dune seule mt

hode nomme executer().

Implmenter une Commande pour allumer une lampe


Maintenant, imaginons que vous vouliez implmenter une commande pour allumer une lampe. Si nous regardons de nouveau nos classes propritaires, la classe Lampe possde deux mthodes: marche() et arret(). Voici comment vous vous pouvez implmenter la commande :
Lampe
marche() arret()

Comme cest une commande, nous devons implmenter linterface Commande.


public class CommandeAllumerLampe implements Commande { Lampe lampe; public CommandeAllumerLampe(Lampe lampe) { this.lampe = lampe; } public void executer() { lampe.marche(); } }

er() appelle La mthode execut ) sur lobjet e( la mthode march que nous rcepteur: la lampe voulons contrler.

Nous transmettons au constructeur la lampe spcifique que cette commande va contrler par exemple la lampe du sjour et il la planque dans la variable dinstance lampe. Quand executer() est appele, cest lobjet Lampe qui va tre le Rcepteur de la requte.

Maintenant que nous avons une classe CommandeAllumerLampe, voyons si nous pouvons la mettre au travail...
vous tes ici

203

utiliser lobjet de commande

Utiliser lobjet de commande


Bien, simplifions les choses: disons que notre tlcommande ne dispose que dun bouton et de lemplacement correspondant pour contenir lappareil contrler:

public class TelecommandeSimple { Commande emplacement; public TelecommandeSimple() {} public void setCommande(Commande emplacement = commande; } public void boutonPresse() { emplacement.executer(); } }

a quun seul bouton Notre commande n n quipement qui ne contrle quu Nous avons une mthode pour indiquer la commande lemplacement quil va commande) { contrler. Elle pourrait tre appele plusieurs fois si le client de code voulait modifier le comportement du bouton de la commande.

Cette mthode est appele qua le bouton. Il suffit de prendre nd on presse courante associe lemplacem la commande ent et dappeler sa mthode executer( ).

Crer un test simple pour essayer la tlcommande


Voici un petit programme qui va tester notre tlcommande simple. Regardons-le et voyons comment ses lments correspondent au diagramme de classes du pattern Commande:

pattern Commande est notre V La tlcommande i transmettrons Invocateur; nous lu de qui servira public class TestTelecommande { un objet de comman qutes. public static void main(String[] args) { ur mettre des re po TelecommandeSimple telecommande = new TelecommandeSimple(); Lampe lampe = new Lampe(); Maintenant, nous crons CommandeAllumerLampe lampeAllumee = new CommandeAllumerLampe(lampe); lobjet Lampe. Ce sera le Rcepteur de la requte. ns le langage du oici notre Client, da
telecommande.setCommande(lampeAllumee); telecommande.boutonPresse();

} }

L, nous transmettons la commande lInvocateur .

Ici, nous crons une commande et nous la transmettons eu Rcepteur


Fichier dition Fentre Aide FiatLux

Et l nous simulons lappui sur le bouton.

%java TestTelecommande Lampe allume %

Voici le rsultat de lexcution de ce test!

204

Chapitre 6

le pattern Commande

vos crayons Sharpen your pencil


Bien. Il est temps pour vous dimplmenter la classe CommandeOuvrirPorteGarage. Commencez par le code de la classe ci-aprs. Vous aurez besoin du diagramme de classes de PorteGarage.
public class CommandeOuvrirPorteGarage implements Commande {

PorteGarage
ouvrir() fermer() stop() lampeAllumee() lampeEteinte()

crivez votre code

ici.

Maintenant que vous avez votre classe, quel est le rsultat du code suivant? (Indication: la mthode ouvrir() de PorteGarage affiche Porte garage ouverte quand elle se termine.)
public class TestTelecommande { public static void main(String[] args) { TelecommandeSimple telecommande = new TelecommandeSimple(); Lampe lampe = new Lampe(); PorteGarage porteGarage = new PorteGarage(); CommandeAllumerLampe lampeAllumee = new CommandeAllumerLampe(lampe); CommandeOuvrirPorteGarage garageOuvert = new CommandeOuvrirPorteGarage (porteGarage); telecommande.setCommande(lampeAllumee); telecommande.boutonPresse(); telecommande.setCommande(garageOuvert); telecommande.boutonPresse(); }
Fichier dition Fentre Aide SandwichAuJambon

%java TestTelecommande

ici. Inscrivez le rsultat

vous tes ici

205

dfinition du pattern Commande

Le pattern Commande: dfinition


Vous avez pass un certain temps la Caftria dObjectville, vous avez partiellement implment lAPI de la tlcommande et le processus vous a permis de vous faire une bonne ide des interactions des classes et des objets dans le pattern Commande. Nous allons maintenant dfinir ce pattern Commande et en tudier tous les dtails. Commenons par la dfinition officielle:
Le pattern Commande encapsule une requte comme un objet, autorisant ainsi le paramtrage des clients par diffrentes requtes, files dattente et rcapitulatifs de requtes, et de plus, permettant la rversibilit des oprations.

Une requte encapsule

action()

Recepteu

executer() { recepteur.action(); }

Dcortiquons un peu. Nous savons quun objet de commande encapsule une requte en associant un ensemble dactions un rcepteur spcifique. Pour ce faire, il groupe les actions et le rcepteur dans un objet qui nexpose quune seule mthode, executer(). Quand elle est appele, executer() provoque linvocation des actions sur le rcepteur. De lextrieur, aucun autre objet ne sait rellement quelles sont les actions excutes ni de quel rcepteur il sagit. Ils ne savent quune chose: sils appellent la mthode executer(), leur requte sera satisfaite. Nous avons galement vu quelques exemples de paramtrage dun objet avec une commande. la Caftria, la Serveuse tait paramtre par de multiples commandes toute la journe. Dans la simple tlcommande, nous avons commenc par charger dans lemplacement du bouton une commande allumer la lumire que nous avons remplace plus tard par une commande ouvrir la porte du garage. Comme la Serveuse, lemplacement en question ne se soucie pas de savoir quel objet de commande il manipule, tant quil implmente linterface Commande. Ce que nous navons pas encore abord, cest lutilisation de commandes pour implmenter des files dattentes et des rcapitulatifs de requtes et pour prendre en charge la rversibilit des oprations. Ne vous inquitez pas, ce sont des extensions relativement simples du pattern Commande de base et nous les tudierons bientt. Nous verrons galement quil est facile dappliquer ce quon appelle le pattern MtaCommande une fois les bases en place. Le pattern MtaCommande permet de crer des macrocommandes pour pouvoir excuter plusieurs commandes la fois.

C om ma n d e

ampe

rL ma ndeAllum

vr ir

Po r t e G

ar a
executer()

ge

executer()

r
executer()

executer()

lac

Un invocateur par exemple un emplacement de la tlcommande peut tre paramtr par diffrentes requtes.

ementTel

206

Chapitre 6

o mm ande
c

a te R a ur

pid

te reoEt

ein

te

m Co

u O

n Ve

til

p Em

le pattern Commande

Le pattern Commande : le diagramme de classes


LInvocateur contient une commande. un moment donn, il demande la commande de satisfaire une requte en appelant sa mthode executer().

ble de crer Le Client est responsa et de te une CommandeConcre r. dfinir son Recepteu

toutes les commandes. Commande dclare une interface pour est invoque de man Comme vous le savez dj, une com e un rcepteur via sa mthode executer(), qui demand z galement que deffectuer une action. Vous remarquere uler(), que nous cette interface possde une mthode ann pitre. aborderons un peu plus loin dans ce cha

Client

Invocateur

setCommande()

<<interface>> Commande executer() annuler()

Receiver action()

ConcreteCommand executer() annuler()

La mthode executer() invoque sur le rcepteur la ou les actions ncessaires pour satisfaire la requte.

Le Rcepteur sait comment effectuer le travail ncessaire pour rpondre la requte. Nimporte quelle classe peut jouer le rle de Rcepteur.

public void executer() { recepteur.action() }

e finit une liaison entre un te en d te re nc Co de an mm Co qu La LInvocateur met une re action et un Rcepteur. CommandeConcrete y rpond en appelant executer() et laactions sur le Rcepteur. appelant une ou plusieurs

Musclez vos neurones

Comment la conception du pattern Commande permet-elle de dcoupler linvocateur de la requte de son rcepteur?

vous tes ici

207

par o commenons-nous?

Bon. Maintenant, je crois que jai bien compris le pattern Commande. Excellent conseil, Jol. Je crois quon nous considrera comme des superstars quand nous aurons termin lAPI de la tlcommande.

Marie: Moi aussi. Alors, par o commence-t-on? Anne : Comme pour TelecommandeSimple, nous devons fournir un moyen daffecter des commandes aux emplacements. Dans notre cas, nous avons sept emplacements et chacun a un bouton marche et un bouton arrt. Nous pouvons donc affecter des commandes la tlcommande un peu de la mme manire. Marie: Cest faisable, sauf pour les objets Lampe. Comment la tlcommande fait-elle la diffrence entre la lampe du sjour et celle de la cuisine? Anne : Ah, mais justement, elle ne la fait pas! Elle ne sait rien faire dautre quappeler executer() sur lobjet de commande correspondant quand on presse un bouton. Marie: Oui, jai bien compris, mais dans limplmentation, comment allons-nous nous assurer que les bons objets allument et teignent les bons appareils? Anne : Quand nous crerons les commandes qui seront charges dans la tlcommande, nous crerons une CommandeLampe qui sera associe la lampe du sjour et une autre qui sera associe celle de la cuisine. Noublie pas que le rcepteur de la requte est li la commande dans laquelle il est encapsul. partir du moment o le bouton est press, personne ne se soucie de savoir de quelle lampe il sagit: tout se passe correctement quand la mthode executer() est appele. Marie: Je crois que a y est. Implmentons la tlcommande et je pense que tout va sclaircir! Anne : la bonne heure. Allons-y

208

Chapitre 6

le pattern Commande

Affecter des Commandes aux emplacements


Nous avons donc un plan. Nous allons affecter une commande chaque emplacement de la tlcommande. Cela fait de la tlcommande notre Invocateur. Lorsquon appuiera sur un bouton, la mthode executer() sera appele sur la commande correspondante, ce qui aura pour effet dinvoquer des actions sur un rcepteur (par exemple une lampe, un ventilateur ou une chane stro).

(1) Chaque emplacement reoit une comm

ande.
m pe
executer()

ma Com ma Com
tila Ven

a rL nd eAllume

a rL nd eAllume

executer()

m pe

(2) Quand le bouton est press, la mthode executer() est appele sur la commande correspondante.
Lamp e sjou du r Lamp e cuisin de la e Ventil a sjou teur du r
Marc he

executer()

executer()

te e urRapid

L re nd eEteind

am pe am pe

Arr t

ma Com

Nous nous occuperons des emplacements restants dans un moment.

P rir Ouv

ma Com

ge or teGara

executer()

executer()

Porte d garag u e Str o Toute s lumi les res Mode group

L re nd eEteind

tila Ven

o re Ste

executer()

executer()

D Re rC gleeSu

te t urEtein

executer()

r me Fer
er St
eo e Eteint

executer()

e ag Po rteGar

Annu ler

(3) Dans la mthode executer(), les actions sont invoques sur le rcepteur.

LInvocateur
arret() marche()

Stereo

vous tes ici

209

implmenter la tlcommande

implmenter la tlcommande
public class Telecommande { Commande[] commandesMarche; Commande[] commandesArret; public Telecommande() { commandesMarche = new Commande[7]; commandesArret = new Commande[7]; Commande pasDeCommande = new PasDeCommande(); for (int i = 0; i < 7; i++) { commandesMarche[i] = pasDeCommande; commandesArret[i] = pasDeCommande; } } public void setCommande(int empt, Commande comMarche, Commande comArret) { commandesMarche[empt] = comMarche; La mthode setCommande() accepte la position commandesArret[empt] = comArret; dun emplacement et les commandes Marche et } public void boutonMarchePresse(int empt) { commandesMarche[empt].executer(); } public void boutonArretPresse(int empt) { commandesArret[empt].executer(); }

grer Cette fois, la tlcommande va , que rt Ar ou e rch Ma sept commandes leaux nous allons mmoriser dans les tab correspondants.
Dans le constructeur, il suffit dinstancier et dinitialiser deux tableaux pour les deux types de commandes.

Arrt qui y seront stockes. Puis elle place ces commandes dans les tableaux correspondants pour quon puisse les utiliser lus tard.
Quand on appuie sur un bouton Marche ou Arrt, le matriel se charge dappeler les mthodes correspondantes: boutonMarchePresse() ou boutonArretPresse().

public String toString() { StringBuffer stringBuff = new StringBuffer(); stringBuff.append(\n------ Tlcommande -------\n); for (int i = 0; i < commandesMarche.length; i++) { stringBuff.append([empt + i + ] + commandesMarche[i].getClass().getName() + + commandesArret[i].getClass().getName() + \n); } return stringBuff.toString(); } Nous avons redfini toStr }

ing() pour afficher chaque emplacement et la commande correspondante. Vous verrez pourquoi quand nous testerons la tlcommande.

210

Chapitre 6

le pattern Commande

Implmenter les Commandes


Bien. Nous nous sommes dj mouill les pieds en implmentant CommandeAllumerLampe pour la TelecommandeSimple. Nous pouvons insrer ce mme code ici et tout marche magnifiquement bien. Les commandes Arrt ne sont pas si diffrentes. En fait, CommandeEteindreLampe ressemble ceci:
public class CommandeEteindreLampe implements Commande { Lampe lampe; public CommandeEteindreLampe(Lampe lampe) { this.lampe = lampe; La } public void executer() { lampe.arret(); } }

CommandeEteindreLampe fonctionne galement de la mme manire que CommandeAllumerLampe, except que nous associons le rcepteur) une action diffrente: la mthode arret().
Stereo
marche() arret() setCd() setDvd() setRadio() setVolume()

Essayons quelque chose dun peu plus difficile! Que diriez-vous dcrire des commandes marche et arrt pour la chane stro? Pour lteindre, cest facile: il suffit dassocier la Stereo la mthode arret() dans la classe CommandeEteindreStereo. En revanche, cest un peu plus compliqu pour lallumer. Disons que nous voulons crire une classe CommandeAllumerStereoAvecCD...
public class CommandeAllumerStereoAvecCD implements Commande { Stereo stereo;

public CommandeAllumerStereoAvecCD(Stereo stereo) { this.stereo = stereo; Tout comme pour } CommandeAllumerLampe, public void executer() { stereo.marche(); stereo.setCD(); stereo.setVolume(11); } }

on passe au ro que constructeur linstance de la st orise mm la nous allons contrler et on ale. dans une variable dinstance loc

Pour satisfaire cette requte, nous devons appeler mthodes sur la stro: dabord lallumer, puis lui diretrois de jouer le CD et enfin rgler le volume 11. Pourquoi11? Eh bien, cest mieux que10, non?

Pas si mal. Jetons un il au reste des classes propritaires: ici et maintenant, vous pouvez faire un sort aux autres classes Commande dont nous avons besoin.

vous tes ici

211

tester la tlcommande

Voyons ce dont la tlcommande est capable


Nous en avons presque termin avec la tlcommande: il ne nous reste plus qu excuter quelques tests et rassembler la documentation pour dcrire lAPI. Ils vont srement tre impressionns chez Maisons de rve SARL., ne croyez-vous pas? Nous avons russi mettre au point une conception qui va leur permettre de produire une tlcommande facile maintenir et ils nauront aucun problme pour convaincre les fournisseurs dcrire lavenir des classes de commandes simples, puisquelles sont si faciles crer. Testons donc ce code!
public class ChargeurTelecommande { public static void main(String[] args) { Telecommande teleCommande = new Telecommande(); Lampe lampeSejour= new Lampe(Sjour); Lampe lampeCuisine = new Lampe(Cuisine); Ventilateur ventilateur= new Ventilateur(Sjour); PorteGarage porteGarage = new PorteGarage(); Stereo stereo = new Stereo(Sjour); CommandeAllumerLampe lampeSejourAllumee = new CommandeAllumerLampe(lampeSejour); CommandeEteindreLampe lampeSejourEteinte = new CommandeEteindreLampe(lampeSejour); CommandeAllumerLampe lampeCuisineAllumee = new CommandeAllumerLampe(lampeCuisine); CommandeEteindreLampe lampeCuisineEteinte = new CommandeEteindreLampe(lampeCuisine); CommandeAllumerVentilateur ventilateurAllume = new CommandeAllumerVentilateur(ventilateur); CommandeEteindreVentilateur ventilateurEteint = new CommandeEteindreVentilateur(ventilateur); CommandeOuvrirPorteGarage porteGarageOuverte = new CommandeOuvrirPorteGarage(porteGarage); CommandeFermerPorteGarage porteGarageFermee = new CommandeFermerPorteGarage(porteGarage); CommandeAllumerStereoAvecCD stereoAvecCD = new CommandeAllumerStereoAvecCD(stereo); CommandeEteindreStereo stereoEteinte = new CommandeEteindreStereo(stereo);

reils Crer tous les appaopri. lemplacement appr

Crer tous les objets de commande des lampes.

s Crer les commande ur le po t Marche et Arr ventilateur.


Crer les commandes Marche et Arrt pour le garage. Crer les commandes Marche et Arrt pour la stro.

212

Chapitre 6

le pattern Commande

teleCommande.setCommande(0, teleCommande.setCommande(1, teleCommande.setCommande(2, teleCommande.setCommande(3,

lampeSejourAllumee, lampeSejourEteinte); lampeCuisineAllumee, lampeCuisineEteinte); ventilateurAllume, ventilateurEteint); stereoAvecCD, stereoEteinte);

System.out.println(teleCommande); teleCommande.boutonMarchePresse(0); teleCommande.boutonArretPresse(0); teleCommande.boutonMarchePresse(1); teleCommande.boutonArretPresse(1); teleCommande.boutonMarchePresse(2); teleCommande.boutonArretPresse(2); teleCommande.boutonMarchePresse(3); teleCommande.boutonArretPresse(3); } }

Maintenant que nous avons toutes nos commandes, nous pouvons les charger dans les emplacements de la tlcommande. Cest ici que nous utilisons la mthode toString() pour afficher chaque emplacement et la commande laquelle il est affect.

Voil, nous sommes prts! Maintenant nous parcourons chaque emplacement et nous pressons son bouton Marche ou Arrt.

Vrifions maintenant lexcution du test de notre tlcommande...


Fichier dition Fentre Aide MesSuperCommandes % java ChargeurTelecommande ------ Tlcommande ------[empt 0] tetepremiere.commande.telecommande.CommandeAllumerLampe tetepremiere.commande.telecommande.CommandeEteindreLampe [empt 1] tetepremiere.commande.telecommande.CommandeAllumerLampe tetepremiere.commande.telecommande.CommandeEteindreLampe [empt 2] tetepremiere.commande.telecommande.CommandeAllumerVentilateur tetepremiere.commande.telecommande.CommandeEteindreVentilateur [empt 3] tetepremiere.commande.telecommande.CommandeAllumerStereoAvecCD tetepremiere.commande.telecommande.CommandeEteindreStereo [empt 4] tetepremiere.commande.telecommande.PasDeCommande tetepremiere.commande.telecommande.PasDeCommande [empt 5] tetepremiere.commande.telecommande.PasDeCommande tetepremiere.commande.telecommande.PasDeCommande [empt 6] tetepremiere.commande.telecommande.PasDeCommande tetepremiere.commande.telecommande.PasDeCommande

Sjour: lampe allume Sjour: lampe teinte Cuisine: lampe allume Cuisine: lampe teinte Sjour: ventilateur sur rapide Sjour: ventilateur arrt Sjour: stro allume Sjour: stro rgle pour le CD Sjour: le volume stro est 11 Sjour: stro teinte %

emplacements Marche

emplacements Arrt Nos commandes en action! Souvenez-vous que ce qui est affich pour chaque appareil vient dune classe du fournisseur. Par exemple, quand un objet Lampe est allume, un message affiche Lampe sjour allume

vous tes ici

213

objet null Attendez une seconde. Quest-ce que cest que ce PasDeCommande qui est charg dans les emplacements quatre six? Un tour votre faon?

Bien vu. Nous avons gliss l un petit quelque chose. Dans la tlcommande, nous ne voulions pas vrifier quune commande tait charge chaque fois que nous rfrencions un emplacement. Par exemple, dans la mthode boutonMarchePresse(), il nous faudrait un fragment de code de ce genre:
public void boutonMarchePresse(int empt) { if (commandesMarche[empt] != null) { commandesMarche[empt].executer(); } }

Alors, comment rsoudre le problme? Implmenter une commande qui ne fait rien!
public class PasDeCommande implements Commande { public void executer() { } }

Ainsi, dans le constructeur de notre Tlcommande, nous affectons chaque emplacement un objet PasDeCommande par dfaut et nous savons que nous avons toujours une commande appeler chaque emplacement.
Command pasDeCommande = new PasDeCommande(); for (int i = 0; i < 7; i++) { commandesMarche[i] = pasDeCommande; commandesArret[i] = pasDeCommande; }

Et dans le rsultat de notre test, vous voyez des emplacements qui ne contiennent rien dautre que lobjet PasDeCommande par dfaut que nous avons affect quand nous avons cr la Tlcommande.

tion Men ble ra hono e Tt ire rem la P

Mention Honorable

Lobjet PasDeCommande est un exemple dobjet null. Un objet null est utile quand il ny a pas dobjet significatif retourner mais que vous ne voulez pas laisser au client la responsabilit de grer null. Par exemple, dans notre tlcommande, nous navions pas dobjets significatifs affecter chaque emplacement du botier et nous avons cr un objet PasDeCommande qui sert de substitut et ne fait rien quand sa mthode executer() est appele. Vous trouverez des utilisations dObjet Null combines de nombreux design patterns, et vous verrez mme que certains auteurs le considrent comme un pattern part entire.

214

Chapitre 6

le pattern Commande

Il est temps de rdiger cette documentation...


Conception de lAPI de la tlcom
domotique. Notre premier objectif n de lAPI de votre tlcommande eptio conc la r ente prs ncessiter vous de ir Nous avons le plais i simple que possible pour ne pas de la tlcommande demeure auss pattern le loy emp s avon nous a t de faire en sorte que le code fin, tte ront de nouvelles classes. ce cre urs nisse four convaincus les mes d som quan s Nou tions de modifica des classes propritaires. uement la classe Telecommande s de maintenance. cot les ent alem radic t Commande pour dcoupler logiq nuan dimi en uction de la tlcommande tout que cela rduira les cots de prod notre conception: de ble sem den s fournit une vue Le diagramme de classes ci-dessou
mble dobjets La Telecommande gre un ense uon presse un Commande, un par bouton. Lorsq ) est appele, esse( onPr Bout ode mth la on, bout ) sur la uter( exec ode mth la lle ce qui appe tlcommande commande. Cest tout ce que la puisque lobjet sait des classes quelle invoque, es qui effec tuent Commande la dcouple des class rellement lautomatisation.

mande pour Maisons de rve, SAR

Le ChargeurTelecommande cre un certain nombre dobjets Commande qui sont chargs dans les emplacements de la Tlcommande. Chaque objet de commande encapsule une requte un quipement mnager automatis.

Toutes les commandes de la Telecommande implmentent linterface Commande qui ne contient quune seule mthode: executer(). Les Commandes encapsulent un ensemble dactions sur une classe propritaire spcifique. La tlcommande invoque ces actions en appelant la mthode executer().

ChargeurTelecommande

Telecommande
commandesMarche commandesArret setCommande() boutonMarchePresse() boutonArretPresse()

<<interface>> Commande
executer()

Lampe
marche() arret()

CommandeAllumerLampe
execute()

CommandeEteindreLampe
executer()
public void executer() { } lampe.marche() public void executer() {

lampe.arret() }

isseurs Nous utilisons les classes des fourn pour excuter les vraies fonctions dautomatisation et de contrle des ent appareils. Voici par exemple comm nous employons la classe Lampe.

ue action pouvant Grce linterface Commande, chaq on de la tlcommande tre invoque en pressant un bout simple objet Commande. est implmente au moyen dun rfrence un objet qui est une ent conti ande Comm et Lobj re et implmente une ritai prop e class dune nce une insta ou plusieurs mthodes une lle appe qui ter() execu mthode ici deux classes sent repr s avon Nous sur cet objet. er et teindre une lampe. destines respectivement allum

vous tes ici

215

noubliez pas Annuler

Beau travail. On dirait que vous avez mis au point une conception fabuleuse. Mais est-ce que vous nauriez pas oubli une petite demande du client? PAR EXEMPLE LE BOUTON ANNULER!!!!

Oups! Nous avons failli oublier... Heureusement, une fois que nous avons nos classes Commande de base, la fonction dannulation est facile insrer. Voyons les tapes pour lajouter nos commandes et la tlcommande...

Quallons-nous faire?
Nous devons ajouter une fonctionnalit qui prendra en charge le bouton Annuler de la tlcommande. En voil le principe. Disons que la lampe du sjour est teinte et que vous appuyez sur le bouton de la tlcommande. De toute vidence, la lumire sallume. Maintenant, si vous appuyez sur le bouton Annuler, la dernire commande est inverse en loccurrence la lumire steint. Avant de rentrer dans des exemples plus complexes, manipulons la lampe avec le bouton Annuler:
1

Quand les commandes peuvent tre inverses, elles possdent une commande annuler() qui est lhomologue de la mthode executer(). Quelle que soit la dernire action dexecuter(), annuler() linverse. Avant dajouter une fonction dannulation nos commandes, nous devons donc insrer une mthode annuler() dans linterface Commande :
public interface Command { public void executer(); public void annuler(); }

Voici la nouvelle mthode annuler().

Lenfance de lart! Lanons-nous maintenant dans la commande de la Lampe et implmentons la mthode annuler().

216

Chapitre 6

le pattern Commande

Commenons par CommandeAllumerLampe. Si la mthode executer() de CommandeAllumerLampe a t appele, alors la mthode marche() a t appele en dernier. Nous savons quannuler() doit faire loppos en appelant la mthode arret().
public class CommandeAllumerLampe implements Commande { Lampe lampe; public CommandeAllumerLampe(Lampe lampe) { this.lampe = lampe; } public void executer() { lampe.marche(); } public void annuler() { lampe.arret(); } }

allume executer() et, par consquent, la lumire, teint. annuler() l

Du gteau! Maintenant passons CommandeEteindreLampe. Ici, la mthode annuler() doit simplement appeler la mthode marche() de Lampe.

public class CommandeEteindreLampe implements Commande { Lampe lampe; public CommandeEteindreLampe(Lampe lampe) { this.lampe = lampe; } public void executer() { lampe.arret(); } public void annuler() { lampe.marche(); } }

ler() Et ici, annulumire! rallume la

Quoi de plus facile? Daccord, nous navons pas fini: il nous reste un peu de travail sur la Tlecommande pour grer la mmorisation du dernier bouton press et le bouton Annuler.
vous tes ici

217

implmenter lannulation

Pour ajouter la prise en charge du bouton Annuler, il suffit dapporter quelques modifications la classe Tlcommande. Voici comment nous allons procder: nous allons insrer une nouvelle variable dinstance pour mmoriser la dernire commande invoque, puis, chaque appui sur le bouton Annuler, nous extrairons cette commande et nous appellerons sa mthode annuler().

public class TelecommandeAvecAnnul { Commande[] commandesMarche; Commande[] commandesArret; Commande commandeAnnulation; public TelecommandeAvecAnnul() { commandesMarche = new Commande[7]; commandesArret = new Commande[7];

la dernire Cest ici que nous mmorisons du bouton Annuler. commande excute lusage

Commande pasDeCommande = new PasDeCommande(); for(int i=0;i<7;i++) { commandesMarche[i] = pasDeCommande; commandesArret[i] = pasDeCommande; } commandeAnnulation = pasDeCommande; }

Tout comme les autres emplacements, Annuler est initialis PasDeCommande: si on appuie dessus avant tout autre bouton, il ne fait strictement rien.

public void setCommande(int empt, Commande comMarche, Commande comArret) { commandesMarche[empt] = comMarche; Quand un bouton est press, nous commandesArret[empt] = comArret; } prenons la commande et nous public void boutonMarchePresse(int empt) { commandesMarche[empt].executer(); commandeAnnulation = commandesMarche[empt]; } public void boutonArretPresse(int empt) { commandesArret[empt].executer(); commandeAnnulation = commandesArret[empt]; } public void boutonAnnulPresse() { commandeAnnulation.annuler(); } public String toString() { // code de toString... } }

commenons par lexcuter; puis nous sauvegardons une rfrence celle-ci dans la variable dinstance commandeAnnulation. Nous procdons ainsi pour les commandes marche et pour les commandes arrt.

Quand le bouton Annuler est press, nous invoquons la mthode annuler() de la commande mmorise dans commandeAnnulation. Ceci inverse leffet de la dernire commande excute.

218

Chapitre 6

le pattern Commande

Il est temps de tester le bouton Annuler!


Retravaillons un peu le test en prenant en compte le bouton Annuler:
public class ChargeurTelecommande { public static void main(String[] args) { TelecommandeAvecAnnul teleCommande = new TelecommandeAvecAnnul(); Lampe lampeSejour= new Lampe(Sjour); CommandeAllumerLampe lampeSejourAllumee = new CommandeAllumerLampe(lampeSejour);

velles Crer une Lampe et nos nouprenant en t mthodes Marche et arr charge lannulation.

CommandeEteindreLampe lampeSejourEteinte = new CommandeEteindreLampe(lampeSejour); teleCommande.setCommande(0, lampeSejourAllumee, lampeSejourEteinte); teleCommande.boutonMarchePresse(0); teleCommande.boutonArretPresse(0); System.out.println(teleCommande); teleCommande.boutonAnnulPresse(); teleCommande.boutonArretPresse(0); teleCommande.boutonMarchePresse(0); System.out.println(teleCommande); teleCommande.boutonAnnulPresse(); } }

Ajouter les commandes de la lampe lemplacement0. Allumer la lampe, puis teindre, puis annuler.. puis teindre de nouveau la lampe, la rallumer et annuler.

Et voici les rsultats du test...


Fichier dition Fentre Aide DefierLEntropie

% java ChargeurTelecommande Lumire allume Allumer Lumire teinte


-----[empt [empt [empt

la lampe, puis lteindre.

Voici les commandes de la Lampe

Tlcommande ------0] tetepremiere.commande.annulation.CommandeAllumerLampe tetepremiere.commande.annulation.CommandeEteindreLampe 1] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande 2] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande

[empt 3] tetepremiere.commande.annulation.PasDeCommande [empt 4] tetepremiere.commande.annulation.PasDeCommande

[empt 5] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande [empt 6] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande [undo] tetepremiere.commande.annulation.CommandeEteindreLampe

Lumire allume Lumire teinte Lumire allume ------ Tlcommande -------

Puis nous lteignons et nous la rallumons.

Annuler a t press... la mthode annuler() de CommandeEteindreLampe rallume la lumire.

Maintenant, Annuler dtient CommandeEteindreLampe, la dernire commande invoque.

[empt 0] tetepremiere.commande.annulation.CommandeAllumerLampe tetepremiere.commande.annulation.CommandeEteindreLampe [empt 1] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande [empt 2] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande [empt 3] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande [empt 4] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande [empt 5] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande [empt 6] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande [undo] tetepremiere.commande.annulation.CommandeAllumerLampe

Lumire teinte

Annuler a t press, la lumire steint de nouveau.

Maintenant, Annuler dtient CommandeAllumerLampe, la dernire commande invoque.


vous tes ici

219

nous devons conserver ltat de lannulation

Utiliser un tat pour implmenter Annuler


Limplmentation dune annulation pour la Lampe tait instructif mais un peu trop facile. En gnral, il faut grer un tat pour implmenter une annulation. Essayons quelque chose dun peu plus intressant, comme la classe propritaire Ventilateur. Celle-ci dispose de trois mthodes pour rgler la vitesse et dune mthode pour arrter lappareil. Voici le code source du Ventilateur:
Ventilateur
apide() moyen() lent() arret() getVitesse()

con public class Ventilateur { e Ventilateurde lappareil. ss la c la ue q public static final int RAPIDE = 3; Remarquez reprsente la vitesse public static final int MOYEN = 2; tat local qui public static final int LENT = 1; public static final int ARRET = 0; String localisation; int vitesse; Mmm, public Ventilateur(String localisation) { alors pour implmenter this.localisation = localisation; correctement Annuler, je vitesse = ARRET; prendre en compte devrais } vitesse du dernire la public void rapide() { ventilateur... vitesse = RAPIDE; // code pour rgler la vitesse sur rapide }
public void moyen() { vitesse = MOYEN; // code pour rgler la vitesse sur moyen } public void lent() { vitesse = LENT; // code pour rgler la vitesse sur lent } public void arret() { vitesse = ARRET; // code pour arrter le ventilateur } public int getVitesse() { return vitesse; } }

tient un

Ces mthodes permettent de rgler la vitesse du ventilateur.

La mthode us permet getVitesse()notesse dobtenir la vintilateur. actuelle du ve

220

Chapitre 6

le pattern Commande

Ajouter lannulation aux commandes du ventilateur


Attaquons maintenant lajout dune annulation aux diffrentes commandes du Ventilateur. Pour ce faire, nous devons mmoriser le dernier rglage du ventilateur et, si la mthode annuler() est appele, restaurer le rglage prcdent. Voici le code de CommandeVentilateurRapide :

public class CommandeVentilateurRapide implements Commande { Ventilateur ventilateur; int derniereVitesse; public CommandeVentilateurRapide(Ventilateur ventilateur) this.ventilateur = ventilateur; } public void executer() { derniereVitesse = ventilateur.getVitesse(); ventilateur.rapide(); } public void annuler() { if (derniereVitesse == Ventilateur.RAPIDE) { ventilateur.rapide(); } else if (derniereVitesse == Ventilateur.MOYEN) { ventilateur.moyen(); } else if (derniereVitesse == Ventilateur.LENT) { ventilateur.lent(); } else if (derniereVitesse == Ventilateur.ARRET) { ventilateur.arret(); } } }

t un Nous avons ajou mmoriser tat local pouresse du la dernire vit { ventilateur.

Dans executer(), avant de modifier la vitesse du ventilateur, nous devons dabord enregistrer son tat prcdent, juste au cas o nous devrions annuler nos actions.

Pour annuler, nous redonnons au ventilateur sa vitesse prcdente

Musclez vos neurones

Il nous reste trois commandes crire: lent, moyen et arrt. Voyez-vous comment elles sont implmentes?

vous tes ici

221

tester le ventilateur

Prparons-nous tester le ventilateur


Il est temps de charger dans notre tlcommande les commandes du ventilateur. Nous allons charger le rglage moyen dans le bouton Marche de lemplacement zro et le rglage rapide dans celui de lemplacement un. Les deux boutons Arrt correspondants contiendront la commande darrt du ventilateur. Voici notre script de test:

Vent il Moy ateur en Vent il Rapid ateur e

Marc he

Arr t

Annu ler

public class ChargeurTelecommande { public static void main(String[] args) { TelecommandeAvecAnnul teleCommande = new TelecommandeAvecAnnul(); Ventilateur ventilateur = new Ventilateur(Sjour); CommandeVentilateurMoyen ventilMoyen = new CommandeVentilateurMoyen(ventilateur); CommandeVentilateurRapide ventilRapide = new CommandeVentilateurRapide(ventilateur); CommandeEteindreVentilateur ventilateurEteint = new CommandeEteindreVentilateur(ventilateur);

Ici, nous instancions trois commandes: rapide, moyen et teint. L, nous affectons moyen lemplacement zro et rapide lemplacement un. Nous chargeons galement les commandes darrt.

teleCommande.setCommande(0, ventilMoyen, ventilateurEteint); teleCommande.setCommande(1, ventilRapide, ventilateurEteint); teleCommande.boutonMarchePresse(0); teleCommande.boutonArretPresse(0); System.out.println(teleCommande); teleCommande.boutonAnnulPresse(); teleCommande.boutonMarchePresse(1); System.out.println(teleCommande); teleCommande.boutonAnnulPresse(); } }

en. Dabord, allumer le ventilateur sur moy Ensuite lteindre. Annuler! Il doit revenir moyen... Cette fois, le rallumer sur rapide. en. On annule encore: il doit revenir moy

222

Chapitre 6

le pattern Commande

Tester le ventilateur...
Bien, allumons la tlcommande, chargeons-y quelques commandes et appuyons sur quelques boutons!

Fichier Edition Fentre Aide DfaireTout!

% java ChargeurTelecommande Ventilateur sur moyen Ventilateur teint

Allumer le ventilateur sur moyen, puis lteindre.

Voici les commandes de la tlcommande...

...et Annuler contient la dernire commande excute, CommandeEteindreVentilateur.

------- Tlcommande ------[empt 0] tetepremiere.commande.annulation.CommandeVentilateurMoyen tetepremiere.commande.annulation.CommandeEteindreVentilateur [empt 1] tetepremiere.commande.annulation.CommandeVentilateurRapide tetepremiere.commande.annulation.CommandeEteindreVentilateur [empt 2] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande [empt 3] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande [empt 4] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande [empt 5] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande [empt 6] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande [annulation] tetepremiere.commande.annulation.CommandeEteindreVentilateur

Ventilateur sur moyen Ventilateur sur rapide

On annule la dernire commande: il revient moyen. Maintenant, on le rgle sur rapide.

------ Tlcommande ------[empt 0] tetepremiere.commande.annulation.CommandeVentilateurMoyen tetepremiere.commande.annulation.CommandeEteindreVentilateur [empt 1] tetepremiere.commande.annulation.CommandeVentilateurRapide tetepremiere.commande.annulation.CommandeEteindreVentilateur [empt 2] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande [empt 3] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande [empt 4] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande [empt 5] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande [empt 6] tetepremiere.commande.annulation.PasDeCommande tetepremiere.commande.annulation.PasDeCommande [annulation] tetepremiere.commande.annulation.CommandeVentilateurRapide

Ventilateur sur moyen %

Maintenant, rapide est la dernire commande excute.

Encore une annulation et le ventilateur revient la vitesse moyenne.

vous tes ici

223

macro-commandes

Toute tlcommande a besoin dun mode group!


quoi sert de possder une tlcommande si on ne peut pas appuyer sur un bouton qui allume les lampes, allume la tl et la stro pour jouer un DVD et fait chauffer le jacuzzi?

Stereo
marche() arret() setCd() setDvd() setRadio() setVolume()
marche() arret() selectionnerCanal() setVolume()

TV

Jacuzzi
marche() arret() bouillonner() gicleursMarche() gicleursArret() setTemperature()
marche() arret() attenuer()

Lampe

Mmm, notre tlcommande devrait avoir un bouton pour chaque appareil. Je ne crois pas que ce soit possible.

Pas si vite, Anne, ce nest pas si sr. Je pense quon peut le faire sans rien changer la tlcommande!

Lide de Marie consiste crer une nouvelle sorte de Commande qui peut excuter dautres Commandes... et plus dune! Joliment bonne ide, non?

public class MacroCommande implements Commande { Commande[] commandes; public MacroCommande(Commande[] commandes) { this.commandes = commandes; } public void executer() { for (int i = 0; i < commandes.length; i++) { commandes[i].executer(); } } Quan

On prend le tableau de Commandes et on les stocke dans la MacroCommande.

d la tlcommande excute la macro, ces commandes sont toutes excutes une une.

224

Chapitre 6

le pattern Commande

Utiliser une macro-commande


Voyons les tapes de lutilisation dune macro-commande:
1

Nous commenons par crer le jeu de commandes que nous voulons placer dans la macro:

Lampe lampe = new Lampe(Sjour); TV tv = new TV(Sjour); Stereo stereo = new Stereo(Sjour); Jacuzzi jacuzzi = new Jacuzzi();

CommandeAllumerLampe lampeAllumee = new CommandeAllumerLampe(lampe); CommandeAllumerStereo stereoAllumee = new CommandeAllumerStereo(stereo); CommandeAllumerTV tvAllumee = new CommandeAllumerTV(tv); CommandeAllumerJacuzzi jacuzziAllume = new CommandeAllumerJacuzzi(jacuzzi);

Crer tous les quipements: une lampe, une tl, une stro et un jacuzzi. Maintenant, crer toutes les commandes Marche pour les contrler.

vos crayons Sharpen your pencil

Il nous faut galement des commandes pour les boutons Arrt. crivez ici le code de ceux-ci:

Puis nous crons deux tableaux, un pour les commandes Marche et un pour les commandes Arrt, et on y charge les commandes correspondantes:

Crer un tableau pour les commandes Marche et un pour les commandes Arrt...
les deux macros correspondantes pour les contenir.

Commande[] allumageGroupe = { lampeAllumee, stereoAllumee, tvAllumee, jacuzziAllume}; Commande[] extinctionGroupe = { lampeEteinte, stereoEteinte, tvEteinte, jacuzziEteint}; MacroCommande macroAllumageGroupe = new MacroCommande(allumageGroupe); ...et crer MacroCommande macroExtinctionGroupe = new MacroCommande(extinctionGroupe);

Ensuite, nous affectons MacroCommande un bouton, comme nous le faisons toujours:

teleCommande.setCommande(0, macroAllumageGroupe, macroExtinctionGroupe);

Affecter la macrocommande un bouton comme nous le ferions pour tout autre commande.
225

vous tes ici

exercice sur les macro-commandes

Enfin, il suffit dappuyer sur les boutons et de voir si cela fonctionne.


System.out.println(teleCommande); System.out.println(---Excution de Macro Marche ---); teleCommande.boutonMarchePresse(0); System.out.println(--- Excution de Macro Arret ---); teleCommande.boutonArretPresse(0);

Voici le rsultat

Fichier Edition Fentre Aide BeBopALulla

% java ChargeurTelecommande
------ Telecommande ------[empt 0] tetepremiere.commande.groupe.MacroCommande [empt 1] tetepremiere.commande.groupe.PasDeCommande [empt 2] tetepremiere.commande.groupe.PasDeCommande [empt 3] tetepremiere.commande.groupe.PasDeCommande [empt 4] tetepremiere.commande.groupe.PasDeCommande [empt 5] tetepremiere.commande.groupe.PasDeCommande [empt 6] tetepremiere.commande.groupe.PasDeCommande [undo] tetepremiere.commande.groupe.PasDeCommande

Voil les deux macro-commandes.


tetepremiere.commande.groupe.MacroCommande tetepremiere.commande.groupe.PasDeCommande tetepremiere.commande.groupe.PasDeCommande tetepremiere.commande.groupe.PasDeCommande tetepremiere.commande.groupe.PasDeCommande tetepremiere.commande.groupe.PasDeCommande tetepremiere.commande.groupe.PasDeCommande

--- Excution de Macro Marche --Sjour: lumire allume Sjour: stro allume Sjour: la tl est allume Sjour: le canal est positionn sur VCR Le jacuzzi chauffe 40 Le jaccuzi bouillonne !

Toutes les commandes sont excutes quand nous appelons la macro Marche

--- Excution de Macro Arrt --Sjour: lumire teinte Sjour: stro teinte Sjour: la tl est teinte Le jaccuzi refroidit 36 %

... et de mme quand nous invoquons la macro Arret. Observez le fonctionnement.

226

Chapitre 6

le pattern Commande

Exercice

Il ne manque rien notre MacroCommande sauf une fonctionnalit dannulation. Quand on appuie sur le bouton Annuler aprs lexcution dune macro-commande, toutes les commandes que la macro a invoques doivent dfaire leurs actions prcdentes. Voici le code de MacroCommande. Continuez et implmentez la mthode annuler():
public class MacroCommande implements Commande { Commande[] commandes; public MacroCommande(Commande[] commandes) { this.commandes = commandes; } public void executer() { for (int i = 0; i < commandes.length; i++) { commandes[i].executer(); } } public void annuler() {

} }

Q: R:

de questions stupides

Il ny a pas

Est-ce que jai toujours besoin dun rcepteur? Pourquoi lobjet de commande ne peut-il pas implmenter les dtails de la mthode executer()?

Q:

En gnral, nous nous efforons davoir des objets de commande passifs qui invoquent simplement une action sur un rcepteur, mais il existe de nombreux exemples dobjets de commande intelligents qui implmentent la plus grande partie, sinon la totalit, de la logique ncessaire pour rpondre une requte. Bien sr, vous pouvez le faire. Souvenez-vous seulement que vous naurez plus le mme niveau de dcouplage entre linvocateur et le rcepteur et que vous ne pourrez plus paramtrer vos commandes avec des rcepteurs.

Comment puis-je implmenter un historique des oprations dannulation? Autrement dit, que faire si je veux pouvoir appuyer sur le bouton Annuler plusieurs fois?

Q: R:

Est-ce que je pourrais me contenter dimplmenter le mode group comme une Commande en crant une CommandeGroupe et en plaant les appels pour excuter les autres Commandes dans la mthode executer() de CommandeGroupe?

Excellente question! Cest assez facile en ralit. Au lieu de conserver seulement une rfrence la dernire Commande excute, vous mmorisez une pile de commandes. Puis, chaque appui sur le bouton Annuler, votre invocateur dpile le premier lment et appelle sa mthode annuler().

Oui, mais cela reviendrait en substance coder en dur le mode group dans CommandeGroupe. Pourquoi chercher les ennuis? Avec MacroCommande, vous pouvez dcider dynamiquement quelles Commandes vous voulez placer dans la CommandeGroupe, et vous disposez de plus de souplesse pour excuter des MacroCommandes. En gnral, MacroCommande constitue une solution plus lgante et exige dcrire moins de code. vous tes ici

R:

227

mettre en file les requtes

Autre utilisation du pattern Commande: files de requtes


Le pattern Commande nous fournit un moyen de grouper lintgralit dun traitement (un rcepteur et un ensemble dactions) et de le transmettre comme un objet standard. Le traitement lui-mme peut tre invoqu bien aprs quune application cliente a cr lobjet de commande. En fait, il peut mme tre invoqu par un thread diffrent. Nous pouvons nous inspirer de ce scnario pour de nombreuses applications utiles comme des ordonnanceurs, des pools de threads et des Les files de tches, pour nen citer que quelques unes. Imaginez une file de tches. Vous ajoutez des commandes une extrmit de la file et un groupe de threads attend lautre extrmit. Les threads excutent le script suivant: ils retirent une commande de la file, appellent sa mthode executer(), attendent que lappel se termine puis se dbarrassent de lobjet de commande et en extraient un nouveau.
e Trait

Commandes
executer()

objets implmentant ande sont linterface Comm ajouts la file.


lc Ca
executer()

te Tlc

Co mmand

executer()

alc

executer()

cu lF

ulNum

inancie

ul Nu

executer()

p il a te u r

executer()

executer()

lFi nanci

er

lc harg

executer()

cu lN

umeriq

heRse

executer()

me executer() pila ur te nt Dis tribu

Fi le

c de t

he s

executer()

cu lF

inancie

au

Les threads retirent les commandes de la file une par une et appellent leur mthode executer(). Une fois quils ont termin, ils traitent un nouvel objet de commande.

Co m p il a t

eu r

ue

Cette technique nous offre un moyen efficace de limiter le traitement un nombre fixe de threads.

em

ent

merique

executer()

rch R s e

executer()

T l c h a

Threads traitant des tches


Notez que les classes de la file de tches sont totalement dcouples des objets qui effectuent le traitement. Un thread peut excuter un calcul financier pendant une minute et tlcharger quelque chose la minute suivante. Les objets de la file ne sen soucient pas: ils se bornent extraire les commandes et appeler executer(). De mme, tant que vous placerez dans la file des objets qui implmentent le pattern Commande, votre mthode executer() sera invoque quand un thread sera disponible. 228
Chapitre 6

T h re a d

Musclez vos neurones

Comment un serveur web pourrait-il utiliser une telle file? quelles autres applications pourrait-on penser?

rg em

T h re a d

T h re a d

executer() execute()

e nt

ul Nu

merique

T h re a d
e t qu Re

ea u

er iq

lc harg

em

ent

executer()

ha rg e ment

Re

qu

executer()

ue

ut Req he Tc

eT
m Co

l Ca

ut Req
l cu Ca

eT

l Ca

rc he Rec

he Tc

m Co

l Ca

c T

he

Re

e ch

lc Ca

le pattern Commande

Autre utilisation du pattern Commande: journalisation des requtes


La smantique de certaines applications ncessite de journaliser toutes les actions et de pouvoir les invoquer afin de raliser une reprise sur incident. Le pattern Commande peut prendre en charge cette smantique en ajoutant deux mthodes: stocker() et charger(). En Java, nous pouvons utiliser la srialisation pour implmenter ces mthodes, mais les rserves habituelles sur lutilisation de la srialisation pour la persistance continuent sappliquer. Comment cette technique fonctionne-t-elle? mesure que nous excutons les commandes, nous en stockons lhistorique sur disque. Lorsquune panne se produit, nous rechargeons les objets de commande et nous invoquons leurs mthodes executer() en un seul lot et dans lordre. Ce type de journalisation naurait aucun sens pour une tlcommande. Toutefois, il existe de nombreuses applications qui invoquent des actions sur de gros volumes de donnes qui ne peuvent tre sauvegards rapidement chaque fois quune modification est apporte. Avec un mcanisme de journalisation, nous pouvons sauvegarder toutes les oprations postrieures au dernier point de contrle, et, sil y a une panne systme, appliquer ces oprations partir du point de contrle. Prenons lexemple dun tableur: nous pourrions implmenter la rcupration sur panne en journalisant les actions dans la feuille de calcul au lieu dcrire une copie de celle-ci sur disque chaque modification. Dans des applications plus avances, ces techniques peuvent tre tendues pour appliquer des ensembles doprations de manire transactionnelle, de sorte que toutes les oprations se terminent ou aucune.
executer() stocker() charger()

executer() annuler() stocker() charger()

Commande

<<interface

>>

Nous ajoutons deux mthodes pour la journalisation.

store
re sto
sto re

r() ute xec 1. e

om m

ande

un e

2. executer()

In vocateu

de

3. ex ec ute r()

Co mmande
executer() stocker() charger()

ux

executer() stocker() charger()

Crash!
om m
andetr
ois

Chaque commande est stocke sur disque mesure quelle est excute.

Restauration

Aprs une panne systme, les objets sont rechargs et excuts dans lordre appropri.

ande

un e

cha rge r

Co mmande
executer() stocker() charger()

de

ux
r() ute xec 3. e

om m

andetr

ois

vous tes ici

r rge cha

executer() stocker() charger()

om m

C
C

charger
executer() stocker() charger()

1. ex ec ute r()
2. executer()

In vocateu

229

votre bote outils de concepteur

Votre bote outils de concepteur


Votre bote outils commence salourdir! Dans ce chapitre, nous lui avons ajout un pattern qui nous permet dencapsuler des mthodes dans des objets de commande, de les mmoriser, de les transmettre et de les invoquer quand nous en avons besoin

POINTS DIMPACT BULLET POINTS

Le pattern Commande dcouple


un objet mettant une requte de celui qui sait comment lexcuter.

Abstraction n Encapsulatio e. ri va ui q ce e Encapsulez Polymorphism ritage. lh n io t la ncapsu Prfrez le ge es, non desHrita
des interfac Programmez ions. implmentat t r faiblemen us de couplent. vo z ce or f Ef ui interagisse les objets q ouvertes doivent trees la s se as cl es L mais ferm lextensionn. io modificat ions. Ne . es abstract Dpendez d s des classes concrtes dpendez pa

Principes OO

Bases de lOO

Un objet Commande est au


centre du mcanisme de dcouplage et encapsule un rcepteur avec une action (ou un ensemble dactions).

Un invocateur requiert un objet


Commande en appelant sa mthode executer(), laquelle invoque ces actions sur le rcepteur.

Les invocateurs peuvent tre


paramtrs par des Commandes, mme dynamiquement lors de lexcution.

O Patterns O lation ille une ree fa it ef inm init-und esurs,

soin de Quand vous avezjebe mettant dcoupler un ob s t objets qui des requtes de sa tisfaire ces savent commentuez le pattern requtes, appliq Commande.

Les Commandes peuvent


prendre en charge lannulation en implmentant une mthode annuler() qui restitue lobjet ltat qui tait le sien avant le dernier appel de la mthode executer().

f e dsi de n uc unr pulu farcf c -e re an ee h t ie hu tn ct a a v in tg t lep r ab e e su n ae s g s a Str y c a it t s. O e ace he ir in t cn le ep a r b in d t s,tes u f t a e e n e D je g m e b n u h a a o m je it h r b n it r l n c o n o u in r p io e c e e lg p t e t f r . is u a u a t eF sd D d enD t e sq urec ic m nm dil sanlg r d r ,n e,a n iq l lo t eu n b e d rq m n sse na , in n a it m je io e e e a sna hn b p t o le b it is a y r a a n d s d u r e o e b ux ia o nd p iuix ue je ft uo sla sa io u u b qf ded qt F t jo ere ot t q sit x eic a n u r m u n e c e o r is e t d c h e d li n m c la p s n c a tmme u r t t r io s le ie o a e u n e e g t d o s j d g ie poeinco p e a , b t n r t ix n o c n a n u io la et heuor t cso n tss c la mn Str re t t ut n sso esqu r da e le a ue le un oulu rtso i oe d seca un g s rm so o psa e n if eit f in p ss t e x ul u S ot t p la n ps am n c u d io t t e in t t la se c en n a n m e e ic ie a r ie so c r is m e n b st e p e x r la a a vare u u in e d nIl n . a F st n p t iq io ge . le a t nse t r r. Far t niv e ra . a era m ie a m tt sa ic n t r e m r nc is m .r ioin p ect au b nin o tla la iq u C st e le ia tn pa q a lai e ns le a aic b ie onm sut d st c ns n te lo ea lut n n g in io a ss e ailuis l t t e t st p r ia ce an lt y e c in is u n t a r a or g e u t s st d l e oje ss , audlguer lin requtes, files etd cs la d a ep ob d ss un lacn cio riv et es e t d ucna .. d en e s ssss t lala ca li n nue u es. nsss.par diffr es, et de io nte c ie c clla es d fon stso ss ifs de requt de c at sul u t pi so ca s e r d rations. e et
dattent t des op la rversibili t an t et rm plus, pe

Les MacroCommandes sont


une simple extension de Commande qui permet dinvoquer plusieurs commandes. Les macro-commandes supportent galement lannulation.

Dans la pratique, il nest pas


rare que des objets Commande intelligents senvoient implmentent eux-mmes le traitement de la requte au lieu de la dlguer un rcepteur.

On peut galement utiliser des


230 Commandes pour implmenter des mcanismes de journalisation ou des systmes transactionnels.

Chapitre 6

le pattern Commande

Il est temps de souffler un peu et de prendre le temps dassimiler. Un autre mots-croiss; toutes les solutions sont dans ce chapitre.

1 3 4 5 6

7 8

10

11

12

13

14

15

Horizontalement 3. Notre ville prfre. 9. Objet qui sait comment faire les choses. 10. Objectville, elles sont automatises. 11. Le personnage de la premire page de ce chapitre en est un. 12. Objet ________ mrite une mention honorable. 13. Notre langage favori. 14. Dcouple du chef. 15. Ni serveuse, ni serveur.

Verticalement 1. Sujet de ce chapitre. 2. Encapsule dans une commande. 4. Peut bouillonner. 5. Ce que Maisons de Rve vous demande dimplmenter. 6. Commande sert lencapsuler. 7. La serveuse et le chef y travaillent. 8. Commande permet den implmenter une.

vous tes ici

231

solutions des exercices

Qui fait quoi ?


g
pattern Commande Commande executer() Client Invocateur Recepteur setCommande()

Solutions des exercices

Reliez les objets et les mthodes de la Caftria leurs homologues dans le pattern Commande.

Caftria Serveuse Cuisinier faireMarcher() Commande Client prendreCommande()

Sharpen your pencil vos crayons


public class CommandeOuvrirPorteGarage implements Commande { PorteGarage porteGarage; public CommandeOuvrirPorteGarage(PorteGarage porteGarage) { this.porteGarage = porteGarage; } public void executer() { porteGarage.ouvrir(); } }
Fichier Edition Fentre Aide SandwichAuJambon

%java TestTelecommande Lumire allume Porte garage ouverte %

232

Chapitre 6

le pattern Commande

crivez la mthode annuler() pour MacroCommande

Exercice

public class MacroCommande implements Commande { Commande[] commandes; public MacroCommande(Commande[] commandes) { this.commandes = commandes; } public void executer() { for (int i = 0; i < commandes.length; i++) { commandes[i].executer(); } } public void annuler() { for (int i = 0; i < commandes.length; i++) { commandes[i].annuler(); } } }

Solutions des exercices

Sharpen your pencil vos crayons

CommandeEteindreLampe lampeEteinte = new CommandeEteindreLampe(lampe); CommandeEteindreStereo stereoEteinte = new CommandeEteindreStereo(stereo); CommandeEteindreTV tvEteinte = CommandeEteindreTV (tv); CommandeEteindrejacuzzi jacuzziEteint = new CommandeEteindrejacuzzi (jacuzzi);

1 3

C O B
4

R
7

T E L E

E Q U E T C
8

M A N D E

A C U Z Z I

N V O C A T A I O
12 9

A F E C E P T E E

N N U L A T I O R

C O M
10

M A

S U

O L

N
13

S J A V

R I A

11

N D

14

15

vous tes ici

233

7 les patterns Adaptateur et Faade

Savoir sadapterg
Cest ce qui est beau dans notre mtier. Nous crons lillusion de la ralit!

Croyez-vous vraiment que les lecteurs vont croire quon assiste une course de chevaux alors quon est en train de poser dans un studio?

Vous voulez dire que ce nest pas un match de foot?

Je me sens un autre homme dans ce manteau!

Dans ce chapitre, nous allons entreprendre des choses impossibles, comme faire entrer une cheville ronde dans un trou carr. Cela vous semble impossible? Plus maintenant avec les design patterns. Vous
souvenez-vous du pattern Dcorateur? Nous avons envelopp des objets pour leur attribuer de nouvelles responsabilits. Nous allons recommencer, mais cette fois avec un objectif diffrent: faire ressembler leurs interfaces quelque chose quelles ne sont pas. Pourquoi donc? Pour pouvoir adapter une conception qui attend une interface donne une classe qui implmente une interface diffrente. Et ce nest pas tout. Pendant que nous y sommes, nous allons tudier un autre pattern qui enveloppe des objets pour simplifier leur interface. nouveau chapitre

235

le monde est plein dadaptateurs

Nous sommes entours dadaptateurs


Vous nprouverez aucune difficult comprendre ce quest un adaptateur OO parce que le monde rel en est plein. Prenons un exemple. Avez-vous dj eu besoin dutiliser dans un pays dEurope un ordinateur portable fabriqu aux tats-Unis? Si oui, vous avez sans doute eu besoin dun adaptateur CA...

Prise murale europenne Adaptateur CA Fiche CA US

ose une enne exp ant. p o r u e murale u cour La prise e pour obtenir d interfac

Le portable sattend une autre interface

Ladaptateur convertit une interface en une autre


du teurs ? a t p a es ad ter dautr vez-vous ci n e i b Com rel pou monde

Vous savez comment ladaptateur opre: il se place entre la fiche du portable et la prise europenne et sa tche consiste sadapter celle-ci pour que vous puissiez y brancher le portable et recevoir du courant. Ou, pour voir le problme autrement, ladaptateur transforme linterface de la prise pour offrir celle que le portable attend. Certains adaptateurs CA sont simples: ils se bornent modifier la forme de la prise pour quelle corresponde celle de votre fiche et laissent passer directement le courant alternatif. Dautres sont plus complexes et peuvent augmenter ou diminuer le voltage en fonction des besoins de votre machine. Voil pour le monde rel, mais quen est-il des adaptateurs orients objet? Eh bien nos adaptateurs OO jouent le mme rle que leurs homologues du monde rel: ils prennent une interface et ladaptent de manire ce quelle corresponde celle que le client attend. 236
Chapitre 7

le pattern Adaptateur

Adaptateurs orients objet


Disons que vous possdez un systme logiciel dans lequel vous voulez charger une nouvelle bibliothque de classe dun fournisseur donn, mais que ce nouveau fournisseur a conu ses interfaces diffremment du prcdent:
Votre systme existant Classe propritaire

avez lle que vous ! e c s a p d n as spo nctionnera p ace ne corre Leur interf votre code. Cela ne fo utilise dans
Bien. Vous vous refusez rsoudre le problme en modifiant votre code existant (et vous ne pouvez pas rcrire celui du fournisseur). Que faites-vous? Eh bien vous pouvez crire une classe qui adapte linterface du nouveau fournisseur pour quelle corresponde celle dont vous avez besoin.
Votre systme existant Classe propritaire

Adaptateur

Ladaptateur implmente linterface laquelle vos classes sattendent.

isseur erface du fourn nt li ec av se er Et conv vos requtes. pour satisfaire

Ladaptateur joue le rle dintermdiaire: il reoit les requtes du client et les convertit en requtes comprhensibles par les classes du fournisseur.
Votre systme existant Classe Adaptateur propritaire

Pas de modification du code.

Nouveau code.

Pas de modification du code.

imaginer une Pouvez-vous ne VOUS oblige PAS solution qui code supplmentaire crire du r les nouvelles pour intgre e penseriez-vous de classes? Qu te que le fournisseur faire en sor classe adaptatrice. fournisse la
vous tes ici

237

un adaptateur de dindons

Si a marche comme un canard et si a cancane comme un canard, ce doit peut tre un canard dindon envelopp dans un adaptateur pour canard...
Il est temps de voir un adaptateur en action. Vous souvenez-vous de nos canards du chapitre1? Examinons une version lgrement simplifie des interfaces et des classes Canard:

public interface Canard { public void cancaner(); public void voler(); }

nos canards Cette fois, t une interface implmenten leur permet de Canard qui cancaner. voler et de

Voici une sous-classe de Canard, le Colvert.


public class Colvert implements Canard { public void cancaner() { System.out.println(Coincoin); } public void voler() { System.out.println(Je vole); } }

nard se mples: le ca ait. si s n io t a t n e f uil Implm fficher ce q contente da

Il est temps de faire connaissance avec notre tout dernier volatile:

Les d
public interface Dindon { public void glouglouter(); public void voler(); }

ncanent pa indons ne ca

s, ils glouglou

tent.

Les dindons ne volent pas, sauf parfois sur de courtes distances.

238

Chapitre 7

le pattern Adaptateur

public class DindonSauvage implements Dindon { public void glouglouter() { System.out.println(Glouglou); }

tion concrte Voici une implmentale canard, ce de Dindon; comme dafficher ses dindon se contente actions.

public void voler() { System.out.println(Je ne vole pas loin); } }

Disons maintenant que vous tes court dobjets Canard et que vous aimeriez utiliser des objets Dindon la place. De toute vidence, il est impossible dutiliser les dindons directement parce que leur interface est diffrente. Nous allons donc crire un Adaptateur:

Code la loupe

Vous devez dabord implmenter linterface du type auquel vous vous adaptez. Cest linterface que votre client sattend voir. Puis vous devez obtenir une rfrence lobjet que vous adaptez; pour ce faire, nous utilisons le constructeur.

public class AdaptateurDindon implements Canard { Dindon dindon; public AdaptateurDindon(Dindon dindon) { this.dindon = dindon; } public void cancaner() { dindon.glouglouter(); } public void voler() { for(int i=0; i < 5; i++) { dindon.voler(); } } }

Maintenant, nous devons implmenter toutes les mthodes de linterface. La traduction de cancaner() entre les classes est facile: il suffit dappeler la mthode glouglouter(). Mme si les deux interfaces possdent une mthode voler(), les Dindons ne volent que sur de courtes distances ils ne peuvent pas voler trs longtemps comme les Canards. Pour assurer la correspondance entre la mthode voler() dun Canard et celle dun Dindon, nous devons appeler cinq fois la mthode voler() du Canard pour simuler son comportement.
vous tes ici

239

tester ladaptateur

Testons ladaptateur
Il ne nous faut plus quun peu de code pour tester notre adaptateur: crons un Canard... et un Dindon.
public class TestCanard { public static void main(String[] args) { Colvert canard = new Colvert();

Canard... don. et un Din Puis enveloppons le dindon DindonSauvage dindon = new DindonSauvage(); dans un AdaptateurDindon Canard adaptateurDindon = new AdaptateurDindon(dindon); qui le fait ressembler un Canard. System.out.println(Dindon dit...); dindon.glouglouter(); Testons maintenant le canard dindon.voler); en appelant la mthode testerCanard() qui attend un objet System.out.println(\n Canard dit...); Canard. testerCanard(canard); Puis testons le Dindon: System.out.println(\nAdaptateurDindon dit...); quil glougloute et quil vole. Crons un
testerCanard(adaptateurDindon);

} static void testerCanard(Canard canard.cancaner(); canard.voler(); } }

Et mai ntenan nous es t le s a canard) { dindon yons de f grand test pour un aire pas : canard ser le Voici notre mtho ... de te st er C anard(): elle reoit appelle ses mthode un canard et s cancaner() et voler().

Fichier Fentre dition Aide Canarder

%java TestCanard

Excution du test

Dindon dit... Glouglou Je ne vole pas loin Canard dit... Coincoin Je vole AdaptateurDindon dit... Glouglou Je ne vole pas loin Je ne vole pas loin Je ne vole pas loin Je ne vole pas loin Je ne vole pas loin

Le Dindon glougloute et vole sur une courte distance.


Le Canard cancane et vole exactement comme prvu.

240

nd Et ladaptateur glougloute qua cinq e vol et e el app cancaner() est La e. el app est ) fois quand voler( t pas ses ne d() nar Ca mthode tester don din un it ava elle rendu compte qu ! dguis en canard

Chapitre 7

le pattern Adaptateur

Le pattern Adaptateur expliqu


Maintenant que nous avons une ide de ce quest un Adaptateur, revenons en arrire et regardons de nouveau tous ses constituants.

Adapt Client
requ ete()

req

uet

eTr

adu

) ite(

Le Client est implment selon linterface cible.

Adaptateur
interf ac adapt e

r inte

face

cibl

e
LAdaptateur implmente linterface cible et contient une instance de lAdapt.

Voici comment le Client utilise lAdaptateur


1

enon implm d in D r ard. u n e a Adaptatterface cible, C tait lin

Dindon tait linterface de ladapt

Le client envoie une requte ladaptateur en appelant dessus une mthode en utilisant linterface cible. Ladaptateur traduit cette requte en un ou plusieurs appels ladapt en utilisant linterface de ladapt. Le client reoit les rsultats de lappel et ne sait jamais que cest un adaptateur qui effectue la traduction.

nt et lAdapt Notez que le Clie cun des deux ne au s pl ou c d sont connat lautre.

vous tes ici

241

le pattern Adaptateur: dfinition

vos crayons Sharpen your pencil

Imaginez que nous ayons galement besoin dun Adaptateur qui convertit un Canard en Dindon. Appelons-le AdaptateurCanard. crivez cette classe:

Comment avez-vous trait la mthode voler() (aprs tout nous savons que les canards volent plus loin que les dindons? Regardez notre solution la fin de ce chapitre. Avez-vous trouv un meilleur moyen?

questions stupides

Il ny a pas de

Q: R:

Quel volume d adaptation un adaptateur doit-il effectuer? On dirait que si je dois implmenter une interface Cible importante, cela implique une somme IMPORTANTE de travail.

Q: R:

Un adaptateur enveloppe-t-il toujours une classe et une seule?

Certainement. Le travail dimplmentation dun adaptateur est exactement proportionnel la taille de linterface que vous devez prendre en charge en tant quinterface Cible. Mais vous disposez de deux options. Vous pouvez retravailler tous vos appels linterface ct client, ce qui entranerait beaucoup de recherches et de modifications du code. Ou bien vous pouvez fournir bien proprement une seule classe qui encapsule tous les changements.

Le rle du pattern Adaptateur consiste convertir une interface en une autre. Si la plupart des exemples du pattern montrent un adaptateur enveloppant un adapt, nous savons tous les deux que les choses sont un peu plus complexes dans la ralit. Vous pouvez donc rencontrer des situations dans laquelle un adaptateur ncessite deux ou plusieurs adapts pour implmenter linterface Cible. Ceci est li un autre pattern nomm Faade et il est frquent de confondre les deux. Rappelezmoi den reparler quand nous aborderons les faades plus loin dans ce chapitre.

Que se passe-t-il si mon systme comprend plusieurs parties et que les plus anciennes attendent linterface de lancien fournisseur mais que nous en ayons dj crit dautres qui utiliseront celle du nouveau? Cela va poser des problmes si nous utilisons un adaptateur ici et linterface non enveloppe l. Ne vaudrait-il pas mieux que je rcrive mon ancien code et que joublie ladaptateur?

Q:

Pas ncessairement. Vous pouvez par exemple crer un Adaptateur bidirectionnel qui prenne en charge les deux interfaces. Pour crer un Adaptateur bidirectionnel, il suffit dimplmenter les deux interfaces concernes, afin que ladaptateur puisse se comporter comme lancienne interface ou comme la nouvelle.

R:

242

Chapitre 7

le pattern Adaptateur

Le pattern Adaptateur : dfinition


Assez de canards, de dindons et dadaptateurs lectriques pour aujourdhui. Revenons la ralit et voyons la dfinition officielle du pattern Adaptateur:

Le pattern Adaptateur convertit linterface dune classe en une autre conforme celle du client. LAdaptateur permet des classes de collaborer, alors quelles nauraient pas pu le faire du fait dinterfaces incompatibles.

Maintenant, nous savons que ce pattern nous permet dutiliser un client dont linterface est incompatible en crant un Adaptateur qui excute la conversion, ce qui a pour effet de dcoupler le client de linterface implmente. Si nous nous attendons ce que linterface doive changer, ladaptateur encapsule ce changement pour quon nait pas besoin de modifier le client chaque fois quil doit fonctionner avec une interface diffrente. Aprs avoir eu un aperu du comportement de ce pattern lors de lexcution, jetons galement un coup dil au diagramme de classes:
Client
<<interface>> Cible
requete()

LAdaptateur implmente linterface Cible.

Le Client ne voit que linterface Cible.


Adaptateur
requete()

Adapt
requeteSpecifique()

LAdaptateur est compos de lAdapt.


Le pattern Adaptateur est plein de bons principes de conception OO: remarquez lusage de la composition des objets pour envelopper ladapt dans une interface modifie. Cette approche prsente lavantage supplmentaire de permettre dutiliser un adaptateur avec nimporte quelle sous-classe de ladapt. Observez galement comment le pattern lie le client une interface, non une implmentation: nous pourrions utiliser plusieurs adaptateurs, chacun deux convertissant un ensemble de classes diffrent. Ou bien nous pourrions ajouter de nouvelles implmentations aprs, tant quelles adhrent linterface Cible.

Toutes les requtes sont dlgues lAdapt.

vous tes ici

243

adaptateurs dobjet et adaptateurs de classe

Adaptateurs dobjet et adaptateurs de classe


Si nous avons bien dfini le pattern, nous ne vous avons pas encore tout dit. Il existe en ralit deux sortes dadaptateurs: les adaptateurs dobjet et les adaptateurs de classe. Nous navons vu jusquici que des adaptateurs dobjet, et le diagramme de classes de la page prcdente est le diagramme dun adaptateur dobjet. Quest-ce donc quun adaptateur de classe et pourquoi ne pas en avoir parl? Parce que lhritage multiple est ncessaire pour limplmenter, ce qui est impossible en Java. Mais cela ne veut pas dire que vous naurez pas besoin un jour dadaptateurs de classe si votre langage favori autorise lhritage multiple! Voici le diagramme de classes pour lhritage multiple:

Client
requete()

Cible

Adapt
requeteSpecifique()

Adaptateur
requete()

Cela vous rappelle quelque chose? Exactement. La seule diffrence est que ladaptateur de classe sous-classe la Cible et lAdapt, tandis que, dans le cas de ladaptateur dobjet, nous utilisons la composition pour transmettre des requtes un Adapt.

Au lieu dutiliser la composition pour adapter lAdapt, lAdaptateur sous-classe maintenant les classes Adapt et Cible.

A
244

Musclez vos neurones

Les adaptateurs dobjet et les adaptateurs de classe utilisent deux techniques diffrentes pour adapter ladapt (composition vs. hritage). Comment ces diffrences dimplmentation affectent-elles la souplesse de ladaptateur?

Chapitre 7

le pattern Adaptateur

Le frigo
Votre tche consiste prendre les canards et les dindons magntiques et les poser sur la partie du diagramme qui dcrit le rle jou par chaque volatile dans notre prcdent exemple. (Essayez de ne pas tourner les pages). Puis ajoutez vos propres annotations pour documenter le fonctionnement.
Adaptateur de classe
Client
requete()

Cible

Adapt
requeteSpecifique()

Adaptateur
requete()

Adaptateur dobjet
Client
<<interface>> Cible
requete()

Adaptateur
requete()

Adapt
requeteSpecifique()

Posez ces aimants sur le diagramme de classes pour in di du diagramme repr quer quelle partie et laquelle reprsensente le Canard te le Dindon.

vous tes ici

245

solution de lexercice

Solution du jeu du frigo

Note: ladaptat r de classe sappuyant sur lheu r it vous ne pouvez pas age multiple, limplmenter en Java...
Classe Canard

Classe Dindon

Adaptateur de classe
Client
requete()

Cible

Adapt
requeteSpecifique()

Le Client croit qu il parle un Canard.

se est la claselle La Cible C est sur Canard. ent invoque que le cliodes. les mth

les on na pasnard, d in D e s s La cla thodes que Ca mmes m aptateur peut ode mais lAd les appels de mth t Adaptateur prendre d, les convertir e requete() de Canar s mthodes sur le LAdaptateur permet appeler le au D ind on de rpondre aux requtes Dindon. en vo y es au Canard en tendant (Canard et Dindon). les DEUX classes

Interface Canard
Adaptateur dobjet
Client
<<interface>> Cible
requete()

Le Client croit qu il parle un Canard.

de ans le caslasse, la Comme dt ur de c ladapta ela classe Canard. cible est elle que le client Adaptateur Cest sur s mthodes. requete() invoque le

La classe Dindon na que Canard. Autremenpas la mme interface pas de mthode cancant dit, le Dindon na er(), etc.
Objet Dindon

Adapt
requeteSpecifique()

LAdaptateur impl nt e linterface Canard, me ma is reoit un appel de mth quand il ode, il la dlgue au Dindon.
246
Chapitre 7

n ur, le Dinduo client e t a t p a d A l Grce recevra les appels d (Adapt) ce Canard. linterfa

le pattern Adaptateur

Face face :

Le face face de ce soir: lAdaptateur dobjet et lAdaptateur de classe.

Adaptateur dobjet
Comme jutilise la composition, jai une longueur davance. Je suis capable dadapter une classe Adapt, mais aussi nimporte laquelle de ses sous-classes.

Adaptateur de classe

Cest vrai, cela me pose des problmes parce que je suis li une classe Adapt spcifique. Mais jai un norme avantage: je ne suis pas oblig de rimplmenter mon Adapt entier. Je peux aussi redfinir son comportement si jen ai besoin, parce que je me contente de sous-classer.

Dans le monde o je vis, nous prfrons la composition lhritage. Vous conomisez peut-tre quelques lignes de code, mais je ne fais rien dautre quen ajouter un petit peu pour dlguer ladapt. Nous aimons bien conserver de la souplesse. Souple, peut-tre. Efficace, srement pas. Quand on utilise un adaptateur de classe, il ny a que moi. Il ny a pas un adaptateur ET un adapt.

Parce que vous souciez dun seul petit objet? Vous pourriez tre capable de redfinir rapidement une mthode, mais tout comportement que jajoute au code adaptateur fonctionne avec ma classe Adapt et toutes ses sous-classes. Oh, a va, lchez-moi les baskets, je nai qu composer avec la sous-classe pour que a fonctionne. Vous voulez voir quelque chose de pas terrible? Regardez-vous dans une glace!

Ouais, et si une sous-classe de ladapt ajoute un nouveau comportement. Quest-ce qui se passe?

Pas terrible

vous tes ici

247

adaptateurs du monde rel

Adaptateurs du monde rel


Jetons un coup dil une utilisation dun Adaptateur simple dans le monde rel (quelque chose de plus srieux que les canards en tous cas)...

Lancien monde: Enumeration


Si vous programmez en Java depuis un moment, vous vous souvenez probablement que les premiers types collection (Vector, Stack, Hashtable et quelques autres) implmentent une mthode elements(), qui retourne une Enumeration. Linterface Enumeration vous permet de parcourir les lments dune collection sans connatre en dtail la faon dont ils sont grs au sein de cette collection.

Enumera
<<interface>> Enumeration
hasMoreElements() nextElement()

e interf tion a un

ace simple

Indique sil reste des lments dans la collection.


Retourne llment suivant de la collection.

Le nouveau monde: Iterator


Lorsque Sun a cr ses nouveaux types collections, nous avons commenc utiliser une interface Iterator qui, comme Enumeration, sert oprer des itrations sur les lments dune collection, mais qui permet de plus den supprimer..
<<interface> Iterator
hasNext() next() remove()

Analogue hasMoreElements() dans linterface Enumeration. Cette mthode indique couru simplement si vous avez par tion. tous les lments de la collec
Retourne llment suivant dans la collection. Supprime un lment de la collection.

Et aujourdhui...
Nous sommes souvent confronts du code hrit qui expose linterface Enumerator, mais nous voudrions que notre nouveau code nutilise quIterator. On dirait que nous avons avoir besoin dun adaptateur.

248

Chapitre 7

le pattern Adaptateur

Adapter une Enumeration un Iterator


Nous allons dabord tudier les deux interfaces pour voir comment les mthodes se correspondent. Autrement dit, nous allons dterminer quoi appeler sur linterface Adapt quand le client invoque une mthode sur la cible.

interface Cible
<<interface>> Iterator
hasNext() next() remove()

Ces deux mthodes ont lair facile: elles correspondent directement hasNext() et next() dans Iterator.
<<interface>> Enumeration
hasMoreElements() nextElement()

interface Adapt

Mais quen est-il de cette mthode remove() dans Iterator? Il nexiste rien de la sorte dans Enumeration.

Concevoir lAdaptateur
Voici quoi nos classes doivent ressembler: il nous faut un adaptateur qui implmente linterface Cible et qui soit compos avec un adapt. La correspondance entre les mthodes hasNext() et next() va tre facile raliser entre la cible et ladaptateur: nous les transposons directement. Mais que faites-vous de remove()? Rflchissez-y un moment (nous allons nous en occuper la page suivante). Pour linstant, voici le diagramme de classes:

Votre nouveau code utilise toujours des Iterators, mme sil y a en ralit une Enumeration sousjacente.
EnumerationIterator est ladaptateur.

<<interface>> Iterator
hasNext() next() remove()

Nous faisons en sorte que les Enumerations de votre ancien code ressemblent aux Iterators du nouveau.

EnumerationIterator hasNext() next() remove()

<<interface>>
Enumeration hasMoreElements() nextElement()

Ladapt est une classe implmentant linterface Enumeration.

vous tes ici

249

adapter une enumeration un iterator

Traiter la mthode remove()


Bien. Nous savons quEnumeration est purement et simplement incapable de prendre en charge remove(). Cest une interface en lecture seule. Il nexiste aucun moyen dimplmenter une mthode remove() pleinement fonctionnelle dans ladaptateur. Au mieux, nous pouvons lancer une exception au moment de lexcution. Heureusement, les concepteurs de linterface Iterator ont prvu ce cas et ont dfini remove() pour quelle supporte une UnsupportedOperationException. Cest lune des situations dans lesquelles ladaptateur nest pas parfait. Les clients devront grer les exceptions potentielles, mais tant quils font attention et que ladaptateur est bien document, cest une solution parfaitement raisonnable.

crire ladaptateur EnumerationIterator


Voici un code simple mais efficace pour toutes ces classes hrites qui continuent produire des Enumerations:

public class EnumerationIterator implements Iterator { Enumeration enum; public EnumerationIterator(Enumeration enum) { this.enum = enum; } public boolean hasNext() { return enum.hasMoreElements(); } public Object next() { return enum.nextElement(); } public void remove() { throw new UnsupportedOperationException(); } }

Comme nous adaptons Enumeration Iterator, notre Adaptateur implmente linterface Iterator... Il doit ressembler un Iterator.
LEnumeration que nous adaptons. Comme nous utilisons la composition, nous la plaons dans une variable dinstance. La mthode hasNext() dIterator est dlgue la mthode hasMoreElements() dEnumeration... ... et la mthode next() dIterator est dlgue la mthode nextElement() dEnumeration.

Malheureusement, nous ne pouvons pas prendre en charge la mthode remove() dIterator, et nous laissons tomber! Nous nous contentons de lancer une exception.

250

Chapitre 7

le pattern Adaptateur

Exercice

Si Java a pris la direction de lIterator, il existe nanmoins beaucoup de code client hrit qui dpend de linterface Enumeration, et un Adaptateur qui convertit un Iterator en Enumeration est galement trs utile. crivez un Adaptateur qui convertit un Iterator en Enumeration. Vous pouvez tester votre code en adaptant une ArrayList. La classe ArrayList supporte linterface Iterator mais pas Enumeration (enfin, pas encore en tous cas).

Musclez vos neurones

Non contents de modifier linterface, certains adaptateurs lectriques ajoutent des fonctionnalits: protection contre les surtensions, voyants indicateurs et autres accessoires possibles et imaginables. Si vous deviez implmenter ce genre de fonctionnalits, quel pattern appliqueriez-vous?

vous tes ici

251

face face: dcorateur et adaptateur

Face face :

Lentretien de ce soir : Le pattern Dcorateur et le pattern Adaptateur analysent leurs diffrences.

Dcorateur
Je suis trs important. Ma vocation, cest la responsabilit quand un Dcorateur est impliqu, vous savez que votre conception va comprendre de nouvelles responsabilits ou de nouveaux comportements.

Adaptateur

Mais vous, vous voulez toute la gloire pendant que nous autres adaptateurs pataugeons dans les tranches faire le sale boulot: convertir des interfaces. Notre tche nest peut tre pas prestigieuse, mais nos clients apprcient coup sr quon leur facilite la vie. Cest possible, mais nallez pas croire quon se tourne les pouces. Quand il faut dcorer une grosse interface, oh l l! Cela peut demander des quantits de code.

Essayez donc dtre un adaptateur, quand vous devez faire appel plusieurs classes pour fournir linterface que votre client attend. Voil qui est dur. Mais nous avons un dicton: un client dcoupl est un client heureux.

Malin. Mais ne pensez pas que nous rcoltons toute la gloire. Parfois, je ne suis quun dcorateur envelopp par je ne sais combien dautres dcorateurs. Quand une mthode mest dlgue, je nai aucune ide du nombre de dcorateurs qui lont dj traite, et je ne sais mme pas si les efforts que jai dploys pour satisfaire la requte seront remarqus. H, si les adaptateurs font leur travail, nos clients ne remarquent mme pas notre prsence. Ce peut tre un travail ingrat.

252

Chapitre 7

le pattern Adaptateur

Dcorateur

Adaptateur
Mais ce qui est gnial avec nous autres adaptateurs, cest que nous permettons aux clients dutiliser de nouvelles bibliothques et autres sous-ensembles sans avoir changer une ligne de code, et de sen remettre nous pour effectuer la conversion leur place. Bien sr, cest une niche, mais nous y excellons.

Nous aussi, seulement nous permettons dajouter de nouveaux comportements aux classes sans modifier le code existant. Je continue prtendre que les adaptateurs ne sont que des sortes de dcorateurs. Je veux dire que vous enveloppez un objet, tout comme nous. Non, non, non, pas du tout. Nous convertissons toujours linterface de ce que nous enveloppons, vous jamais. Je dirais quun dcorateur est comme un adaptateur, sauf que vous ne modifiez pas linterface! Euh, non. Notre tche dans la vie consiste tendre les comportements ou les responsabilits des objets que nous enveloppons. Nous ne sommes que de simples intermdiaires. H, qui appelez-vous de simples intermdiaires? Venez ici et nous verrons combien de temps vous durerez convertir quelques interfaces! Nous devrions peut-tre convenir que nous sommes diffrents. Nous avons sans doute lair quelque peu similaires sur le papier, mais nous sommes des lieues lun de lautre en termes de motivation. Oh oui, l je vous rejoins.

vous tes ici

253

qui fait quoi?

Et maintenant pour changer...


Il y a un autre pattern dans ce chapitre.
Vous avez vu comment le pattern Adaptateur convertit linterface dune classe en celle que le client attend. Vous savez galement que Java permet de mettre en uvre ce mcanisme en enveloppant lobjet qui a une interface incompatible dans un objet qui implmente la bonne. Nous allons maintenant tudier un pattern qui modifie une interface, mais pour une autre raison: pour simplifier ladite interface. On lappelle point nomm le pattern Faade parce quil masque toute la complexit dune ou plusieurs classes derrire une faade bien propre et bien claire.

Qui fait quoi ?


g
Motivation Convertit une interface en une autre Ne modifie pas linterface mais ajoute une responsabilit Simplifie une interface

Faites correspondre chaque pattern sa motivation:

Pattern Dcorateur Adaptateur Faade

254

Chapitre 7

le pattern Adaptateur

Home Cinma
Avant de plonger dans les dtails du pattern Faade, jetons un coup dil une obsession nationale qui ne cesse de prendre de lampleur: construire son propre home cinma. Vous avez fait des recherches et vous avez assembl un systme denfer, avec un lecteur de DVD, un projecteur vido, un cran automatis, le son surround et mme une machine pop-corn. Vrifions tous les composants que vous avez assembls:

Amplificateur
tuner lecteurDvd lecteurCd marche() arret() setCd() setDvd() setSonStereo() setSonSurround() setTuner() setVolume()

Tuner
amplificateur

LecteurDvd
amplificateur marche() arret() ejecter() pause() jouer() jouer() setAudioSurround() SetAudioStereo() stop()

marche() arret() setAm() setFm() setFrequence()

LecteurCd
amplificateur marche() arret() ejecter() pause() jouer() jouer() stop()

Ecran
monter() baisser()

Cela fait beaucoup de classes, beaucoup dinteractions et un ensemble important dinterfaces apprendre et utiliser.

Projecteur
lecteurDvd marche() arret() modeTV() modeGrandEcran()

MachineAPopcorn
marche() arret() eclater()

Lumieres
marche() arret() attenuer()

Vous avez pass des semaines tirer des fils, monter le projecteur, raliser toutes les connexions et effectuer tous les rglages. Maintenant, il est temps de tout mettre en marche et dapprcier un bon film...

vous tes ici

255

tches pour regarder un film

Regarder un film ( la dure)


Choisissez un DVD, relaxez-vous et prparez vous contempler la magie du cinma. Oh, juste une chose, pour regarder un film, vous devez effectuer un certain nombre de tches:
1 2 3 4 5 6 7 8 9 10 11 12 13

Allumer la machine pop-corn Commencer faire clater le pop-corn Attnuer les lumires Baisser lcran Allumer le projecteur Rgler le projecteur sur DVD Rgler le projecteur en mode grand cran Allumer lamplificateur de son Rgler lamplificateur sur DVD Rgler le son de lamplificateur surround Rgler le volume de lamplificateur sur moyen (5) Allumer le lecteur de DVD Jouer le DVD
Je suis dj puis et je nai rien fait dautre que de tout allumer!

256

Chapitre 7

le pattern Adaptateur

Voyons ces mmes tches en termes de classes et dappels de mthode ncessaires pour les raliser:

Allumer lappareil et commencer clater le pop-corn...


machineAPopCorn.marche(); machineAPopCorn.eclater(); lumieres.attenuer(10);

Attnuer les lumires 10%... Baisser lcran... Allumer le projecteur et le pour rgler en mode grand cran regarder un film... D, Allumer lampli, le rgler sur DV nd rou sur son le mettre en mode et rgler le volume 5... Allumer le lecteur de DVD... et ENFIN jouer le film!

rentes Six classes diff sont impliques!

ecran.baisser(); projecteur.marche(); projecteur.setEntree(dvd); projecteur.modeGrandEcran(); amp.marche(); amp.setDvd(dvd); amp.setSonSurround(); amp.setVolume(5); dvd.marche(); dvd.jouer(film);

Mais ce nest pas tout...

Quand le film est termin, comment faites-vous pour tout teindre?


Ne faut-il pas tout refaire, mais cette fois lenvers?

Ne serait-ce pas aussi complexe dcouter un CD ou la radio? Et si vous dcidez dacqurir un nouveau composant, vous devrez probablement
apprendre une procdure lgrement diffrente. Alors, que faire? La complexit dutilisation de votre home cinma commence apparatre! Voyons comment le pattern Faade peut nous sortir de ce ptrin et nous permettre dapprcier le film...

vous tes ici

257

lumires camra faade

Lumires, Camra, Faade!


Une Faade est exactement ce quil vous faut: le pattern Faade vous permet de prendre un sous-systme complexe et den simplifier lutilisation en implmentant une classe Faade qui fournit une interface unique et plus raisonnable. Ne vous inquitez pas: si vous avez besoin du sous-systme complexe, il est toujours votre disposition, mais si vous navez besoin que dune interface simple, la Faade est l pour vous. Jetons un coup dil au fonctionnement de la Faade:

Bien. Il est temps de crer une Faade pour le systme de home cinma. Pour ce faire, nous crons une e nouvelle classe nomm i qu a, em FacadeHomeCin re mb no expose un petit de mthodes simples comme regarderFilm().

2 La classe Faade traite

La Faade
FacadeHomeCinema
regarderFilm() arreterFilm() ecouterCd() arreterCd() ecouterRadio() arreterRadio()

les composants du home cinma comme un sous systme, et fait appel au sous-systme pour implmenter sa mtho de regarderFilm().

Amplificateur
tuner lecteurDvd lecteurCd marche() arret()

Tuner
amplificateur marche() arret() setAm() setFm() setFrequence()

setCd() setDvd() setSonStereo() setSonSurround() setTuner() setVolume()

LecteurDvd
amplificateur marche() arret() ejecter() pause() jouer() jouer() setAudioSurround()

jouer

()

LecteurCd
amplificateur

SetAudioStereo() stop()

Ecran
monter()

marche() arret() ejecter() pause() jouer() jouer() stop()


marche()

e que Le sous-systm ie. pl la Faade sim if

baisser()

Projecteur
lecteurDvd

MachineAPopcorn
marche() arret() eclater()

arret() modeTV() modeGrandEcran()

Lumieres
marche() arret() attenuer()

marche

()

258

Chapitre 7

le pattern Adaptateur

regard er

Film()
REG ARD ERF ILM ARR [] ETE RFI LM []

Un client du sous-systme de la faade

appelle Le code de votre client s sur la maintenant les mthode -systme. Faade, pas sur le sous er un Dsormais, pour regard une seule film, il suffit dappeler (), et mthode, regarderFilm notre celle-ci communique le place avec les lumires, teur, jec pro le D, DV de lec teur et la lamplificateur, lcran machine pop-corn.

Il faut que jaie mon propre accs de bas niveau! 4

Ancien prsident du de cin-club du collge . Villedieu-les-Poles

Clie nt

La Faade permet toujours dutiliser direc tement le sous-systme qui demeure accessible. Si vous avez besoin des fonctionnalits avances des classes du sous-systme, elles sont disponibles.

vous tes ici

259

facade versus adapter

Q: R:

questions stupides

Il ny a pas de

Si la Faade encapsule les classes du sous-systme, comment un client qui a besoin dune fonctionnalit de plus bas niveau peut-il y accder?

Q: R:

Quel est lavantage dune faade en dehors du fait quelle simplifie linterface?

Les Faades n encapsulent pas les classes du sous-systme. Elles fournissent simplement une interface simplifie leurs fonctionnalits. Les classes du sous-systme demeurent toujours accessibles et directement utilisables par les clients qui ont besoin dinterfaces plus spcifiques. Cest une proprit sympathique du pattern Faade: il fournit une interface simplifie tout en continuant exposer toutes les fonctionnalits du systme ceux qui pourraient en avoir besoin.

Le pattern Faade vous permet galement de dcoupler limplmentation de votre client de tout sous-systme. Disons par exemple que vous avez eu une grosse augmentation et que vous dcidez dacheter tout un tas de nouveaux composants qui ont des interfaces diffrentes. Eh bien, si vous avez cod votre client en fonction de la faade et non du sous-systme, ce code client na pas besoin de changer: seule la faade est modifie (et avec un peu de chance le fournisseur sen occupe!).

Une faade ne se limite pas simplifier une interface: elle dcouple un client dun sous-systme de composants. Faades et adaptateurs peuvent envelopper plusieurs classes, mais la finalit dune faade est de simplifier une interface, tandis que celle dun adaptateur est de la convertir en quelque chose de diffrent.

Q: R:

Q: R:

La faade ajoute-t-elle une fonctionnalit quelconque ou se contente-t-elle de transmettre chaque requte au sous-systme?

Donc la diffrence entre le pattern Adaptateur et le pattern Faade est que ladaptateur enveloppe une classe et que la faade peut reprsenter plusieurs classes?

Une faade est libre dajouter ses propres plus lutilisation du soussystme. Par exemple, si la faade de notre home cinma faade nimplmente aucun nouveau comportement, elle est suffisamment intelligente pour savoir quune machine pop-corn doit tre allume avant que le mas ne puisse clater (de mme quelle connat les dtails des oprations pour dmarrer et arrter un film).

Q: R:
260

Non! Souvenez-vous: le pattern Adaptateur transforme linterface dune ou plusieurs classes en une interface laquelle le client sattend. Si dans la plupart des ouvrages les exemples montrent un adaptateur adaptant une seule classe, vous pouvez avoir besoin dadapter plusieurs classes pour fournir linterface en fonction de laquelle le client est cod. De mme, une Faade peut fournir une interface simplifie une seule classe dote dune interface trs complexe. La diffrence entre les deux ne rside pas dans le nombre de classes quils peuvent envelopper mais dans leur motivation. La motivation du pattern Adaptateur est de modifier une interface pour quelle corresponde celle quun client attend. Celle du pattern Faade est de fournir une interface simplifie un sous-systme.

Est-ce que chaque sous-systme na quune seule faade?

Pas ncessairement. Le pattern permet sans quivoque de crer un nombre quelconque de faades pour un soussystme donn.

Chapitre 7

le pattern Adaptateur

Construire la faade de votre home cinma


Voyons les tapes de la construction de la FacadeHomeCinema: La premire consiste utiliser la composition afin que la faade ait accs tous les composants du sous-systme :

public class FacadeHomeCinema { Amplificateur amp; Tuner tuner; LecteurDvd dvd; LecteurCd cd; Projecteur projecteur; Lumieres lumieres; Ecran ecran; MachineAPopcorn machineAPopCorn;

Voici la composition: ce sont tous les composants du sous- r. systme que nous allons utilise

public FacadeHomeCinema(Amplificateur amp, Tuner tuner, LecteurDvd dvd, LecteurCd cd, Projecteur projecteur, Ecran ecran, Lumieres lumieres, MachineAPopcorn machineAPopCorn) { this.amp = amp; this.tuner = tuner; this.dvd = dvd; this.cd = cd; this.projecteur = projecteur; this.ecran = ecran; this.lumieres = lumieres; this.machineAPopCorn = machineAPopCorn; } // autres mthodes }

On transmet au constructeur de la faade une rfrence chaque composant du sous-systme. Puis la faade affecte chacun deux la variable dinstance correspondante.

Nous allons les voir dans

un instant...

vous tes ici

261

implmenter la faade

Implmenter linterface simplifie


Cest maintenant le moment de rassembler les composants du sous-systme dans une interface unifie. Implmentons les mthodes regarderFilm() et arreterFilm():

public void regarderFilm(String film) { System.out.println(Vous allez voir un bon film...); machineAPopCorn.marche(); machineAPopCorn.eclater(); me lumieres.attenuer(10) ; regarderFilm() observe la msio ns fai s ecran.baisser(); squence que lorsque nou velopp e projecteur.marche(); len tout la main, mais elle qui fait projecteur.modeGrandEcran(); ue dans une mthode pratiq que nous amp.marche(); tout le travail. Remarquez chaque amp.setDvd(dvd); dlguons la responsabilit de dant amp.setSonSurround(); tche au composant correspon amp.setVolume(5); dvd.marche() ; dans le sous-systme. dvd.jouer(film); } public void arreterFilm() { System.out.println(Cest la fin du film...); machineAPopCorn.arret(); lumieres.marche(); ecran.monter(); projecteur.arret(); Et arreterFilm() soccupe de amp.arret(); tout arrter votre place. dvd.stop(); Encore une fois, chaque tche dvd.ejecter(); est dlgue au composant dvd.arret(); appropri dans le sous-systme. }

A
262

Musclez vos neurones


Pensez aux faades que vous avez rencontres dans lAPI Java. O aimeriez-vous en avoir quelques unes de plus?

Chapitre 7

le pattern Adaptateur

Regardons un film (sans peine)


Cest lheure du SPECTACLE!

public class TestHomeCinema { public static void main(String[] args) { // instanciation des composants

Ici nous crons les composants directement dans le test. Normalement, le client voit une faade et na pas besoin de la construire lui-mme. On commence par instancier la Faade avec tous les composants du sous-systme.

FacadeHomeCinema homeCinema = new FacadeHomeCinema(amp, tuner, dvd, cd, projecteur, ecran, lumieres, machineAPopCorn); homeCinema.regarderFilm(Htel du Nord); homeCinema.arreterFilm(); } }
Fichier Fentre dition Aide AtmosphreAtmosphre?

On utilise linterface simplifie pour dmarrer le film, puis pour larrter.

%java TestHomeCinema

Voici le rsultat. Lappel de la mthode regarderFilm() de la Faade fait tout le travail notre place...

... ici, nous avons fini de regarder le film et arreterFilm() arrte tous les appareils.

Vous allez voir un bon film... Machine pop-corn en marche Machine pop-corn fait clater le pop-corn ! Lumires du Home Cinma attnues 10% cran du Home Cinma descendu Projecteur en marche Projecteur en mode grand cran (aspect 16x9) Magnifique Amplificateur en marche Magnifique Amplificateur positionn sur le lecteur DVD Super Lecteur DVD Magnifique Amplificateur rgl sur son surround (5 enceintes, 1 caisson de basses) Magnifique Amplificateur volume rgl sur 5 Super Lecteur DVD en marche Super Lecteur DVD joue Htel du Nord Cest la fin du film... Machine pop-corn arrte Lumires du Home Cinma allumes cran du Home Cinma remont Projecteur arrt Magnifique Amplificateur teint Super Lecteur DVD arrt sur Htel du Nord Super Lecteur DVD jection Super Lecteur DVD arrt % 263

vous tes ici

dfinition du pattern Faade

Le pattern Faade: dfinition


Pour utiliser le pattern Faade, nous crons une classe qui simplifie et unifie un ensemble de classes plus complexes qui appartiennent un sous-systme donn. Contrairement beaucoup de patterns, Faade est relativement simple et ne contient aucune de ces abstractions compliques qui vous font tourner la tte. Mais cela nte rien sa puissance: le pattern Faade vous permet dviter de coupler fortement les clients et les sous-systmes et, comme vous allez le voir bientt, vous aide galement observer un autre principe orient objet. Mais avant de prsenter ce nouveau principe, regardons la dfinition officielle du pattern:

Le pattern Faade fournit une interface unifie lensemble des interfaces dun sous-systme. La faade fournit une interface de plus haut niveau qui rend le soussystme plus facile utiliser.

Il ny a rien l que vous ne sachiez dj, mais lune des choses les plus importantes mmoriser propos dun pattern est sa motivation. Cette dfinition clame haut et fort que lobjectif dune faade est de rendre un sous-systme plus facile utiliser via une interface simplifie. Vous pouvez le constater dans son diagramme de classes:

dont Client heureuxt le travail vien grce dtre facilit la faade. Sous-systme pl us complexe.

Client

Faade

Interface unifie plus facile utiliser

classes du sous-systme

Et voil! Vous avez un autre pattern en poche! Maintenant, il est temps de voir ce nouveau principe OO. Attention, celui-ci peut remettre en question quelques ides reues!

264

Chapitre 7

le pattern Adaptateur

Ne parlez pas aux inconnus


Ne parlez pas aux inconnus est un principe qui nous aide restreindre les interactions entre objets quelques amis proches. Il snonce habituellement ainsi:

Principe de conception
Ne parlez pas aux inconnus ne parlez qu vos amis immdiats.

Mais quel est le sens de ce principe dans la ralit? Lors de la conception dun systme, il signifie que vous devez tre attentif pour chaque objet au nombre de classes avec lesquelles il interagit et la faon dont il entre en interaction avec elles. Ce principe nous empche de crer des systmes constitus dun grand nombre de classes fortement couples dans lesquels les modifications dune composante se propagent en cascade aux autres parties. Lorsquil existe trop de dpendances entre de nombreuses classes, vous construisez un systme fragile dont la maintenance sera coteuse et que les autres auront du mal comprendre en raison de sa complexit.

Musclez vos neurones


combien de classes ce code est-il coupl?

public float getTemp() { return station.getThermometre().getTemperature(); }

vous tes ici

265

principe de conception

Comment ne PAS se faire des amis et influencer les objets


Daccord, mais comment procder concrtement? Voici quelques lignes directrices: considrez un objet quelconque; maintenant, partir de nimporte quelle mthode de cet objet, le principe nous enjoint de nappeler que des mthodes qui appartiennent:

lobjet lui-mme Aux objets transmis en arguments la mthode Aux objets que la mthode cre ou instancie Aux composants de lobjet

us ctrices no e ir d s e n g que ces li thodes Remarquezne pas appeler de m ourns par disent de nt des objets ret appartena ppels de mthodes!! dautres a me composant comle un us vo ez nt riab Reprse frenc par une va un objet qui est r ent dit, il sagit dune dinstance. Autrem relation A-UN.

Cela semble un peu draconien, nest-ce pas? Quel mal y a-t-il appeler la mthode dun objet quun autre appel nous a retourn? Eh bien, si nous le faisions, ce serait une requte dune sous-partie dun autre objet (et nous augmenterions le nombre dobjets que nous connaissons directement). Dans de tels cas, le principe nous force demander lobjet dmettre la requte notre place. Ainsi, nous navons pas connatre les objets qui le composent (et notre cercle damis demeure restreint). Par exemple:

Sans le Principe

public float getTemp() { Thermometre thermometre = station.getThermometre(); return thermometre.getTemperature(); }

Ici, nous obtenons lobjet thermomtre de la station, puis nous appelons la mthode getTemperature() nous-mmes.

Avec le Principe

public float getTemp() { return station.getTemperature(); }

Quand nous appliquons le principe, nous ajoutons la classe Station une mthode qui demande le thermomtre notre place. Cette technique rduit le nombre de classes dont nous dpendons.
266
Chapitre 7

le pattern Adaptateur

Fixer des limites aux appels de mthodes...


Voici une classe Voiture qui illustre toutes les faons dont vous pouvez appeler des mthodes tout en observant le principe Ne parlez pas aux inconnus:

public class Voiture { Moteur moteur; // autres variables dinstance public Voiture() { // initialiser moteur, etc. } public void demarrer(Cle cle) { Portes portes = new Portes();

posant Voici un com sse. Nous de cette cla ler ses pouvons appe mthodes. Ici, nous crons un nouvel objet: lappel de ses mthodes est lgal. Vous pouvez appeler une mthode dun objet transmis en argument.
r une Vous pouvez appelemposant de co un mthode sur lobjet.

boolean autorise = cle.tourner(); if (autorise) { moteur.demarrer(); majTableauDeBord(); portes.fermer(); } } public void majTableauDeBord() { // mettre jour laffichage } }

Vous pouvez appeler une mthode locale lobjet. Vous pouvez appeler une mthode dun objet que vous crez ou que vous instanciez.

questions stupides

Il ny a pas de

Q: R:

Il y a un autre principe nomm Loi de Dmter. Comment sont-ils lis?

Il sagit dune seule et mme chose et vous rencontrerez les deux termes indiffremment. Nous prfrons Ne parlez pas aux inconnus pour deux raisons: (1) il se comprend mieux intuitivement et (2) lemploi du mot loi laisse entendre que ce principe doit toujours tre appliqu.

En fait, aucun principe nest une loi et tous doivent tre utiliss quand et o ils sont utiles. Toute conception implique des compromis (abstractions vs. rapidit, espace vs. temps, etc.). Les principes peuvent nous guider, mais nous devons prendre en compte tous les facteurs avant de les appliquer.

Q:

Y a-t-il un inconvnient quelconque appliquer Ne parlez pas aux inconnus?

Oui. Si ce principe diminue les dpendances entre les objets et si des tudes ont dmontr que la maintenance sen trouve rduite, il arrive galement que son application conduise crire plus de classes enveloppes pour grer les appels de mthodes dautres composants. Cela peut entraner une augmentation de la complexit et du temps de dveloppement, ainsi quune dgradation des performances lors de lexcution.

R:

vous tes ici

267

enfreindre le principe ne parlez pas aux inconnus

vos crayons Sharpen your pencil


Lune ou lautre de ces classes enfreint-elle le principe Ne parlez pas aux inconnus? Pourquoi?

public Maison { StationMeteo station; // autres mthodes et constructeur public float getTemp() { return station.getThermometre().getTemperature(); } }

public Maison { StationMeteo station; // autres mthodes et constructeur public float getTemp() { Thermometre thermometre = station.getThermometre(); return getAuxiliaireTemp(thermometre); } public float getAuxiliaireTemp(Thermometre thermometre) { return thermometre.getTemperature(); } }

Attention! Chutes dhypothses. Port du casque obligatoire.

A
268

Musclez vos neurones

Connaissez-vous un emploi courant de Java qui viole le principe Ne parlez pas aux inconnus? Est-ce important?

Chapitre 7

Rponse: Que pensez-vous de System.out.println()?

le pattern Adaptateur

Faade et Ne parlez pas aux inconnus


: quun seul ami Ce client na eCinema. En la FacadeHom n OO, navoir quun programmatio e BONNE chose! seul ami est un

Client

La FacadeHomeCinema gre tous les composants du soussystme la place du client. Le client conserve simplicit et souplesse.

FacadeHomeCinema
regarderFilm() arreterFilm() ecouterCd() arreterCd() ecouterRadio() arreterRadio()

s mettre Nous pouvon mposants du niveau les co sans affecter home cinma le client

Amplificateur
tuner lecteurDvd lecteurCd marche() arret()

Tuner
amplificateur marche() arret() setAm() setFm() setFrequence()

setCd() setDvd() setSonStereo() setSonSurround() setTuner() setVolume()

LecteurDvd
amplificateur marche() arret() ejecter() pause() jouer() jouer() setAudioSurround()

LecteurCd
amplificateur

SetAudioStereo() stop()

Ecran
monter()

marche() arret() ejecter() pause() jouer() jouer() stop()


marche()

s galement de Nous nous efforon sous-systmes faire adhrer les inconnus. Si cela Ne parlez pas aux exe et que trop devient trop complremlent, nous damis sent des faades pouvons introduire ur former des supplmentaires po stmes.. couches de sous-sy

baisser()

Projecteur
lecteurDvd

MachineAPopcorn
marche() arret() eclater()

arret() modeTV() modeGrandEcran()

Lumieres
marche() arret() attenuer()

vous tes ici

269

votre bote outils de concepteur

Votre bote outils de concepteur


Votre bote outils commence salourdir! Dans ce chapitre, nous lui avons ajout deux patterns qui permettent de modifier des interfaces et de rduire le couplage entre les clients et les systmes quils utilisent.

POINTS DIMPACT

Quand vous devez utiliser une


classe existante et que son interface nest pas celle dont vous avez besoin, employez un adaptateur.

Principes OO

Abstraction n Encapsulatio e. ri va ui q ce e.olymorphisme Encapsulez ritagP sulation lh ap nc le ez r Prf des erfaces, non Hritage mez des int
Program ions. t implmentat ler faiblemen vous de coup . z ce or f f E nt ui interagisse les objets q ouvertes doivent tre Les classes ais fermes la m lextension n. io at ic modif ions. Ne es abstract Dpendez d es classes concrtes. sd dpendez pa . u vos amis Ne parlez q

Bases de lOO

Quand vous devez simplifier et


unifier une grosse interface ou un ensemble dinterfaces complexe, employez une faade.

Un adaptateur transforme une


interface en celle que le client attend.

Une faade dcouple un client dun e ll e v u sous-systme complexe. o n e sons dun Nous dispo pour maintenir le Limplmentation dun adaptateur technique aible. (Souvenez- ... peut demander plus ou moins couplage f arlez qu vos amis) de travail selon la taille et la vous: ne p complexit de linterface cible. ux vea nou UX et de DE Limplmentation dune faade patterns. Chacun deux ncessite de composer la faade modifie une interface, avec son sous-systme et dutiliser ladaptateur pour la dlgation pour effectuer le convertir et la faade travail de la faade. O pour faade pour unifier n io t Patterns O la e r ille Le pattern Adaptateur possde une eesurs, fa it ef inm et simplifier. it-und in f e ce dsi de r n uc u pulu farcf c -e re h an e t ie h t c a a v in tg t le u r ab e t deux formes: les adaptateurs e su n e a s a g p s p n a Str e y c a it t s. O e n h ir in cn le eio a r b t s,tes ud f t a eje el n ea eisin D je g mb e b n u ho a ao m it h r n it n c ne o u in rp et c e e lg p t e t f . u u trr in a s D dae t nD e sq u ic m n d s r d r , e n iq lo t e dobjet et les adaptateurs de n b e d a r m n , n a it m je n io e e e a s h b il F u e p t o n le b it is q a ss y r a amrceic non d o na sla d m u e lg , o e an e b l ux rc ia nd p iuix ue je ft ud sla sa io u u b q ded qt F tx jo ere ot t q sit f a n u r u n e e o r is e t d c h e e d li n m c p s m n c a t u r m s t t r io s le ie o a e in u n e e co classe. Les adaptateurs de classe g t o d o s j d g ie p p e e a , b t n r t t ix so ss a n t o c n o a n r u io la et heur t ct n c la mn St d re t et uie n sso equ r e le a ue le un oulu rtso i oe d seca un g s rm so o psa e n if eit f in p ss t e x ul u S ot t p la n ps am n c u d io t e in t t la sc en n a . n m e e ic a r ie so c r is m e e n b st e p e x r la c a a font appel lhritage multiple. vare u u in e d nIl n . a F st f n p t iq io ge . le r in a t n t r u e t n e ra a era t ier. F m ie a se m tt sa ic c n tn r e m r nin is .r iom p ut ect al b nin o t la a iq u it C st e le ia tc e pa t q a is r lai e le iluu a iv aicns r ve t b onm s n d st c ns tr lo e n oa lu nl n g in io a ss e a l t e t e la t st p r ia c an lt y e c in is u n t a r a or g e e u s t e s st d m e o t au ile r d in p ss f s t, dlguer l eo cc la f n nje od a eob dt Vous pouvez implmenter plus ss e un la rc Adapta d cio u rniv et a ucna e .. et frentes requtes, ed se nu lasst face
e ctu c li m r dif a e esrs. n nu p es. nsrie ss io nt eu la unedeinter es dun c t a qu cl n it, et t dune classe nf oL p es rnes a e re d so ut eupa sd ss sd fo A la ife de c s at e . ll sul t u t e d e pi so . erfac li a q ca c s nst s e r u a r d d lo attente et F esioin r , a dat celle le rerd esbop o d m b a se t ll n o ili e c ib l e rs d ve urnit une e ri ait frm nif d,u pe des classes as pu le faire ettant ula a faade foau qui rend le L . e us pl m st p sy nive sousnauraient s incompatibles. e plus haut . interface d e plus facile utiliser dinterface m sous-syst
dune faade pour un mme soussystme.

Un adaptateur enveloppe un
objet pour modifier son interface, un dcorateur enveloppe un objet pour ajouter de nouveaux comportements et de nouvelles responsabilits, et une faade enveloppe un ensemble dobjets afin de simplifier.

270

Chapitre 7

le pattern Adaptateur

Oui, cest un autre mots-croiss. Toutes les solutions sont dans ce chapitre.

1 2

6 7 8

9 10

11

12 13 14

Horizontalement 3. Le monde rel en est plein. 6. Cest une loi. 7. Le fil que nous avons regard. 9. Il ne faut pas leur parler. 11. Viole le principe prcdent (System.out.______). 12. Simplifie une interface. 13. Ladaptateur de classe ncessite l________ multiple. 14. Wrappers.

Verticalement 1. Le home cinma a mme une machine _______. 2. Attention, chute d____________. 4. Ajoute de nouveaux comportements. 5. Lapplication sur laquelle nous avons travaill. 6. Entre classes, point trop nen faut. 8. Lun des avantages de Faade. 10. Il existe des adaptateurs dobjet et des adaptateurs de _______. 13. Interjection.

vous tes ici

271

solutions des exercices

vosyour crayons Sharpen pencil

Imaginez que nous ayons galement besoin dun Adaptateur qui convertit un Canard en Dindon. Appelons-le AdaptateurCanard. crivez cette classe:

solutions des exercices

adaptons Comme nous des Dindons des maintenant implmentons Canards, nous indon. linterface D
public class AdaptateurCanard implements Dindon { Canard canard; Random rand;

public AdaptateurCanard(Canard canard) { que nous adaptons. this.canard = canard; rand = new Random(); Nous recrons galement } public void glouglouter() { canard.cancaner(); } public void voler() { if (rand.nextInt(5) == 0) { canard.voler(); } } }

Nous mmorisons une rfrence au Canard

un objet alatoire;regardez la mthode voler() pour voir comment elle est utilise.

Un glouglou qui se transforme en cancan.

beaucoup Puisque les canards volent s avons plus loin ue les dindons, nou le canard dcid de ne faire voler enne. quun fois sur cinq en moy

vosyour crayons Sharpen pencil

Lune ou lautre de ces classes enfreint-elle Ne parlez pas aux inconnus? Pourquoi?

public Maison { StationMeteo station; // autres mthodes et constructeur public float getTemp() { return station.getThermometre().getTemperature(); } }

public Maison { StationMeteo station; // autres mthodes et constructeur

us s aux inconn e parlez pa N le t in jet Enfre de dun ob z la mthoe appel. Vous appele tr au un r retourn pa

public float getTemp() { Thermometre thermometre = station.getThermometre(); return getAuxiliaireTemp(thermometre); } public float getAuxiliaireTemp(Thermometre thermometre) { return thermometre.getTemperature(); } }

lez pas aux inconnus! Nenfreint pas le Ne par sommes dbrouills pour On dirait que nous nous Quelque chose a-t-il . cipe prin ce r rne tou con que nous avons transfr rellement chang depuis e? lappel une autre mthod

272

Chapitre 7

le pattern Adaptateur

Solutions des exercices


Vous avez vu comment crire un adaptateur qui convertit une Enumeration en Iterator; crivez maintenant un adaptateur qui convertit un Iterator en Enumeration.
public class IteratorEnumeration implements Enumeration { Iterator iterator; public IteratorEnumeration(Iterator iterator) { this.iterator = iterator; } public boolean hasMoreElements() { return iterator.hasNext() ; } public Object nextElement() { return iterator.next(); } }

g Qui fait quoi ? g


Faites correspondre chaque pattern sa motivation:

Pattern Dcorateur Adaptateur Faade

Motivation Convertit une interface en une autrer Ne modifie pas linterface mais ajoute une responsabilit Simplifie une interface
273

vous tes ici

solution des mots-croiss

Solutions des mots-croiss

1 2

P O P C O

H Y

D E C

H O

P O E T H E S E S E

D E P

M E C I N E M A

R N

O R A T E U R S

D E

C O U
11

E N D

10

C L

P L

A N

A S S

12

A G
14

E
13

C H E S R I T A G

274

Chapitre 7

8 le pattern Patron de mthode

h Encapsuler g les algorithmesg

Ouais, cest le meilleur des patrons sauf quand il faut descendre dans ce trou. Alors tout dun coup, cest MON problme. Vous voyez ce que je veux dire? Il nest nulle part en vue!

Nous narrtons pas dencapsuler. Nous avons encapsul la cration dobjets et linvocation de mthodes. Nous avons encapsul des interfaces complexes, des canards, des pizzas... Par quoi pourrions-nous continuer? Nous allons
entreprendre dencapsuler des fragments dalgorithmes afin que les sous-classes puissent sy adapter au moment de leur choix. Nous allons mme dcouvrir un principe de conception inspir par Hollywood. nouveau chapitre

275

les recettes du th et du caf sont similaires

Il est temps dabsorber un peu plus de cafine


Certaines personnes ne peuvent pas vivre sans leur caf, dautres ne peuvent pas vivre sans th. Lingrdient commun? La cafine, bien sr! Mais ce nest pas tout: on prpare le th et le caf de manire trs similaire. Cest ce que nous allons vrifier.

Starbuzz f a c u d as des barist n io t a m r ces e fo ement puleus u Manuel d r c z s u rb z. re


v ns Sta ez sui boisso Veuill s e l ! s r a e ar Barist r prp es pou recett

u caf Recette d

Starbuzz

au e de le illant illir u o au bou b e e l r i a F e f s (1) tas le ca ns une ltrer af da (2) Fi c cre u e s l u rser t et d i a l (3) Ve u d outer (4) Aj

La recette du caf ressemble beaucoup celle du th, nestce pas?

e illant d au bou e r i l l l s i n u da ire bo le th e (1) Fa nfuser e tass i n u e r s i n a d h (2) Fa e t rser l ron (3) Ve du cit r e t u o j A ) (4
r emeure vent d et doi s e t cr ont se buzz s es. Star ntiell du caf confide s t e n t e t m e cte rec i r s t e s l Toutes

uzz th Starb u d e t t e c Re u e lea

276

Chapitre 8

le pattern Patron de mthode

crivons rapidement des classes pour le caf et le th (en Java)


Jouons au barista programmeur et crivons du code pour faire du caf et du th. Voici pour le caf:

Voici notre classe Cafe

qui fait du caf.

public class Cafe { void suivreRecette() { faireBouillirEau(); filtrerCafe(); verserDansTasse(); ajouterLaitEtSucre();

rt e caf. Elle so d e t t ce re re Voici not u manuel de formation. tout droit d te au es est implmen p a t es d ne cte. Chacu thode distin moyen dune m

public void faireBouillirEau() { System.out.println(Portage de leau bullition); } public void filtrerCafe() { System.out.println(Passage du caf); } public void verserDansTasse() { System.out.println(Remplissage de la tasse); } public void ajouterLaitEtSucre() { System.out.println(Ajout du lait et du sucre); } }

Chacune de ces mthodes implmente une tape de lalgorithme. Il y a une mthode pour faire bouillir leau, une pour passer le caf, une pour le verser dans la tasse et une pour ajouter du lait et du sucre.

vous tes ici

277

implmentation du th

Et maintenant, le th... Ceci ressemble beaucoup ce que nous venons dcrire dans la classe Cafe. La deuxime et la quatrime mthode sont diffrentes, mais il sagit essentiellement de la mme recette.

public class The { void suivreRecette() { faireBouillirEau(); tremperSachet(); verserDansTasse(); ajouterCitron(); }

public void faireBouillirEau() { System.out.println(Portage de leau bullition); } public void tremperSachet() { System.out.println(Infusion du th); } public void verserDansTasse() { System.out.println(Remplissage de la tasse); } public void ajouterCitron() { System.out.println(Ajout du citron); } } Quand nous avons du code dupliqu, cest le signe quil faut rviser notre conception. Ici, on dirait quil faut extraire les lments communs pour les placer dans une classe de base puisque les recettes du caf et du th sont tellement similaires.

Ces deux mthodes sont spcifiques la classe The.

Remarquez que ces deux mthodes sont exactement les mmes que dans la classe Cafe! Dcidment, il y a de la duplication de code dans lair.

278

Chapitre 8

le pattern Patron de mthode

Problme de conception
Vous avez constat que les classes Cafe et The contenaient pas mal de code dupliqu. Regardez de nouveau leur code et tracez un diagramme de classes dcrivant comment vous pourriez revoir la conception de ces classes afin dliminer les redondances.

vous tes ici

279

premire approche de labstraction

Monsieur, puis-je transformer votre caf (th) en abstraction?


On dirait que nous sommes en prsence dun problme de conception plutt facile avec ces classes Cafe et The. Votre premier essai ressemble peut-tre ceci:

BoissonCafeinee

lirEau() et Les mthodes faireBouilt partages par verserDansTasse() tan es sont dfinies les deux sous-classes, ell dans la superclasse.

Comme la mthode suivreRecet te() diffre dans chaque sous-cla sse est dfinie comme abstraite. , elle

suivreRecette() faireBouillirEau() verserDansTasse()

Chaque sous-classe implmente sa propre recette.

Cafe
suivreRecette() filtrerCafe() ajouterLaitEtSucre()

The
suivreRecette() tremperSachet() ajouterCitron()

Chaque sous-classe redfinit la mthode suivreRecette() et implmente sa propre recette.

e et The cifiques Caf sp es d ho t m Les s sous-classes. restent dans le

A
280

Musclez vos neurones

Avons-nous fait du bon travail avec cette nouvelle conception? Mmmmm, jetons encore un coup dil. Ne sommes-nous pas en train doublier un autre point commun? En quoi les classes Cafe et The sont-elles galement similaires?

Chapitre 8

le pattern Patron de mthode

Poursuivons notre raisonnement


Quel est donc lautre lment commun Cafe et The? Commenons par les recettes.

zz af Starbu c u d e t t e Rec u e lea

nte ouilla llir d eau b e boui l r i a F e (1) tass le caf ns une ltrer af da (2) Fi c cre u e s l rser et du t i a l (3) Ve du outer Recette du th Starbuzz (4) Aj
(1) (2) (3) (4) Faire bouillir de le au Faire infuser le th dans leau bouillante Verser le th dans une tasse Ajouter du citron

Remarquez que les deux recettes appliquent le mme algorithme:

1 2

Faire bouillir de leau. Utiliser leau chaude pour extraire le caf ou le th. Verser la boisson rsultante dans une tasse. Ajouter les supplments appropris la boisson.
Ces deux mthodes ne Ces deux-l sont sont pas abstraites, dj abstraites dans mais elles sont la classe de base. , nt me ple Sim s. ire simila elles sappliquent des boissons diffrentes.

3 4

Alors? Pouvons-nous trouver un moyen dabstraire galement suivreRecette()? Oui. Rflchissons...

vous tes ici

281

abstraire lalgorithme

Abstraire suivreRecette()
Voyons les tapes pour extraire suivreRecette() de chaque sous-classe (autrement dit, les classes Cafe et The)...
1

Premier problme: Cafe utilise les mthodes filtrerCafe() et ajouterLaitEtSucre() tandis que The utilise les mthodes tremperSachet() et ajouterCitron().

Caf
void suivreRecette() { faireBouillirEau(); filtrerCafe(); verserDansTasse(); ajouterLaitEtSucre(); }

Th
void suivreRecette() { faireBouillirEau(); tremperSachet(); verserDansTasse(); ajouterCitron(); }

Rflchissons: il ny a pas grande diffrence entre faire passer du caf et faire infuser du th. Cest mme pratiquement la mme chose.Crons donc un nouveau nom de mthode, par exemple preparer(), et nous utiliserons le mme nom, que nous fassions du caf ou du th. De mme, lajout de lait et de sucre nest pas trs diffrent de lajout de citron: les deux ajoutent des supplments la boisson. Nous pouvons donc crer un nouveau nom de mthode, ajouterSupplements(), pour rsoudre le problme. Notre nouvelle mthode suivreRecette() va donc ressembler ceci:

void suivreRecette() { faireBouillirEau(); preparer(); verserDansTasse(); ajouterSupplements(); }

Nous avons maintenant une mthode suivreRecette(), mais il nous faut ladapter au code. Pour ce faire, nous allons commencer par la superclasse, BoissonCafeinee :

282

Chapitre 8

le pattern Patron de mthode

straite, tout BoissonCafeinee est abde classes. comme dans le modle


public abstract class BoissonCafeinee { final void suivreRecette() { faireBouillirEau(); preparer(); verserDansTasse(); ajouterSupplements(); } abstract void preparer(); abstract void ajouterSupplements();

e suivreRecette()sera Maintenant, la mme mthod ou du th. utilise pour prparer du caf finale parce que nous suivreRecette() est dclare classes puissent redfinir ne voulons pas que les sous- la recette! Nous avons cette mthode et modifier pour preparer() la gnralis les tapes2 et4 s(). boisson et ajouterSupplement
Comme Cage et The traitent ces mthodes diffremment, il va falloir les dclarer abstraites et laisser les sous-classes faire le travail.

void faireBouillirEau() { System.out.println(Portage de leau bullition); } Souvenez-vous: void verserDansTasse() { System.out.println(Remplissage de la tasse); } }

nous avons transfr ces mthodes dans la classe BoissonCafeinee (voir le diagramme de classes).

Enfin, nous devons nous occuper des classes Cafe et The. Comme elles sen remettent maintenant BoissonCafeinee pour grer la recette, elles nont plus qu traiter la prparation et les supplments:
public class The extends BoissonCafeinee { public void preparer() { System.out.println(Infusion du th); } public void ajouterSupplements() { System.out.println(Ajout du citron); } }

e et Cafe Comme dans notre conception, Th e. eine tendent maintenant BoissonCaf

public class Coffee extends BoissonCafeinee { public void preparer() { System.out.println(Passage du caf); } public void ajouterSupplements() { System.out.println(Ajout du lait et du sucre); } }

The doit dfinir les mthodes preparer() et ajouterSupplements() les deux mthodes abstraites de la boisson Cafe galement, sauf que Cafe utilise du caf, du lait et du sucre et non des sachets de th et du citron.

vous tes ici

283

diagramme de classes des boissons cafines

vos crayons Sharpen your pencil

Tracez le nouveau diagramme de classes, maintenant que nous avons transfr limplmentation de suivreRecette() dans la classe BoissonCafeinee.

284

Chapitre 8

le pattern Patron de mthode

Quavons-nous fait?
Nous avons reconnu que les deux recettes taient globalement identiques, mme si certaines tapes demandaient des implmentations Th e diffrentes. Nous avons donc gnralis la ui lli r de le au 1 Fa ire bo recette et nous lavons u a ns le r le sach et da pe place dans la classe em tr e ir 2 Fa de base. e ss e ta le th dans un
3

C af e
1 2 3 4

Faire b o u Fil t re r le Ve rse r le

il li r de l e au

c af s u ne t a s se

Ve rser

c af dan

tron Ajo uter du ci

Ajo u te r d

u s ucre e

t du lait

BoissonCafeinee
gnralise
1 2

Faire bouillir de leau Prparer Verser la boisson dans une tasse Ajouter des supplments

gnralise

sappuie sur les sous-classes pour certaines tapes

3 4

sappuie sur les sous-classes pour certaines tapes

se The Sous-clas

Sous-cla

sse Cafe

2 4

Faire trempe r le sach et Ajou te r du cit ro n

dans le au

nnat BoissonCafeinee co es de ap t s et contrle le ise les al r la recette et -mme, tapes1 et3 elle mais elle sen remet ur les The ou Cafe po . tapes2 et4

2 4

Fi lt re r le caf cre e t du lait Ajo u te r du su

vous tes ici

285

faites connaissance avec le pattern Patron de mthode

Faites connaissance avec Patron de mthode


En substance, nous venons dimplmenter le pattern Patron de mthode. De quoi sagit-il? Observons la structure de la classe BoissonCafeinee: elle contient le patron de mthode.

public abstract class BoissonCafeinee { void final suivreRecette() { } faireBouillirEau();

suivreRecette() est notre patron de mthode. Pourquoi? Parce que: (1) Cest une mthode en soi. (2) Elle sert de patron un algorithme, en loccurrence un algorithme pour prparer des boissons cafines. Dans le patron, chaque tape de lalgorithme est reprsente par une mthode.

preparer();

verserDansTasse();

ajouterSupplements();

Certaines mthodes sont gres par cette classe...

} abstract void preparer(); abstract void ajouterSupplements(); void faireBouillirEau() { // implmentation } void verserDansTasse() { // implmentation } }

...et dautres sont gres par la sous-classe. Les mthodes qui doivent tre fournies par une sous-classe sont dclares abstraites.

Le Patron de mthode dfinit les tapes dun algorithme et permet aux sous-classes de fournir limplmentation dune ou plusieurs de ses tapes.
286
Chapitre 8

le pattern Patron de mthode

Prparons un peu de th...


Voyons les tapes de la prparation dun th et observons le fonctionnement du patron de mthode. Vous allez voir que le patron de mthode contrle lalgorithme. certains points de lalgorithme, il laisse la sous-classe fournir limplmentation des tapes...

Dans les coulisses

Bien. Dabord, il nous faut un objet The... The monThe = new The();

aireBouillirEau(); preparer(); verserDansTasse(); ajouterSupplements();

Puis nous appelons le patron de mthode: monThe.suivreRecette(); qui applique lalgorithme pour prparer des boissons cafines...

La mthode suivreRecette() contrle lalgorithme, personne ne peut le modifier et elle compte sur les sous-classes pour fournir tout ou partie de limplmentation.
BoissonCafeinee

Premirement, on fait bouillir de leau: faireBouillirEau(); ce qui se passe dans BoissonCafeinee.


suivreRecette() faireBouillirEau() verserDansTasse()

Puis nous devons faire infuser le th. Seule la sous-classe sait comment faire: preparer();
The

Maintenant, nous versons le th dans la tasse. Comme on verse toutes les boissons de la mme faon, cela se passe dans la classe BoissonCafeinee : verserDansTasse();

preparer() ajouterSupplements();

Enfin, nous ajoutons les supplments. Comme ils sont spcifiques chaque boisson, cest la sous-classe qui implmente lopration: ajouterSupplements();

vous tes ici

287

que nous a apport le patron de mthode?

Que nous a apport le patron de mthode?

Implmentation de The et Cafe manquant de puissance

Nouvelle BoissonCafeinee branche, booste par le Patron de mthode

Cafe et The mnent la danse: ils contrlent lalgorithme.

La classe BoissonCafeinee mne la danse: elle possde lalgorithme et elle le protge.

Le code est dupliqu entre Cafe et The.

La classe BoissonCafeinee augmente la possibilit de rutilisation entre les sous-classes.

Changer lalgorithme ncessite douvrir les sous-classes et dapporter de multiples modifications.

Lalgorithme rside un seul endroit, et cest uniquement l que les modifications du code ont lieu.

Les classes sont organises selon une structure qui demande beaucoup de travail lorsquil faut ajouter une nouvelle boisson.

La version Patron de mthode fournit un cadre dans lequel on peut insrer dautres boissons cafines. Celles-ci nauront quune paire de mthodes implmenter.

La connaissance de lalgorithme et la faon de limplmenter est rpartie entre plusieurs classes.

La classe BoissonCafeinee centralise la connaissance de lalgorithme et sen remet aux sous-classes pour fournir les implmentations compltes.

288

Chapitre 8

le pattern Patron de mthode

Le pattern Patron de mthode: dfinition


Vous avez vu comment le pattern Patron de mthode fonctionne dans notre exemple de The et de Cafe. Voyons maintenant la dfinition officielle et tudions tous les dtails:

Le pattern Patron de mthode dfinit le squelette dun algorithme dans une mthode, en dlguant certaines tapes aux sous-classes. Patron de mthode permet aux sous-classes de redfinir certaines tapes dun algorithme sans modifier la structure de celui-ci.

Ce pattern est entirement ddi la cration dun patron dalgorithme. Questce quun patron? Comme vous lavez constat, cest simplement une mthode. Plus spcifiquement, cest une mthode qui dfinit un algorithme sous la forme dune suite dtapes. Une ou plusieurs de ces tapes sont dfinies abstraites et sont implmentes par une sous-classe. Cela permet la structure de lalgorithme de demeurer inchange, tandis que les sous-classes fournissent une partie de limplmentation. Observons le diagramme de classes:

Le patron de mthode utilise des oprations primitives pour implmenter un algorithme. Il est dcoupl de limplmentation effective de ces oprations.

La ClasseAbstraite contient le patron de mthode. ...et les versions abstraites des oprations utilises dans le patron de mthode.

ClasseAbstraite
patronMethode() operationPrimitive1() operationPrimitive2()
operationPrimitive1() operationPrimitive2()

ClasseConcrete

rs ir plusieu une o v a y t u Il pe ncretes, chac es ClasseCo tant lensemble dn de implmen ns dont le patro opratio a besoin. mthode

operationPrimitive1(); operationPrimitive2();

La ClasseConcrete implmente les oprations abstraites qui sont appeles quand patronMethode() en a besoin.

vous tes ici

289

le pattern patron de mthode la loupe

Code la loupe
Regardons de plus prs comment la ClasseAbstraite est dfinie, notamment le patron de mthode et les oprations primitives.

Elle est dclare Voici notre classe abstraite. e sous-classe par abstraite et conue pour tr implmentations les classes qui fournissent les des oprations.

abstract class ClasseAbstraite { final void patronMethode() { operationPrimitive1(); operationPrimitive2(); operationConcrete(); } abstract void operationPrimitive1(); abstract void operationPrimitive2(); void operationConcrete() { // implmentation de lopration } }

La Voici le patron de mthode. ale fin e lar mthode est dc de pour empcher les sous-classes de es tap d ce modifier la squen lalgorithme.

Le patron de mthode dfinit la squence dtape s, chacune delles tant reprsente par une mthod e.

Dans cet exemple, deux de oprations primitives doive s tre implmentes par les nt sous-classes concrtes.

Nous avons galement une opration concrte dfinie dans la classe abstraite. Des dtails sur ces types de mthodes dans un moment...

290

Chapitre 8

le pattern Patron de mthode

Code au microscope
Nous allons maintenant regarder dencore plus prs les types de mthode que peut contenir la classe abstraite:

patronMethode() Nous avons modifi un nouvel appel qui inclut maintenant de mthode.

abstract class ClasseAbstraite { final void patronMethode() { operationPrimitive1(); operationPrimitive2(); operationConcrete(); adapter(); } abstract void operationPrimitive1(); abstract void operationPrimitive2(); final void operationConcrete() { // implmentation de lopration } void adapter() {} }

Nous avons toujours nos mthodes primitives. Elles sont abstraites et implmentes par les sous-classes concrtes.

dfinie dans la Une opration concrte est dclare finale classe abstraite. Celle-ci est puissent pas la afin que les sous-classes ne ilise directement redfinir. Elle peut tre utou bien tre utilise par le patron de mthode, par les sous-classes.

Une mthode concrte, mais qui ne fait rien!

Nous pouvons galement avoir des mthodes concrtes qui ne font rien par dfaut. Nous les appelons adaptateurs. Les sous-classes sont libres de les redfinir, mais elles ny sont pas obliges. Nous allons comprendre leur utilit page suivante.

vous tes ici

291

implmenter une mthode adaptateur

Mthodes adaptateurs et Patron de mthode...


Une mthode adaptateur est une mthode qui est dclare dans la classe abstraite, mais laquelle on ne donne quune implmentation vide ou par dfaut. Cela confre aux sous-classes la capacit de sadapter lalgorithme diffrents endroits si elles le veulent; une sous-classe est galement libre dignorer ladaptateur. Les mthodes adaptateurs ont plusieurs emplois. Regardons-en une maintenant. Nous en verrons dautres plus tard:

Avec une mthode adaptateur, je peux redfinir la mthode si je le souhaite. Jai le choix. Si je ne le fais pas, la classe abstraite fournit une implmentation par dfaut.

public abstract class BoissonAvecAdaptateur { void suivreRecette() { faireBouillirEau(); preparer(); verserDansTasse(); if (clientVeutSupplements()) { ajouterSupplements(); } } abstract void preparer(); abstract void ajouterSupplements(); void faireBouillirEau() { System.out.println(Portage de leau bullition); } void verserDansTasse() { System.out.println(Remplissage de la tasse); } boolean clientVeutSupplements() { return true; } }

e petite instruction Nous avons ajout unle succs dpend dune conditionnelle dont clientVeutSupplements(). mthode concrte, client VEUT Si et seulement si le us appelons des supplments, no (). ajouterSupplements

une mthode Ici, nous avons dfini on par dfaut dont limplmentati vide. Cette est (pratiquement) de retourner mthode se contente dautre. true et ne fait rien

Cest une mthode adaptateur parce que la sous-classe peut la redfinir mais quelle ny est pas oblige.

292

Chapitre 8

le pattern Patron de mthode

Utiliser la mthode adaptateur


Pour utiliser la mthode adaptateur, nous la redfinissons dans notre sous-classe. Ici, ladaptateur contrle si la BoissonCafeinee value une certaine partie de lalgorithme; autrement dit, si elle ajoute un supplment la boisson. Comment savons-nous si le client veut un supplment? Il suffit de demander!

public class CafeAvecAdaptateur extends BoissonAvecAdaptateur { public void preparer() { System.out.println(Passage du caf); } public void ajouterSupplements() { System.out.println(Ajout du lait et du sucre); } public boolean clientVeutSupplements() { String reponse = getReponseUtilisateur(); if (reponse.toLowerCase().startsWith(o)) { return true; } else { return false; } } private String getReponseUtilisateur() { String reponse = null; System.out.print(Voulez-vous du lait et du sucre (o/n)? ); BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); try { reponse = in.readLine(); } catch (IOException ioe) { System.err.println(Erreur dES. Choix non propos); } if (reponse == null) { ut du tilisateur sil ve ligne de lu return non; e d an em d e e sur la Ce cod } e et lit lentr lait et du sucr return reponse; } } vous tes ici

nissez Voil o vous redfi ur et te ta ap la mthode ad votre ez ss ni o vous four propre fonctionnalit

Lire la dcision de lutilisateur sur les supplments et retourner true ou false, en fonction de lentre.

commande.

293

lancer le test

Excuter le test
Bien. Leau est en train de bouillir... Voici le code de test dans lequel nous crons un th et un caf bien chauds

public class TestBoisson { public static void main(String[] args) { } } TheAvecAdaptateur theAdapt = new TheAvecAdaptateur(); CafeAvecAdaptateur cafeAdapt = new CafeAvecAdaptateur(); System.out.println(\nPrparation du th...); theAdapt.suivreRecette(); System.out.println(\nPrparation du caf...); cafeAdapt.suivreRecette();

Crer un th. Un caf. Et appeler suivreRecette() sur les deux!

Excutons-le...
Fichier dition Fentre Aide LiberthEgalithFraternith

%java TestBoisson Prparation du th... Portage de leau bullition Infusion du th Remplissage de la tasse Voulez-vous du citron (o/n)? o Ajout du citron Prparation du caf... Portage de leau bullition Passage du caf Remplissage de la tasse Voulez-vous du lait et du sucre (o/n)? n %

Une tasse de th fumante, et oui, bien sr, nous voulons du citron!


de caf bien Et une bonne tasse eillons notre chaud, mais nous survons noir. ligne et nous le pren

294

Chapitre 8

le pattern Patron de mthode Eh bien, jaurais cru quune fonctionnalit comme la question au client aurait pu tre utilise par toutes les sousclasses?

Vous savez quoi? Nous sommes daccord avec vous. Mais vous devez dabord admettre que vous pensiez que ctait un exemple plutt gnial de la faon dont on peu utiliser une mthode adaptateur pour contrler le flot dun algorithme dans la classe abstraite. Daccord? Nous sommes srs que vous voyez bien dautres scnarios ralistes dans lesquels vous pourriez utiliser le patron de mthode et des mthodes adaptateurs dans votre propre code.

Q: R:

questions stupides
Un autre emploi consiste donner lasous-classe une chance de ragir une tape du patron de mthode qui est sur le point de se produire ou qui vient de se produire. Par exemple, une mthode adaptateur comme listeReordonne() permet la sous-classe dexcuter une activit (comme afficher de nouveau une reprsentation lcran) aprs quune liste interne a t rordonne. Comme vous lavez constat, une mthode adaptateur peut galement confrer une sous-classe la capacit de prendre une dcision pour la classe abstraite.

Il ny a pas de

Quand on cre un patron de mthode, comment savoir quand utiliser des mthodes abstraites et quand utiliser des mthodes adaptateurs?

Q: R:

On dirait que je ferais mieux davoir un petit nombre de mthodes abstraites si je ne veux pas avoir trop de travail pour les implmenter dans la sous-classe.

Utilisez des mthodes abstraites quand votre sous-classe DOIT fournir une implmentation de mthode ou une tape de lalgorithme. Utilisez des adaptateurs quand cette partie de lalgorithme est optionnelle. Une sous-classe peut choisir dimplmenter ces mthodes adaptateurs, mais elle ny est pas oblige.

Q: R:

quoi les mthodes adaptateurs sont-elles censes rellement servir?

Q: R:

Une sous-classe doit-elle implmenter toutes les mthodes abstraites de la ClasseAbstraite?

Il est bon den tre conscient lorsquon crit des patrons de mthodes. On y parvient parfois en vitant que la granularit des tapes de lalgorithme soit trop fine. Mais il sagit de toute vidence dun compromis: moins la granularit est fine, moins il y a de souplesse. Noubliez pas non plus que certaines tapes seront optionnelles: vous pouvez donc les implmenter sous forme de mthodes adaptateurs au lieu de classes abstraites, ce qui allge le fardeau impos aux sous classes de votre classe abstraite.

Il y a plusieurs emplois des mthodes adaptateurs. Comme nous venons de le dire, elles peuvent fournir une sous-classe un moyen dimplmenter une partie optionnelle dun algorithme, ou de la sauter si elle nest pas importante pour limplmentation de la sous-classe.

Oui. Chaque sous-classe concrte dfinit lensemble des mthodes abstraites et fournit une implmentation complte des tapes non dfinies de lalgorithme du patron de mthode.

vous tes ici

295

le principe dHollywood Je lai dj dit cent fois et je le rpte encore: ne mappelez pas, je vous appellerai!

Le principe dHollywood
Nous avons un autre principe de conception pour vous. Il sappelle le Principe dHollywood:

Le principe dHollywood
Ne nous appelez pas, nous vous appellerons.

Facile mmoriser, non? Mais quel est le rapport avec la conceptionOO? Le principe dHollywood nous fournit un moyen de prvenir la gangrne des dpendances. Il y a gangrne des dpendances lorsquon a des composants de haut niveau qui dpendent de composants de bas niveau qui dpendent de composants de haut niveau qui dpendent de composants transverses qui dpendent de composants de haut niveau, et ainsi de suite. Quand la gangrne sinstalle, plus personne ne parvient comprendre la faon dont le systme a t conu. Avec le principe dHollywood, nous permettons aux composants de bas niveau de sadapter un systme, mais les composants de haut niveau dterminent quand on en a besoin et comment. Autrement dit, les composants de haut niveau traitent les composants de bas niveau la mode dHollywood: ne nous appelez pas, nous vous appellerons.

Composant de haut niveau

osants de Les comp u peuvent bas nivea r au calcul. participe

s de Mais les composant lent r nt co haut niveau quand et comment.

Composant de bas niveau

Autre composant de bas niveau

Un composant de bas niv eau nappelle jamais un composa de haut niveau directeme nt nt.

296

Chapitre 8

le pattern Patron de mthode

Le principe dHollywood et le Patron de mthode


La connexion entre le principe dHollywood et le pattern Patron de mthode est probablement assez apparente: quand nous appliquons le pattern Patron de mthode, nous disons aux sous-classes: ne nous appelez pas, nous vous appellerons. Comment? Observons de nouveau la conception de notre BoissonCafeinee:

composant BoissonCafeinee est notre ntrle sur co le de haut niveau. Elle a et ne fait lalgorithme de la recette lorsquelle e qu appel aux sous-classes ation dune en a besoin pour limplment mthode.

BoissonCafeinee suivreRecette() faireBouillirEau() verserDansTasse() preparer() ajouterSupplements()

Les clients des bo dpendront de la issons BoissonCafeinee bstraction dpendre dune clau lieu de comme The ou C asse concrte rduit les dpendafe, ce qui du systme global ances au niveau .

Cafe preparer() ajouterSupplements()

The preparer() ajouterSupplements()

Les sous-classes servent uniquemen fournir les dtails t dimplmentation .

pellent The et Cafe nap straite jamais la classe ab avoir directement sans d. t appeles dabor

Musclez vos neurones


Quels autres patterns utilisent le principe dHollywood?

Fabrication, Observateur En voyez-vous dautres? vous tes ici

297

qui fait quoi

questions stupides

Il ny a pas de

Q: R:

Y a-t-il un rapport entre le principe dHollywood et le principe dinversion des dpendances que nous avons vu il y a quelques chapitres?

Le principe dinversion des dpendances nous apprend comment viter lemploi de classes concrtes et travailler autant que possible avec des abstractions. Le principe dHollywood est une technique pour construire des structures et des composants qui permet dadapter les

composants de bas niveau sans crer de dpendance entre ceux-ci et les couches suprieures. La finalit de ces deux principes est donc le dcouplage, mais le principe dinversion nonce de faon beaucoup plus forte et plus gnrale le moyen dviter des dpendances dans la conception. Le principe dHollywood nous offre une technique pour crer des conceptions qui permettent aux structures de bas niveau dinteroprer tout en empchant les autres classes de devenir trop dpendantes delles.

Q: R:

Un composant de bas niveau a-t-il linterdiction dappeler une mthode dun composant de plus haut niveau?

Qui fait quoi ?


g

Pas vraiment. En ralit, un composant de bas niveau finira par appeler une mthode dfinie au-dessus de lui dans la hirarchie dhritage, par la simple vertu de lhritage. Mais nous voulons dviter des dpendances circulaires explicites entre les composants de bas niveau et de haut niveau.

Faites correspondre chaque pattern sa description:

Pattern Patron de mthode

Description Encapsule des comportements interchangeables et utilise la dlgation pour dcider quel comportement utiliser Les sous-classes dcident de la faon dimplmenter les tapes dun algorithme Les sous-classes dcident des classes concrtes crer

Stratgie

Fabrication

298

Chapitre 8

le pattern Patron de mthode

Patrons de mthodes ltat sauvage


Le pattern Patron de mthode est un pattern trs courant et vous en trouverez des quantits dans le monde rel. Mais vous devrez avoir lil, parce quil en existe de nombreuses implmentations qui ne ressemblent pas vraiment sa dfinition officielle. La frquence dapparition de ce pattern sexplique par le fait que cest un excellent outil pour concevoir des structures: la structure contrle lexcution de lalgorithme, mais elle laisse son utilisateur spcifier les dtails de ce qui se passe rellement chaque tape de lalgorithme. Un petit safari va nous permettre dobserver quelques emplois du pattern en pleine nature (bon, daccord, dans lAPI Java)...

En formation, nous tudions les patterns classiques. Mais dehors, dans le monde rel, nous devons apprendre reconnatre les patterns hors contexte. Nous devons galement apprendre reconnatre les variantes des patterns, parce que,dans le monde rel, un trou carr nest pas toujours vraiment un trou carr.

vous tes ici

299

trier avec un patron de mthode

Trier avec Patron de mthode


Quest-ce quon fait trs souvent avec les tableaux? On les trie! Conscients de ce fait, les concepteurs de la classe Java Arrays ont mis notre disposition un patron de mthode bien pratique pour trier. Jetons un coup dil la faon dont cette mthode opre:
ES IR TA ILI A T V U JA

collaborent pour En ralit, nous avons l deux mthodes qui fournir la fonctionnalit de tri.

Nous avons un peu abrg ce code pour faciliter lexplication. Si vous voulez en voir la totalit, tlchargez le source sur le site de Sun et tudiez-le...

e t quune mthod es n , () rt so e, d ho la La premire mt pie du tableau et co e de un e cr i qu nation la mtho ti auxiliaire es d e d u ea bl ta du transmet comme transmet galement la taille r au lle ce E en ) m t( m de co mergeSor la mthode sort tableau et dit premier lment.

public static void sort(Object[] a) { Object aux[] = (Object[])a.clone(); mergeSort(aux, a, 0, a.length, 0); }

La mthode mergeSort() contient lalgorithme de tri et laisse le soin une implmentation de la mthode compareTo() de terminer lalgorithme.

private static void mergeSort(Object src[], Object dest[], int low, int high, int off) {

Voici le patron de mthode.

for (int i=low; i<high; i++){ for (int j=i; j>low && ((Comparable)dest[j-1]).compareTo((Comparable)dest[j])>0; j--) { swap(dest, j, j-1); } compareTo() est la mthode que Ceci est une mthode concrte, } nous devons implmenter pour return; dj dfinie dans la clas hode.

se Arrays.

remplir le patron de mt

300

Chapitre 8

le pattern Patron de mthode

Nous avons des canards trier...


Disons que vous avez un tableau de canards et que vous voulez le trier. Comment procdez-vous? Eh bien, le patron de mthode sort de la classe Arrays nous fournit lalgorithme, mais vous devez lui indiquer comment comparer les canards, ce que vous faites en implmentant la mthode compareTo()... Logique, non?
Non. Ne sommes-nous pas cens sous-classer quelque chose? Je pensais que ctait le but de Patron de mthode. Un tableau ne sous-classant rien, je ne vois pas comment on pourrait utiliser sort().

Nous avons un tableau de canards trier.

Trs juste. Voil le truc: comme les concepteurs de sort() voulaient quon puisse lutiliser dans tous les tableaux, ils ont donc fait de sort() une mthode statique quon peut appeler de nimporte o. Mais, cest vrai, elle fonctionne presque comme si elle tait dans une superclasse. Maintenant, prcisons un dtail : puisque la mthode sort() nest pas dfinie dans notre superclasse, elle a besoin de savoir que vous avez implment la mthode compareTo(), sinon il vous manque llment ncessaire pour complter lalgorithme de tri. Pour rsoudre le problme, les concepteurs ont utilis linterface Comparable. Il vous suffit dimplmenter cette interface, qui contient une seule mthode nomme (surprise): compareTo().

Quest-ce que compareTo()?


La mthode compareTo() compare deux objets et indique si lun est suprieur, infrieur ou gal lautre. sort() sen sert pour comparer les objets du tableau.
Est-ce que je suis plus grand que toi? Je ne sais pas, cest ce que nous dit compareTo().

vous tes ici

301

implmenter Comparable

Comparer des canards et des canards


Bien. Vous savez donc que si vous voulez trier des Canards, vous devez implmenter cette mthode compareTo(). Ce faisant, vous fournissez la classe Arrays ce dont elle a besoin pour complter lalgorithme et trier vos canards. Voici limplmentation pour les canards:

s pas rellement, Souvenez-vous: puisque nous ne sous-classon ble. nous devons implmenter linterface Compara
public class Canard implements Comparable { String nom; int poids; public Canard(String nom, int poids) { this.nom = nom; this.poids = poids; } public String toString() { return nom + pse + poids; }

Nos Canards ont un nom et un poids.

Restons simples: nos canards se contente dafficher leur nom et leur poids! Voil ce dont sort() a besoin...

nt

public int compareTo(Object object) {

Canard autreCanard = (Canard)object; au Canard if (this.poids < autreCanard.poids) { return -1; } else if (this.poids == autreCanard.poids) { return 0; } else { // this.poids > autreCanard.poids return 1; } } }

compareTo() prend un autre Canard et le compare courant (this).


Voici o nous spcifions comment comparer les Canards. Si le Canard courant (this) pse moins que lautreCanard nous retournons -1, sils sont gaux nous retournons 0 ; et si le Canard courant pse plus, nous retournons 1.

302

Chapitre 8

le pattern Patron de mthode

Trions quelques canards


Voici le test pour trier des Canards...

public class TestTriCanards { public static void main(String[] args) { Canard[] canards = { new Canard(Donald, 8), new Canard(Riri, 2), new Canard(Daisy, 7), new Canard(Fifi, 2), new Canard(Picsou, 10), new Canard(Loulou, 2) };

tableau Il nous faut un Ceux-ci de Canards. mal. nont pas lair

Remarquez que nous appelons la mthode statique sort() de la classe Arrays, et que nous lui transmettons nos Canards.

System.out.println(Avant le tri:); afficher(canards); Arrays.sort(canards); System.out.println(\nAprs le tri:); afficher(canards); } public static void afficher(Canard[] canards) { for (int i = 0; i < canards.length; i++) { System.out.println(canards[i]); } } }

Affichons-les pour voir leur nom et leur poids.

Cest le moment de trier!


Affichons-les (encore) pour voir leur nom et leur poids.

Que le tri commence!


Fichier dition Fentre Aide PicsouDoitSuivreUnRgime

%java TestTriCanards Avant le tri: Donald pse 8 Riri pse 2 Daisy pse 7 Fifi pse 2 Picsou pse 10 Loulou pse 2 Aprs le tri: Riri pse 2 Fifi pse 2 Loulou pse 2 Daisy pse 7 Donald pse 8 Picsou pse 10 %

Les Canards en vrac

Les Canards tris

vous tes ici

303

dans les coulisses: trier des canards

Les secrets de la machine trier les canards

Dans

Observons pas pas le fonctionnement du patron les coulisses de mthode sort() de la classe Arrays. Nous verrons comment il contrle lalgorithme, et, certains points, comment il demande nos Canards de fournir limplmentation dune tape... for (int i=low; i<high; i++){

Tout dabord, il nous faut un tableau de Canards:

... compareTo() ... ... swap() ...

Canard[] canards = {new Canard(Donald, 8), ... };

Puis nous appelons le patron de mthode sort() de la classe Arrays et nous lui transmettons nos canards: Arrays.sort(canards); La mthode sort() et sa mthode auxiliaire mergeSort() contrlent la procdure de tri.

La mthode sort() contrle lalgorithme, aucune classe ne peut le changer. sort() compte sur une classe implmentant Comparable pour fournir limplmentation de compareTo().

Pour trier un tableau, il faut comparer tous les lments deux par deux jusqu ce que toute la liste soit trie. Pour comparer deux canards, la mthode sort() sen remet la mthode compareTo() du Canard, puisquelle sait comment faire. La mthode compare To() est appele sur le premier canard et on lui transmet le canard auquel le comparer: canards[0].compareTo(canards[1]);
compareTo()

Canard
toString()

Premier Canard
4

Canard auquel comparer

Si les Canards ne sont pas dans lordre, il sont permuts grce la mthode concrte swap() de la classe Arrays: swap()
sort() swap()

Pas dhritage, contrairement au patron de mthode typique.


Arrays

La mthode sort() continue comparer et permuter les Canards jusqu ce que le tableau soit dans lordre correct!
Chapitre 8

304

le pattern Patron de mthode

questions stupides

Il ny a pas de

Q: R:

Est-ce que cest vraiment le Patron de mthode, ou est-ce que vous cherchez la petite bte?

en laissant aux lments trier la partie comparaison de lalgorithme. Donc, si ce nest pas un patron de mthode classique, cette implmentation est toujours dans lesprit du pattern Patron de mthode. De plus, en liminant la ncessit de sousclasser Arrays pour utiliser cet algorithme, ils ont dune certaine faon rendu le tri plus souple et plus pratique.

Le pattern permet dimplmenter un algorithme et de laisser les sousclasses fournir limplmentation les tapes. Il est clair que ce nest pas le rle de la mthode sort() de la classe Arrays! Mais, comme nous le savons, les patterns du monde rel ne ressemblent pas toujours exactement ceux des manuels. Il faut modifier ces derniers pour quils sadaptent au contexte et respectent les contraintes dimplmentation. Les concepteurs de sort() taient confronts certaines contraintes. En gnral, on ne peut pas sous-classer un tableau Java et ils voulaient que sort() soit utilisable sur tous les tableaux (et chaque tableau est une classe diffrente). Ils ont donc dfini une mthode statique

Q: R:

vous avez raison: comme nous utilisons lobjet Arrays pour trier note tableau, cela ressemble Stratgie. Mais souvenezvous: dans Stratgie, la classe avec laquelle vous composez implmente la totalit de lalgorithme. Celui que Arrays implmente pour trier est incomplet; il a besoin dune classe pour remplir la mthode compareTo() manquante. Cest en cela que cest plutt un Patron de mthode.

Cette implmentation du tri ressemble plutt au pattern Stratgie quau pattern Patron de mthode. Pourquoi le considre-t-on comme un Patron de mthode?

Q: R:

Y a-t-il dautres exemples de patrons de mthode dans lAPI Java?

Vous pensez sans doute cela parce que le pattern Stratgie utilise la composition des objets. Dans un sens,

Oui, vous en trouverez quelques uns. Par exemple, java.io possde dans InputStream une mthode read() que les sous-classes doivent implmenter et elle est utilise par le patron de mthode read(byte b[], int off, int len).

A A

Musclez vos neurones

Nous savons que nous devons prfrer la composition lhritage, daccord? Mais les concepteurs du patron de mthode sort() ont dcid de ne pas utiliser lhritage et dimplmenter sort() comme une mthode statique qui est compose avec un Comparable au moment de lexcution. En quoi est-ce mieux? En quoi est-ce pire? Comment traiteriez-vous ce problme? Les tableaux Java le rendent-ils particulirement dlicat?

Musclez vos neurones

Pensez un autre pattern qui est une spcialisation du patron de mthode. Dans cette spcialisation, on utilise des oprations primitives pour crer et retourner des objets.

vous tes ici

305

ladaptateur paint()

Les adaptateurs de Swing


Continuons notre safari... Soyez vigilant, vous allez voir des JFrames! Si vous navez jamais rencontr de JFrame, cest le conteneur Swing le plus lmentaire, un cadre, et il hrite dune mthode paint(). Par dfaut, paint() ne fait rien parce que cest une mthode adaptateur! En redfinissant paint(), vous pouvez vous insrer dans lalgorithme du JFrame pour afficher sa surface lcran et y incorporer vos propres lments graphiques. Voici un exemple affreusement simple dutilisation dun JFrame et de redfinition de la mthode paint(): Nous tendons JFrame qui contient
ES IR TA ILI UT JAVA

une mthode update() qui contrle lalgorithme pour mettre jour lcran. Nous pouvons nous adapter cet public class MonCadre extends JFrame { algorithme en redfinissant la mthode adaptateur paint(). public MonCadre(String titre) { Ne regardez pas derrire super(titre); le rideau! Ce nest quun this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); peu dinitialisation...
this.setSize(300,300); this.setVisible(true); } public void paint(Graphics graphics) { super.paint(graphics); String msg = Je suis le matre du monde !!; graphics.drawString(msg, 100, 100); }

Lalgorithme de mise jour du JFrame appelle paint(). Par dfaut, paint() ne fait s rien... cest un adaptateur. Nous redfinisson paint() et disons au JFrame dafficher un message dans la fentre.

public static void main(String[] args) { MonCadre monCadre = new MonCadre(Design Patterns Tte la Premire); } }

Voici le message qui est affich dans le cadre puisque nous avons utilis la mthode paint().

306

Chapitre 8

le pattern Patron de mthode

Applets
Dernire tape du safari: lapplet. Vous savez probablement quune applet est un petit programme qui sexcute dans une page web. Toute applet doit sous-classer Applet, et cette classe fournit plusieurs mthodes adaptateurs. Regardons-en quelques-unes:
public class MonApplet extends Applet { String message; public void init() { message = Bonjour, me voil!; repaint(); } public void start() { message = Je dmarre...; repaint(); } public void stop() { message = Oh, on marrte...; repaint(); } public void destroy() { // lapplet sen va... } public void paint(Graphics g) { g.drawString(message, 5, 15); } }
ES IR TA ILI UT JAVA

applet de La mthode adaptateur init()permet l ire fois. faire ce quelle veut pour sinitialiser la prem

classe Applet repaint() est une mthode concrte de la niveau de savoir qui permet aux composants de plus haut que lapplet doit tre redessine.

La mthode adaptateur start() permet lapplet de faire quelque chose quand elle est sur le point dtre affiche sur la page web.
page, La mthode Si lutilisateur va une autre et lapplet peut adaptateur stop() est appele r ses actions. faire ce quil faut pour arrte

Et ladaptateur destroy() est utilis quand lapplet va tre dtruite, par exemple quand on ferme le panneau du navigateur.

mais quel serait lintrt? Nous pourrions afficher quelque chose, hode paint()! Applet H, regardez! Notre vieille amie la mtptateur. lemploie galement comme mthode ada

Les applets concrtes font un usage intensif des mthodes adaptateurs pour fournir des comportements spcifiques. Puisque ces mthodes sont des adaptateurs, lapplet nest pas tenue de les implmenter.
vous tes ici

307

face face: Patron de mthode et Stratgie

Face face :

Le face face de ce soir: Patron de mthode et stratgie comparent leurs mthodes.

Patron de mthode
H, Stratgie, quest-ce que vous faites dans mon chapitre? Je pensais quon allait me coller quelquun de barbant comme Fabrication.

Stratgie

Fabrication
Je men souviendrai!

Nan, cest moi. Mais faites attention, vous tes parents, Fabrication et vous, si je ne mabuse? Je plaisantais! Mais srieusement, quest-ce que vous faites l? Il y a huit chapitres quon na pas entendu parler de vous!

Vous pourriez peut-tre rappeler au lecteur ce que vous faites, depuis le temps.

Jai appris que vous mettiez la touche finale votre chapitre, et je me suis dit que jallais faire une apparition pour voir o vous en tiez. Nous avons beaucoup de points communs, et je me suis dit que je pourrais peut-tre vous aider... Je ne sais pas, mais, depuis le chapitre1, les gens marrtent dans la rue en disant Vous ne seriez pas ce pattern.... Je pense donc quils savent qui je suis. Mais, pour vous faire plaisir, je dfinis une famille dalgorithmes et je les rends interchangeables. Comme chacun deux est encapsul, le client peut utiliser facilement des algorithmes diffrents.

H, voil qui ressemble beaucoup ce que je fais. Mais mon rle est un peu diffrent du vtre. Ma tche consiste dfinir les grandes lignes dun algorithme et laisser mes sous-classes faire le reste du travail. De cette faon, je peux avoir diffrentes implmentations des tapes individuelles dun algorithme tout en gardant le contrle de la structure. On dirait que vous, vous devez abandonner le contrle de vos algorithmes.

Je ne suis pas sr qu abandonner soit le bon terme... Et, en tout cas, je ne suis pas oblig dutiliser lhritage pour implmenter les algorithmes. Grce la composition, je peux offrir aux clients un grand choix dimplmentations. 308

Chapitre 8

le pattern Patron de mthode

Patron de mthode
Je men souviens. Mais jai plus de contrle sur mon algorithme et je ne duplique pas le code. En fait, si toutes les parties de mon algorithme sont identiques lexception de, disons une ou deux lignes, mes classes sont beaucoup plus efficace que les autres. Tout le code commun tant plac dans la superclasse, toutes les sous-classes peuvent le partager.

Stratgie

Vous pourriez tre un peu plus efficace (juste un peu) et ncessiter moins dobjets. Et vous pourriez galement tre un peu moins compliqu par rapport mon modle de dlgation, mais je suis plus souple parce que jutilise la composition. Avec moi, les clients peuvent changer leurs algorithmes au moment de lexcution en utilisant simplement un autre objet Stratgie. Allons, ce nest pas pour rien quon ma choisi pour le chapitre1! Ouais, eh bien jen suis vraiment heureux pour vous, mais noubliez pas que je suis le pattern le plus utilis. Pourquoi? Parce que je fournis une mthode fondamentale la rutilisation du code qui permet aux sous-classes de spcifier des comportements. Je suis sr que vous voyez que cest parfait pour crer des frameworks. Ouais, jimagine... Mais quen est-il de la dpendance? Vous tes beaucoup plus dpendant que moi. Comment donc? Ma superclasse est abstraite. Mais vous dpendez forcment des mthodes implmentes dans votre superclasse, qui font partie de votre algorithme. Moi, je ne dpends de personne: je peux me charger de tout lalgorithme moi-mme! Comme je lai dit, Stratgie, Jen suis vraiment heureux pour vous. Merci dtre pass, mais jai le reste de ce chapitre terminer. Bon, bon, ne soyez pas susceptible. Je vous laisse travailler, mais si jamais vous avez besoin de mes techniques spciales, faites-le moi savoir. Je suis toujours ravi de donner un coup de main. Compris. Ne nous appelez pas, nous vous appellerons.
vous tes ici

309

mots-croiss

Il est de nouveau temps...

4 5

6 9 10 12 13

11

14

15

16

17

Horizontalement 1. Le pattern Stratgie lutilise la place de lhritage. 4. Le pattern Patron de mthode lutilise pour dlguer limplmentation dautres classes. 5. La mthode de JFrame que nous redfinissons pour afficher Je suis le matre du monde. 6. Ne nous appelez pas, nous vous appellerons est le principe d_________. 9. On y met du citron. 13. Un patron de mthode dfinit les tapes dun ___________. 14. Dans ce chapitre, les boissons contiennent de la ___________. 15. Mot-cl Java pour labstraction. 16. Classe qui aime les pages web. 17. Type de tri utilis dans la classe Arrays.

Verticalement 2. Facultatif. 3. Fabrication est une ______________ de Patron de mthode. 7. On y met du lait. 8. Le patron de mthode est gnralement dfini dans une classe ________. 9. sort() est une mthode de ___. 10. Ce pattern a la grosse tte! 11. Dans la classe Arrays, les mthodes ralisant le tri sont des mthodes ________. 12. Notre caf prfr Objectville.

310

Chapitre 8

le pattern Patron de mthode

Votre bote outils de concepteur


Nous avons ajout Patron de mthode votre bote outils. Patron de mthode vous permet de rutiliser du code comme une pro tout en gardant le contrle de vos algorithmes.

Principes OO

Abstraction n Encapsulatio e. ri va ui ce q morphisme Encapsulez itage.Poly r lh n io t ncapsula Hritage Prfrez le


des inter Programmez ions. at implment aiblement de coupler f us vo z ce . or nt Eff ui interagisse les objets q ouvertes oivent tre s la d s se as cl Les mais ferme lextensionn. modificatio ions. Ne . es abstract Dpendez d s des classes concrtes dpendez pa . u vos amis Ne parlez q vous lez pas, nous Ne nous appe appellerons. es faces, non d

Bases de lOO

POINTS DIMPACT
Un patron de mthode dfinit les tapes dun algorithme, dfrant aux sous-classes limplmentation de ces tapes. Le pattern Patron de mthode nous fournit une technique importante pour la rutilisation du code. La classe abstraite du patron de mthode peut dfinir des concrtes, des mthodes abstraites et des mthodes adaptateurs. Les mthodes abstraites sont implmentes par les sous-classes. Les mthodes adaptateurs sont des mthodes qui ne font rien ou qui ont un comportement par dfaut dans la classe abstraite, mais qui peuvent tre redfinies dans une sous-classe. Pour empcher les sous-classes de modifier lalgorithme du patron de mthode, dclarez le patron de mthode final. Le principe dHollywood nous conseille de placer les prises de dcision dans les modules de haut niveau, lesquels dcident quand et comment appeler les modules de bas niveau. Vous rencontrerez beaucoup demplois du pattern Patron de mthode dans le code du monde rel, mais nesprez pas (comme pour tout pattern) quils soient entirement conus dans les rgles. Les patterns Stratgie et Patron de mthode Patterns encapsulent tous deux les algorithmes, lun au moyen de lhritage lautre de la composition. Fabrication est une spcialisation de Patron de mthode.

us principe vouperu a e v u o n Notre ue ce sont les s s rappelle q dcident et quelle classes qui appeler les sous- in, ne doivent lorsquon en a beso classes que ollywood. comme H
Notre nouveau principe vous rappelle que ce sont les super-classes qui dcident et quelles ne doivent appeler les sousclasses que lorsquon en a besoin, comme Hollywood.

O Patterns O lation ille une ree fa it ef inm init-und esurs,

f e dsi de n uc unr pulu farcf c -e re an ee h t ie hu tn ct a a v in tg t lep r ab e e su n ae s g s a Str y c a it t s. O e ace he ir es in t cn le ep a r b in d t s, u f t a e e n e D je g m e t b n u h a a o m je it h r b n it r l n c o n o u in r p io e c e e lg p t e t f .n sse na uu a tr sd D daenD t e sq urec ic m nis d sanlg r d r ,n e,a n iq l lo t eu n b e d rq m , in n a it m je io e e e a sna hn b il p t o le b it is a y r a a n d t s d ic m u e r e o e b ap ux ia o nd iuix ue je fF uo sla s io u u b qf ded qt F t jo ere ot t q s x e a n u r m it u n e c e o r is e t d c h e d li n m c la p s n c a tmme u r t t r io s le ie o a e u n e e t g t d o s j d g ie poeinco p e a , b tta n eset r ix n o c n n u io la et heuor t cso n tss c la m Str re t t ut n m sso esqu r lu da e le a ue le un ou i oe d un g s rm so o psa n if eit f in p ss t e x ul u S o n p la n ps a n c u d io ace ca t t e in t t la se c t en n a r so n m e e ic ie a r ie so t c r is m e n b st e in p e x r la a a l vare u u in e d nIl n . a F st n p t io it ge .erf e . r. Fabe le a t nse t riq u t t niv e ra a rin era m ie a m t sa ic c t n r ve e m r n t is m n . io p u e a n o s o t la t c c iq u n C st e le ia t e pa c in q a c is l n le a il a t m a r i f b ie o a s d r st c ns n u t lo e n lut e e n nr e g t a ss sses s et aualt tinai e rm in lar t st a p oile ce anl y e eo cria in t is u ncio n p r or g eta u s sd t st d l it o d eut au nn d in ,t ra lf A n t e ,ff ues c ep la u ru o d uit je es a e a ue ob t et dcio ss n in g qu un cD lacn lss re am e n d edt f d riv et e r a r e e d f ucna e t .. d en p e s a in r la ss r f c F s u t la e if e eun e c li e d d t n a a d e r u le n n t s. o et d pa u b p e n , h a s m ss e es t io d se nt la t t n c A ie c e m qu cllass Lat it e .tul n us-des c e ll r t s.P e re dL fon nl e eli da e ue sf as.ns une f q dns o if desdso e sohu r neif uic d t d m lo ao pin lluet it een rr le ca er ct a es sous-t rn r io da o a,alg at .tll eta ab eo r r euspes i u m op u c d q st es e e u d d sy s nt a t ee e it s da d t e a le in iv ili e a f n ss u ib t u t r sqt ladr rs claletan d u e c a ve e h esso t ir s n a a lu f u p le g e .. r l u e e d p e et d is c o s n il a rm f peu rn t hpoade, e lus t ou etr ie plus,na in ae rt t ac s. ntde mth ir m le ib aef p Pila p eo m s. m c ss in st la c s sy e c ssedfin a u u f so so r e x t u n di a classes de r hme sans su so x u a orit permet apes dun alg e. certaines t structure de celui-ci. la r ie if mod

vous tes ici

311

solutions des exercices

vos crayons Sharpen your pencil

Solutions des exercices


preparer()

Tracez le nouveau diagramme de classes maintenant que nous avons transfr limplmentation de suivreRecette() dans la classe BoissonCafeinee.
BoissonCafeinee
suivreRecette() faireBouillirEau() verserDansTasse() preparer() ajouterSupplements()

Cafe
preparer() ajouterSupplements()
ajouterSupplements()

The

Qui fait quoi ?


g

Faites correspondre chaque pattern sa description:

Pattern Patron de mthode

Description Encapsule des comportements interchangeables et utilise la dlgation pour dcider quel comportement utiliser Les sous-classes dcident de la faon dimplmenter les tapes dun algorithme. Les sous-classes dcident des classes concrtes crer.

Stratgie

Fabrication

312

Chapitre 8

le pattern Patron de mthode

Solutions des mots-croiss


1

O P T

S P

E C I A

I O N N

L I S

D
10

8A

T R I

E L
12

A S H M R N E
16 15

B S T R
11 S

F E

13

A T

T A C T I Q U E

T
14

A R B U Z Z

I O N

A T

A I

E G I

T E

17

vous tes ici

313

9 les patterns Itrateur et Composite

Des collections h bien gres g

Regardez comment mes collections sont bien encapsules!

Il y a des quantits de faon de placer des objets dans une collection. Tableaux, piles, listes, tables de hachage: vous avez le choix. Chacune
a ses avantages et ses inconvnients. Mais il y aura toujours un moment o votre client voudra oprer des itrations sur ces objets. Allez-vous alors lui montrer votre implmentation? Esprons que non! Ce serait un manque de professionnalisme absolu. Eh bien vous navez pas besoin de risquer votre carrire. Vous allez voir comment autoriser vos clients procder sans mme jeter un coup dil la faon dont vous stockez vos objets. Vous apprendrez galement crer des super collections dobjets qui peuvent parcourir des structures de donnes impressionnantes dun seul trait. Et si cela ne suffit pas, vous allez dcouvrir une chose ou deux sur les responsabilits des objets. nouveau chapitre

315

dernire minute...

Le scoop de lanne: fusion de la Cafeteria et de la Crperie dObject ville


Voil une excellente nouvelle! Maintenant, nous allons pouvoir trouver les dlicieuses crpes de la crperie dObjectville et les succulents plats de sa Cafeteria au mme endroit. Mais on dirait quil y a comme un lger problme...

Ils veulent utiliser les menus de ma Crperie pour le brunch et le menu de la Cafeteria pour le djeuner. Nous nous sommes mis daccord sur une implmentation des plats...

Mais nous ne pouvons pas nous mettre daccord sur limplmentation des menus. Ce charlot-l utilisait une ArrayList pour en stocker les lments et jutilisais un tableau. Aucun de nous deux naccepte de changer son implmentation... Nous avons bien trop de code existant qui en dpend.

Lon

Nol

316

Chapitre 9

les patterns Itrateur et Composite

Observons les plats


Au moins, Lon et Nol sont daccord sur limplmentation des lments des menus, les Plats. Observons les plats de chaque menu et jetons galement un coup dil limplmentation.

toutes sortes de Le menu de la cafeteria a ui de la crperie e cel plats pour le dner alors qu h. Chaque plat a un unc br le ur a plein de plats po prix nom, une description et un

public class Plat { String nom; String description; boolean vegetarien; double prix; public Plat(String nom, String description, boolean vegetarien, double prix) { this.nom = nom; this.description = description; this.vegetarien = vegetarien; this.prix = prix; } public String getNom() { return nom; } public String getDescription() { return description; } public double getPrix() { return prix; } public boolean estVegetarien() { return vegetarien; } }

Salade printa nire Salade verte, tomates, conc 2. ombre, olives, 99 pommes de te rre Salade paris ienne Salade verte, 2.99 Baco tomates, poul n with et, emmental Soupe du jour Bol de soupe 3.29 du jour et cro Cr p Quiche aux fr to ill nsegr l s uf uits de mer Crpe avec Pte brise, cr uf au3. ev pl at ou brouill 05 Quiche aux p ettes, moules, champignon s inards Cr p e complte Pte feuillete, pommes de te Crp rre, pe av frache in arec ds, au cruf m e plat et jambon Crpe forest ire Myrtilles frach es et sirop de myrtille Crpe du chef Crme frache et fruits rouges au choix

Cafeteria dObjectvil le

Crperie dObjectville
2.99 2.99 3.49 3.59

Un Plat comprend un statut pour indiquer nom, une description, un et un prix. On transmsil convient aux vgtariens constructeur pour ini et toutes ces valeurs au tialiser le Plat.

Ces mthodes get vous permettent daccder aux champs de llment de menu.

vous tes ici

317

deux menus

Les implmentations de Lon et Nol


Voyons maintenant lobjet de la dispute de Lon et Nol. Ils ont tous deux investi beaucoup de temps et de code dans la faon de stocker les plats, et dnormes quantits de code dpendent de celle-ci.
Jai utilis une ArrayList pour pouvoir ajouter facilement des plats mon menu.

le Voici comment Lon a implment menu de la crperie.


{ public class MenuCreperie implements Menu { ArrayList plats; public MenuCreperie() { plats = new ArrayList(); ajouterPlat(Crpe loeuf, Crpe avec oeuf au plat ou brouill, true, 2.99); ajouterPlat(Crpe complte, Crpe avec oeuf au plat et jambon, false, 2.99); ajouterPlat(Crpe forestire, Myrtilles fraches et sirop de myrtilles, true, 3.49); ajouterPlat(Crpe du chef, Crme frache et fruits rouges au choix, true, 3.59); cre un nouvel n o L , t la p } un ent Pour ajouter i transmet chaque argum public void ajouterPlat(String nom, String description, objet Plat, lu ans lArrayList. boolean vegetarien, double prix) puis linsre d { Plat plat = new Plat(nom, description, vegetarien, prix); plats.add(plat); } public ArrayList getPlats() { return plats; } // autres mthodes }

Lon utilise une ArrayList pour stocker ses plats.


Chaque plat est ajout lArrayLis dans le constructeur. Chaque Plat a un nom, une sil description, un flag indiquant prix. est vgtarien ou non et un t, ici,

La mthode getPlats() retourne la liste des plat

s.

end de cette Lon a un tas dautre code qui dp Il ne veut pas ist. ayL Arr implmentation avec une devoir TOUT rcrire!

318

Chapitre 9

les patterns Itrateur et Composite Haah! Une Arraylist... Jai utilis un VRAI tableau pour pouvoir contrler la taille maximale de mon menu et accder mes Plats sans coercition de type.

Et
public class MenuCafeteria implements Menu { { static final int MAX_PLATS = 6; int nombreDePlats = 0; Plat[] plats; public MenuCafeteria() { plats = new Plat[MAX_PLATS];

l implmente le voici comment No

menu de la Cafet

eria.

il utilise un tableau Nol adopte une approche diffrente: son menu et viter de afin de contrler la taille maximale de devoir sous-typer ses objets.

ajouterPlat(Salade printanire, Salade verte, tomates, concombre, olives, pommes de terre, true, 2.99); ajouterPlat(Salade parisienne, Salade verte, tomates, poulet, emmental, false, 2.99); ajouterPlat(Soupe du jour, Soupe du jour et crotons grills, false, 3.29); ajouterPlat(Quiche aux fruits de mer, Pte brise, crevettes, moules, champignons, false, 3.05); // ajouter ici dautres plats ajouterPlat() accepte tous les }

Comme Lon, Nol cre les lments de son menu dans le constructeur, grce la mthode auxiliaire ajouterPlat().

public void ajouterPlat(String nom, String description, boolean vegetarien, double prix) { Plat plat = new Plat(nom, description, vegetarien, prix); if (nombreDePlats >= MAX_PLATS) { System.err.println(Dsol, le menu est plein! Impossible dajouter un plat.); } else { Nol veut explicitement que son menu ne dpasse plats[nombreDePlats] = plat; nombreDePlats = nombreDePlats + 1; pas une taille donne (probablement pour ne pas } avoir trop de recettes mmoriser). } public Plat[] getPlats() { return plats; } // autres mthodes }

arguments en instancie et t Pla un er ncessaires pour cr ons pas un. Elle vrifie galement que nous nav atteint les limites du tableau.

getPlats() retourne le tableau de plats. Comme Lon, Nol a un tas de code qui dpend de limplmentation de son menu sous forme de tableau. Il est bien trop occup faire la cuisine pour tout rcrire.
vous tes ici

319

une serveuse compatible Java

Quel est le problme si les deux menus sont reprsents diffremment?


Pour voir en quoi deux reprsentations diffrentes constituent un problme, essayons dimplmenter un client qui utilise les deux menus. Imaginez que vous ayez t embauch par la nouvelle socit constitue par le nouveau propritaire de la Cafeteria et de la Crperie pour crer une serveuse compatible Java (nous sommes Objectville, aprs tout). Les spcifications prcisent que celle-ci peut afficher un menu la demande des clients, et mme vous indiquer si un plat est vgtarien sans avoir demander au chef. Quelle innovation! Examinons les spcifications, puis nous parcourrons les tapes ncessaires pour les implmenter...

use se m La serve Java.

et

Les spcifications de la ser veuse compatible Java


code Alice va : -nom de Ja le ib at mp Serveuse co ) afficherMenu( plats au menu he tous les fic af runch() ch afficherMenuB menu du brun fiche que le af n ejeuner() jeuner afficherMenuD le menu du d e qu he fic af - n egetarien() gtariens afficherMenuV les plats v us to he fic - af ne vrai arien(nom) plat, retour estPlatVeget le nom dun ux nn fa do ne t ur an - t n, reto tarien, sino g v t es at si le pl

Les spcifications de la Serveuse

320

Chapitre 9

les patterns Itrateur et Composite

Commenons par voir comment nous implmenterions la mthode afficherMenu(): Pour afficher tous les plats de chaque menu, vous aurez besoin dappeler la mthode getPlat() de MenuCreperie et de MenuCafeteria pour extraire leurs plats respectifs. Notez que chacune delles retourne un type diffrent:
MenuCreperie menuCreperie = new MenuCreperie(); ArrayList platsBrunch = menuCreperie.getPlats(); MenuCafeteria menuCafeteria = new MenuCafeteria(); Plat[] platsDejeuner = menuCafeteria.getPlats();

e La mthode sembl s identique, mais le des appels retournent types diffrents.

Maintenant, pour afficher les plats du MenuCreperie, nous devrons itrer sur les lments de lArrayList nomme platsBrunch. Et pour afficher ceux de la Cafeteria, nous parcourrons le tableau.
for (int i = 0; i < platsBrunch.size(); i++) { Plat plat = (Plat)platsBrunch.get(i); System.out.print(plat.getNom() + ); System.out.println(plat.getPrix() + ); System.out.println(plat.getDescription()); } for (int i = 0; i < platsDejeuner.length; i++) { Plat plat = platsDejeuner[i]; System.out.print(plat.getNom() + ); System.out.println(plat.getPrix() + ); System.out.println(plat.getDescription()); }

Limplmentation montre son nez: les plats du brunch sont dans une ArrayList et ceux du djeuner sont dans un tableau.
Maintenant, nous devons crire deux boucles diffrentes pour parcourir les deux implmentations des menus...
...une boucle pour lArrayList... et une autre pour le tableau.

Limplmentation de toute autre mthode de la serveuse sera une variation sur ce thme. Nous devrons toujours lire les deux menus et utiliser deux boucles pour parcourir leurs lments. Si nous fusionnons avec un autre restaurant et que son implmentation est diffrente, il nous faudra alors trois boucles.

vous tes ici

321

quel est lobjectif?

vos crayons Sharpen your pencil


Par rapport notre implmentation dafficherMenu(), quelles sont les affirmations qui sont vraies?

A. Nous codons pour des implmentations


concrtes de MenuCreperie et de MenuCafeteria, non pour une interfaces.

D. Pour chaque menu, la Serveuse doit


connatre la reprsentation interne de sa collection de plats, ce qui viole lencapsulation.

B. La Serveuse nimplmentant pas lAPI


Java Waitress, elle nadhre pas un standard.

E. Nous avons du code dupliqu: la


mthode afficherMenu() a besoin de deux boucles spares pour parcourir les deux types de menus. Et si nous ajoutions un troisime menu, il nous faudrait une nouvelle boucle.

C. Si nous dcidions de passer de lemploi


de MenuCafeteria un autre type de menu qui implmenterait sa liste de plats sous forme de Hashtable, le code de Serveuse ncessiterait beaucoup de modifications.

F. Limplmentation ntant pas base sur


MXML (Menu XML), elle ne prsente pas linteroprabilit dsirable.

Et maintenant?
Nol et Lon nous mettent dans une position difficile. Ils ne veulent pas modifier leur implmentation parce que cela les obligerait rcrire une grande quantit de code dans leur classe Menu respective. Mais si lun des deux ne cde pas, nous aurons la tche dimplmenter une Serveuse qui sera difficile maintenir, sans parler dextensibilit. Ce serait vraiment gnial si nous pouvions trouver un moyen qui leur permettrait dimplmenter la mme interface pour leurs menus (leurs approches sont dj assez semblables, sauf en ce qui concerne le type de retour de leur mthode getPlats()). Ainsi, nous pourrions minimiser les rfrences concrtes dans le code de Serveuse, et probablement nous dbarrasser du problme des deux boucles ncessaires pour parcourir les deux menus. Bonne ide, non? Mais comment procder?

322

Chapitre 9

les patterns Itrateur et Composite

Pouvons-nous encapsuler litration?


Si nous avons appris une seule chose dans ce livre, cest encapsuler ce qui varie. Ici, la variation est vidente: cest litration provoque par la diffrence entre les collections dobjets retournes par les menus. Mais pouvons-nous lencapsuler? Approfondissons cette ide...
1

Pour parcourir les plats du brunch, nous appelons les mthodes size() et get() sur lArrayList :

for (int i = 0; i < platsBrunch.size(); i++) { Plat plat = (Plat)platsBrunch.get(i); }


get(1) get(0) get(2) get(3)

ArrayList

get() nous permet de parcourir chaque lment.

Plat

Plat

Plat

Plat

Une ArrayList de Plats

Et pour parcourir les plats du djeuner, nous utilisons lattribut length du tableau et loprateur [] pour accder aux lments du tableau Plat. platsDejeuner[0]

Array
1
Plat

for (int i = 0; i < platsDejeuner.length; i++) { platsDeje Plat plat = platsDejeuner[i]; uner[1] } p l at s Dejeu
p l at sDe

2
Plat

ner[2
er[3 ]

Nous utilisons les indices du tableau pour parcourir les plats.

jeun

3
Plat

4
Plat

Un tableau de Plats.
vous tes ici

323

encapsuler litration

Nous demandons au menuBrunch un itrateur sur Iterateur iterateur = menuBrunch.creerIterateur(); ses Plats. Et tant quil reste des lments... while (iterateur.encore()) { Plat plat = (Plat)iterateur.suivant(); } suivant() nous accdons au suivant.

Et si nous crions maintenant un objet, appelons-le Iterateur, qui encapsule litration sur une collection dobjets? Essayons avec lArrayList.

It

get(2)
e ra
teur

get(3)

get(1) get(0)

Le client appelle simplement encore() et suivant(). Dans les coulisses, litrateur appelle get() sur lArrayList.

ArrayList

Plat

Plat

Plat

Plat

Procdons de mme avec le tableau:

Iterateur iterateur = menuDejeuner.creerIterateur(); while (iterateur.encore()) { Plat plat = (Plat)iterateur.suivant(); }

Oaouh! Ce code est exactement le mme que celui de menuBrunch.


Mme situation: le client se contente dappeler encore() et suivant(). Dans les coulisses, litrateur parcourt le tableau.

suivant() platsDejeuner[0]
platsDeje
Ite teu ra

Array
1
Plat

uner[1]

p l at s
p l at

2
Plat

Dejeu
jeun

sDe

ner[2
]

3
Plat

er[3

4
Plat

324

Chapitre 9

les patterns Itrateur et Composite

Faites connaissance avec le pattern Itrateur


Eh bien, on dirait que notre projet dencapsulation de litration pourrait bien fonctionner pour de bon; et comme vous lavez sans doute dj devin, cest grce un design pattern nomm le pattern Itrateur. La premire chose que vous devez savoir sur le pattern Itrateur, cest quil sappuie sur une interface Iterateur. Voici une interface possible:

<<interface>> Iterateur
encore() suivant()

La mthode encore() nous indique sil reste des lments parcourir dans lagrgat.

La mthode suivant() retourne lobjet suivant dans lagrgat.


Maintenant, une fois que nous disposons de cette interface, nous pouvons implmenter des itrateurs pour nimporte quelle sorte de collection dobjets: tableaux, listes, tables de hachage choisissez votre collection prfre. Disons que nous voulons implmenter un itrateur pour le tableau utilis dans le MenuCafeteria. Il ressemblerait ceci:
<<interface>> Iterateur
encore() suivant()

Quand nous parlons de COLLECTION, il sagit simplement dun groupe dobjets. Ceux-ci peuvent tre stocks dans diffrentes structures de donnes telles que des listes, des tableaux, des tables de hachage, mais ces structures sont toujours des collections. Parfois, nous les appelons galement des AGRGATS.

IterateurMenuCafeteria
encore() suivant()

IterateurMenuCafeteria est une implmentation dIterateur qui sait comment parcourir un tableau de Plats.

Poursuivons. Nous allons implmenter cet itrateur et ladapter MenuCafeteria pour voir comment il fonctionne...

vous tes ici

325

crer un itrateur

Ajoutons un itrateur MenuCafeteria


Pour ajouter un itrateur MenuCafeteria, nous devons dabord dfinir linterface Iterateur :

public interface Iterateur{ boolean encore(); Object suivant(); }

Voici nos deux mthodes: len la mthode encore() retourne un booents qui indique sil reste ou non des lm parcourir...
...et la mthode suiva retourne llment sunt() ivant.

Et maintenant, nous devons implmenter un itrateur concret qui fonctionne avec le menu de la Cafeteria:

Nous implmentons linterface Iterateur.

public class IterateurMenuCafeteria implements Iterateur { e la position Plat[] elements; position mmoris ration sur int position = 0; courante de lit public IterateurMenuCafeteria(Plat[] elements) { this.elements = elements; } public Object encore() { Plat plat = elements[position]; position = position + 1; return plat; }

le tableau.

Le constructeur accepte le tableau de plats que nous allons parcourir. La mthode suivant() retourne llment suivant dans le tableau et incrmente position.

public boolean suivant() { if (position >= elements.length || elements[position] == null) { return false; } else { return true; } } }

La mthode encore() vrifie que nous avons lu tous les lments du tableau et retourne vrai sil en reste parcourir.
Chapitre 9

326

Comme le chef y est all de bon cur et a allou un tableau qui a une taille maximale, nous devons non seulement vrifier que nous sommes la fin du tableau, mais aussi que le dernier lment est null, ce qui indique quil ny a plus dlments.

les patterns Itrateur et Composite

Revoir le menu de la cafeteria avec Itrateur


Bien, nous avons un itrateur. Il est temps de linsrer dans MenuCafeteria; il suffit dajouter une seule mthode pour crer un IterateurMenuCafeteria et le retourner au client:

{ public class MenuCafeteria { static final int MAX_PLATS = 6; int nombreDePlats = 0; Plat[] plats; // code du constructeur // code de la mthode ajouterPlat public Plat[] getPlats() { return plats; } public Iterateur creerIterateur() { return new IterateurMenuCafeteria(plats); } // autres mthodes du menu }

Nous nallons plus avoir besoin de la mthode getPlats() et, en fait, nous nen voulons pas parce quelle expose notre implmentation interne!

Voici la mthode creerIterateur(). Elle cre un IterateurMenuCafeteria partir du tableau de plats et le retourne au client.

Nous retournons linterface Iterateur. Le client na pas besoin de savoir comment les plats sont grs dans le MenuCafeteria, pas plus quil na besoin de savoir comment IterateurMenuCafeteria est implment. Il lui suffit dutiliser litrateur pour parcourir les lments du menu.

Exercice
Continuez. Implmentez un IterateurCreperie vous-mme et apportez les modifications ncessaires pour lincorporer MenuCreperie.

vous tes ici

327

les itrations de la serveuse

Modifier le code de la Ser veuse


Nous devons maintenant intgrer le code de litrateur la Serveuse. Du mme coup, nous devrions pouvoir nous dbarrasser dun certain nombre de redondances. Lintgration est simple: nous crons dabord une mthode afficherMenu() qui accepte un Iterateur, puis nous appelons la mthode creerIterateur() sur chaque menu pour extraire litrateur et le transmettre la nouvelle mthode.

Revue et corrige avec Iterateur.

public class Serveuse { MenuCreperie menuCreperie; MenuCafeteria menuCafeteria;

Dans le constructeur, la Serveuse prend les deux menus.


La mthode afficherMenu() cre maintenant deux itrateurs, un pour chaque menu.

public Serveuse(MenuCreperie menuCreperie, MenuCafeteria menuCafeteria) { this.menuCreperie = menuCreperie; this.menuCafeteria = menuCafeteria; } public void afficherMenu() { Iterateur iterateurCrepe = menuCreperie.creerIterateur(); Iterateur iterateurCafet = menuCafeteria.creerIterateur(); System.out.println(MENU\n----\nBRUNCH); afficherMenu(iterateurCrepe); System.out.println(\nDJEUNER); afficherMenu(iterateurCafet); Tester sil reste } private void afficherMenu(Iterator iterateur) { while (iterateur.encore()) { Plat plat = (Plat)iterateur.suivant(); System.out.print(plat.getNom() + , ); System.out.print(plat.getPrix() + --); System.out.println(plat.getDescription()); } } // autres mthodes }

Puis elle appelle la mthode afficherMenu() surcharge avec chaque itrateur. La mthode afficherMenu() surcharge utilise litrateur pour parcourir les lments du menu et les afficher.

des lments.

Accder llment suivant. Utiliser llment pour obtenir nom, prix et description et les afficher.

Notez que nous navons plus quune boucle.

328

Chapitre 9

les patterns Itrateur et Composite

Tester notre code


Il est temps de tester tout cela. crivons le code du test et voyons comment la Serveuse fonctionne...

Nous crons dabord les nouveaux menus.


public class TestMenu { public static void main(String args[]) { MenuCreperie menuCreperie = new MenuCreperie(); MenuCafeteria menuCafeteria = new MenuCafeteria(); Serveuse serveuse = new Serveuse(menuCreperie, menuCafeteria); serveuse.afficherMenu(); } }

Et nous les affichons.

Puis nous crons une Serveuse et nous lui transmettons les menus.

Excutons le test...
Fichier dition Fentre Aide OmeletteAuJambon

% java TestMenu MENU ---BRUNCH Crpe luf, 2.99 -- Crpe avec uf au plat ou brouill Crpe complte, 2.99 -- Crpe avec uf au plat et jambon Crpe forestire, 3.49 -- Myrtilles fraches et sirop de myrtille Crpe du chef, 3.59 -- Crme frache et fruits rouges au choix

DEJEUNER Salade printanire, 2.99 -- Salade verte, tomates, concombre, olives, pommes de terre Salade parisienne, 2.99 -- Salade verte, tomates, poulet, emmental Soupe du jour, 3.29 -- Soupe du jour et crotons grills Quiche aux fruits de mer, 3.05 -- Pte brise, crevettes, moules, champignons Quiche aux pinards, 3.99 -- Pte feuillete, pommes de terre, pinards, crme frache %

Dabord nous parcourons le menu de crpes. Puis le menu du djeuner, tout cela avec le mme code.

vous tes ici

329

avantages dun itrateur

Quavons-nous fait jusquici?


Pour commencer, nous avons fait le bonheur des cuisiniers dObjectville. Ils ont rgl leurs diffrends et conserv leurs implmentations. Une fois que nous leur avons eu fourni un IterateurMenuCreperie et un IterateurMenuCafeteria, il leur a suffi dajouter une mthode creerIterateur() et le tour tait jou. Nous nous sommes rendu service par la mme occasion. Le code de la Serveuse sera maintenant bien plus extensible et facile maintenir. Voyons exactement ce que nous avons fait et rflchissons aux consquences:

Gnial! Rien modifier dans le code sauf lajout dune mthode creerIterateur().

Crpe vgtarienne

Implmentation de Serveuse difficile maintenir


Les Menus sont mal encapsuls: on constate que la Cafeteria utilise une ArrayList et la crperie un tableau.

Nouvelle Serveuse branche, booste avec Itrateur


Les implmentations des menus sont maintenant bien encapsules. La Serveuse na aucune ide de la faon dont les Menus grent leur collection de plats. Une seule boucle suffit. Elle gre de manire polymorphe nimporte quelle collection dlments tant quelle implmente Iterateur. La Serveuse utilise maintenant une interface (Iterateur).

Il nous faut deux boucles pour itrer sur les Plats.

La Serveuse est lie des classes concrtes: (Plat[] et ArrayList).

La Serveuse est lie deux classes Menu concrtes diffrentes, mme si leurs interfaces sont presque identiques.

Les interfaces Menu sont exactement identiques, et, oh Nous navons toujours pas dinterface commune, ce qui signifie que la Serveuse dpend toujours de deux classes Menu concrtes. Mieux vaudrait arranger cela.

330

Chapitre 9

les patterns Itrateur et Composite

La conception actuelle...
Avant de faire le mnage, formons-nous une vue densemble de la conception actuelle.

ntent Ces deux menus implmesemble de exactement le mme en lmentent mthodes, mais ils nimp Nous allons pas la mme interface. la serveuse corriger cela et librer x menus de toute dpendance au concrets.

Itrateur permet de dcoupler la Serveuse de limplmentation relle des classes concrtes. Elle na pas besoin de savoir si un Menu est implment avec un tableau, une ArrayList ou des Post-It. Une seule chose lintresse: disposer dun itrateur pour pouvoir itrer.

Nous utilisons maintenant une interface Iterateur commune et nous avons implment deux classes concrtes.

MenueCreperie
plats

Serveuse
afficherMenu()

<<interface>> Iterateur
encore() suivant()

creerIterateur()

MenuCafeteria
plats

creerIterateur()

IterateurMenuCreperie
encore() suivant()

IterateurMenuCafeteria
encore() suivant()

Notez que litrateur nous fournit un moyen parcourir les lments dun agrgat sans forc de dernier encombrer sa propre interface de er ce un tas de mthodes pour prendre en charge tout la navigation dans ses lments. Cela permet gal limplmentation de litrateur de rsider ement en dehors de lagrgat: autrement dit, nous avons enca psul litration.

entent enuCafeteria implm MenuCreperie et M creerIterateur(); ils sont la nouvelle mthodecration de litrateur pour responsables de la s de menus respectives. leurs implmentation

vous tes ici

331

amliorer litrateur

Apportons quelques amliorations...


Bien. Nous savons que les interfaces de MenuCreperie et de MenuCafeteria sont exactement identiques, mais nous navons pourtant pas dfini dinterface commune. Cest ce que nous allons faire maintenant, ce qui va allger un peu plus le code de la Serveuse. Vous vous demandez peut-tre pourquoi nous nutilisons pas linterface Iterator de Java. Nous avons procd ainsi pour que vous voyiez comment construire un itrateur ex nihilo. Cela fait, nous allons maintenant adopter linterface Iterator standard, parce que nous disposerons de beaucoup plus de puissance en implmentant cette dernire la place de notre propre interface maison. Quelle sorte de puissance? Vous nallez pas tarder le savoir. Examinons dabord linterface java.util.Iterator:
<<interface>> Iterator
hasNext() next() remove())

On dirait exactement notre dfinition prc

dente.

Sauf que son nom et celui des mthodes ont chang, et nous avons une mthode supplmentaire qui nous permet que supprimer de lagrgat le dernier lment retourn par de la mthode next()[lancienne mthode suivant()].

Voil qui va tre du gteau! Il nous suffit de changer linterface quIterateurMenuCreperie et IterateurMenuCafeteria tendent tous deux, daccord? Enfin presque... en ralit, ce nest pas vraiment aussi simple. Non seulement java.util possde sa propre interface Iterator, mais ArrayList a galement une mthode iterator() qui retourne un itrateur. En dautres termes, nous navons jamais eu besoin dimplmenter notre propre itrateur pour ArrayList. En revanche, notre implmentation du MenuCafeteria est ncessaire parce quelle sappuie sur un tableau, qui ne prend pas en charge la mthode iterator() (ni aucun autre moyen de crer un itrateur sur un tableau).

Q: R:

questions stupides
voulez pas autoriser remove() dans votre itrateur, vous devrez lancer lexception java. lang.UnsupportedOperationException. Dans lAPI, la documentation dIterator spcifie que cette exception peut tre lance depuis remove() et tout client qui se comporte en bon citoyen vrifiera cette exception en appelant la mthode remove().

Il ny a pas de

Et si je ne veux pas permettre de supprimer quelque chose de la collection dobjets?

Q: R:

Comment remove() se comportet-elle si plusieurs threads utilisent des itrateurs diffrents sur la mme collection dobjets?

La mthode remove() est considre comme optionnelle. Vous ntes pas oblig de proposer une fonctionnalit de suppression. Mais, de toute vidence, vous devez conserver la mthode parce quelle fait partie de linterface Iterator. Si vous ne

Le comportement de remove() nest pas spcifi si la collection est modifie pendant que vous oprez une itration. Vous devez donc concevoir soigneusement votre code multithread quand il existe des accs concurrents une collection.

332

Chapitre 9

les patterns Itrateur et Composite

Une conception plus propre avec java.util.Iterator


Commenons par MenuCreperie. Le passage java.util.Iterator va tre facile. Nous supprimons simplement la classe IterateurMenuCreperie, nous ajoutons une instruction import java.util.Iterator au dbut de MenuCreperie et nous y changeons une ligne de code:
public Iterator creerIterateur() { return plats.iterator(); }

Au lieu de crer notre propre itrateur, nous appelons simplement la mthode iterator() sur lArrayList de plats.

Et voil! Cest tout pour MenuCreperie. Nous devons maintenant apporter des modifications pour que MenuCafeteria fonctionne avec java.util.Iterator.

import java.util.Iterator; public class IterateurMenuCafeteria implements Iterator { Plat[] liste; int position = 0; public IterateurMenuCafeteria(Plat[] liste) { this.liste = liste; } public Object next() { //limplmentation ici } public boolean hasNext() { //limplmentation ici }

Nous importons dabord java.util. Iterator, linterface que nous allons implmenter.

Aucun changement dans notre s implmentation actuelle, sauf les nom de mthode...
...mais nous sommes obligs dimplmenter remove(). Ici, comme le chef utilise un tableau de taille fixe, nous incrmentons simplement tous les lments de un quand remove() est appele.

public void remove() { if (position <= 0) { throw new IllegalStateException (Vous ne pouvez pas supprimer dlment si vous navez pas excut au moins un next()); } if (liste[position-1] != null) { for (int i = position-1; i < (liste.length-1); i++) { liste[i] = liste[i+1] ; } liste[liste.length-1] = null; } } }

vous tes ici

333

dcoupler la serveuse des menus

Nous y sommes presque...


Il suffit de donner aux Menus une interface commune et de retravailler un peu le code de la Serveuse. Linterface Menu est trs simple: nous finirons peut-tre par ajouter quelques mthodes, ajouterPlat() par exemple, mais, pour linstant, nous allons laisser les chefs contrler leurs menus en maintenant cette mthode hors de linterface publique:
public interface Menu { public Iterator creerIterateur(); }

Cest une interface simple qui permet aux clients dobtenir un itrateur sur les lments du menu..

Nous devons maintenant ajouter implements Menu aux dfinitions des classes MenuCreperie et MenuCafeteria et mettre jour le code de Serveuse:

import java.util.Iterator; public class Serveuse { Menu menuCreperie; Menu menuCafeteria;

Maintenant, la Serveuse utilise aussi java.util.Iterator. Nous devons remplacer les classes Menu concrtes par linterface Menu.

public Serveuse(Menu menuCreperie, Menu menuCafeteria) { this.menuCreperie = menuCreperie; this.menuCafeteria = menuCafeteria; } public void afficherMenu() { Iterator iterateurCrepe = menuCreperie.creerIterateur() ; Iterator iterateurCafet = menuCafeteria.creerIterateur() ; System.out.println(MENU\n----\nBRUNCH) ; afficherMenu(iterateurCrepe) ; System.out.println(\nDJEUNER) ; afficherMenu(iterateurCafet) ; } private void afficherMenu(Iterator iterateur) { while (iterateur.hasNext()) { Plat plat = (Plat)iterateur.next(); System.out.print(plat.getNom() + , ); System.out.print(plat.getPrix() + --); System.out.println(plat.getDescription()); } } // autres mthodes }

Cette portion de code ne change pas.

334

Chapitre 9

les patterns Itrateur et Composite

O cela nous amne-t-il?


Les classes MenuCreperie et MenuCafeteria implmentent une interface, Menu. La Serveuse peut se rfrer chaque objet Menu en utilisant linterface la place de la classe concrte. Nous rduisons ainsi la dpendance entre la Serveuse et les classes concrtes en programmant une interface, non une implmentation. La nouvelle interface Menu ne possde quune mthode, creerIterateur(), qui est implmente par MenuCreperie et MenuCafeteria. Chaque classe de menu assume la responsabilit de crer un itrateur concret appropri son implmentation interne des lments des menus.

e de Ceci rsout le problm nus me x au e la Serveuse li concrets.


Et cela rsout celui de la Serveuse lie limplmentation des Plats.
dcoupl la Comme nous avonsmentation des Serveuse de limpl s utiliser un menus, nous pouvon ourir nimporte Iterator pour parc ents de menu sans quelle liste dlm la faon dont nous proccuper de ise en uvre. cette liste est m
<<interface>> Iterator
hasNext() next() remove()

Voici notre nouvelle interface Menu. Elle spcifie la nouvelle mthode, creerIterateur().

Maintenant, la Serveuse na plus besoin de se soucier que de Menu et dIterator


Serveuse
afficherMenu()

<<interface>> Menu
creerIterateur() createIterator()

MenuCreperie
Plats creerIterateur()
Plats

MenuCafeteria

IterateurMenuCreperie
hasNext() next() remove()

IterateurMenuCafeteria
hasNext() next() remove()

creerIterateur()

MenuCreperie et MenuCafetaria implme maintenant linterface Menu: elles doiv ntent dfinir la nouvelle mthode creerItera ent donc teur().

Nous utilisons maintenant litrateur dArrayList fourni par java.util. Nous navons plus besoin de cette classe.

La mthode creerIterateur() de MenuCafeteria retourne un IterateurMenuCafeteria parce que cest le type ditrateur ncessaire pour parcourir son tableau de plats.

Chaque Menu concret est responsable de la cration de la classe itrateur concrte qui convient.
vous tes ici

335

dfinition du pattern Itrateur

Le pattern Itrateur: dfinition


Vous avez dj vu comment on implmente le pattern Itrateur avec notre itrateur maison. Vous avez galement vu comment Java prend en charge les itrateurs dans lune de ses classes collections (ArrayList). Il est maintenant temps dtudier la dfinition officielle du pattern:
Le pattern Itrateur fournit un moyen daccder en squence un objet de type agrgat sans rvler sa reprsentation sousjacente.

Le pattern Itrateur permet de parcourir les lments dun agrgat sans exposer limplmentation sous-jacente. Il attribue galement cette tche de navigation lobjet itrateur, non lagrgat, ce qui simplifie linterface et limplmentation de lagrgat et place la responsabilit au bon endroit.

Voil qui est trs logique: le pattern vous fournit un moyen de parcourir les lments dun agrgat sans tre oblig de savoir comment ils sont rllement implments. Vous vous en tes rendu compte avec les deux implmentations des Menus. Mais leffet de lemploi ditrateurs dans votre conception est tout aussi important. Une fois que vous disposez dune faon uniforme daccder aux lments de tous vos agrgats, vous pouvez crire du code polymorphe qui fonctionne avec nimporte lequel de ces agrgats tout comme la mthode afficherMenu(), qui se moque de savoir si les plats sont stocks dans un tableau ou une ArrayList (ou toute structure pouvant crer un itrateur) du moment quelle peut obtenir un itrateur. Le pattern Itrateur a un autre impact important sur votre conception: il prend la responsabilit de parcourir les lments et la donne lobjet itrateur, non lagrgat. Non seulement limplmentation et linterface de lagrgat en sont simplifies, mais comme il nest plus responsable de litration, lagrgat peut se concentrer sur sa vritable vocation (grer des collections dobjets) et non sur litration. tudions le diagramme de classes pour replacer tous les lments dans un contexte...

336

Chapitre 9

les patterns Itrateur et Composite

Disposer dune interface commune pour les agrgats est pratique pour votre client: elle dcouple la collection dobjets de son implmentation.
<<interface>> Agrgat
creerIterateur()

Client

<<interface>> Iterateur
hasNext() next() remove()

Cette interface est celle que tous les itrateurs doivent implmenter, et elle fournit un ensemble de mthodes qui permettent de parcourir les lments dune collection. Ici, il sagit de linterface java.util.Iterator. Si vous ne voulez pas lutiliser, vous pouvez toujours crer la vtre.

AgregatConcret
creerIterateur()

IterateurConcret
hasNext() next()

LAgregatConcret a une collection dobjets et implmente la mthode qui retourne un itrateur pour cette collection.

Chaque AgregatConcret est responsable de linstanciation dun IterateurConcret qui peut parcourir sa collection dobjets.

remove()

LIterateurConcret est responsable de la gestion de la position courante de litration.

Musclez vos neurones

Le diagramme de classes du pattern Itrateur a un aspect trs similaire celui dun autre pattern que vous avez tudi. Pouvez-vous dire lequel? Indice: Une sous-classe dcide de lobjet crer.

vous tes ici

337

questions et rponses sur Itrateur

questions stupides
pas de contrle sur litration. Toutefois, certains soutiennent quils sont plus faciles utiliser parce quil suffit de leur passer une opration et de leur dire ditrer pour quils fassent tout le travail votre place. Quand nous crivons des mthodes qui acceptent un itrateur en paramtre, il sagit dune itration polymorphe. Autrement dit, nous crons un code qui peut parcourir nimporte quelle collection du moment quelle supporte Iterator. Ce code fonctionnera toujours, quelle que soit la faon dont la collection est implmente.

Il ny a pas de

Q: R:

Jai vu dans dautres livres que le diagramme de classes dItrateur contenait les mthodes premier(), suivant(), termin() et elementCourant(). Pourquoi ces mthodes sont-elles diffrentes?

R:

Q: R:

Ce sont les noms des mthodes classiques qui ont t employs. Celles-ci ont chang au fil du temps, et nous utilisons maintenant celles de java. util.Iterator: next(), hasNext() et mme remove() qui a t ajoute. Voyons ces mthodes classiques. suivant() et elementCourant() ont t fusionnes en une seule dans java.util, termin() est de toute vidence devenue hasNext(), mais nous navons aucune mthode correspondant premier(). La raison en est que, en Java, nous avons simplement tendance crer un nouvel itrateur chaque fois que nous devons commencer un parcours. Nanmoins, vous pouvez constater que ces interfaces prsentent trs peu de diffrences. En fait, il existe toute une gamme de comportements que vous pouvez donner vos itrateurs. La mthode remove() est un exemple dextension dans java.util.Iterator.

Peut-on implmenter un itrateur qui parcourt aussi une collection en arrire?

Q: R:

Absolument. Dans ce cas, vous voudrez probablement ajouter deux mthodes, lune pour accder llment prcdent et lautre pour vous indiquer que vous avez atteint le dbut de la collection dlments. Le framework Collections de Java fournit un autre type dinterface nomm ListIterator. Celle-ci ajoute previous() et quelques autres mthodes linterface Iterator standard. Elle est supporte par toute collection qui implmente linterface List.

Si je dveloppe en Java, estce que je nutiliserai pas toujours linterface java.util.Iterator pour que mes propres implmentations ditrateurs fonctionnent avec des classes qui utilisent dj les itrateurs Java?

Q: R:

Qui dfinit lordre de litration dans des collections comme Hashtable, qui sont par nature non ordonnes?

Probablement. Si vous avez une interface Iterator commune, vous aurez certainement plus de facilit combiner vos propres agrgats avec ceux de Java comme ArrayList et Vector. Mais noubliez pas que si vous avez besoin dajouter des fonctionnalits ncessaires vos agrgats, vous pouvez toujours tendre linterface Iterator.

Q: R:

Q: R:

Jai entendu parler ditrateurs externes et internes. Questce que cest? Lequel avons-nous implment dans cet exemple?

Nous avons implment un itrateur externe, ce qui veut dire que le client contrle litration en appelant next() pour accder llment suivant. Un itrateur interne est contrl par litrateur lui-mme. Dans ce cas, comme cest litrateur qui parcourt les lments, vous devez lui dire quoi en faire mesure quil les parcourt. Cela implique que vous devez avoir un moyen de lui transmettre une opration. Les itrateurs internes sont moins souples que les externes parce que le client na

Les itrateurs nimpliquent aucun ordre. Les collections sous-jacentes peuvent tre non ordonnes, comme le sont les tables de hachage ou les bags; elles peuvent mme contenir des doublons. Lordre est donc li la fois aux proprits de la collection et limplmentation. En gnral, vous ne devez pas prsupposer dordre particulier, moins dindication contraire dans la documentation de la classe collection.

Jai vu que Java avait une interface Enumeration. Est-ce quelle implmente le pattern Itrateur?

Q:

Vous avez dit quon pouvait crire du code polymorphe qui utilise un itrateur. Pouvez-vous expliquer?

Nous en avons parl au chapitre consacr Adaptateur. Souvenez-vous: java.util. Enumeration est une ancienne implmentation dItrateur qui a depuis t remplace par java.util.Iterator. Enumeration possde deux mthodes: hasMoreElements()qui correspond hasNext() et nextElement()qui correspond next(). Mais vous prfrerez probablement Iterator Enumeration parce quil y a plus de classes Java qui le prennent en charge. Si vous devez convertir lun en lautre, revoyez le chapitre sur Adaptateur, dans lequel nous avons implment un adaptateur entre Enumeration et Iterator.

338

Chapitre 9

les patterns Itrateur et Composite

Une seule responsabilit


Et si nous permettions nos agrgats dimplmenter leurs collections internes et les oprations associes ET les mthodes ditration? Nous savons dj que cela augmenterait le nombre de mthodes de lagrgat. Et alors? O serait le mal?
Eh bien, pour le savoir, vous devez dabord reconnatre que si lon permet une classe non seulement de soccuper de ses propres affaires (grer un type dagrgat quelconque) mais aussi dassumer une autre responsabilit (par exemple une itration), nous donnons la classe deux raisons de changer. Deux? Oui, deux. Elle peut changer si la collection change dune manire ou dune autre, et elle peut changer si le mode ditration change. Encore une fois, notre ami le CHANGEMENT est au centre dun autre principe de conception: Principe de conception
Une classe ne doit avoir quune seule raison de changer.

Dans une classe, toute responsabilit est un point de changement potentiel. Plus dune responsabilit = plus dun point de changement. Ce principe nous enseigne limiter chaque classe une seule responsabilit.

Cohsion est un terme


souvnt employ pour exprimer le degr auquel une classe ou un module ire prem Tte la sert une seule finalit ou Superglu OO sacquitte dune seule responsabilit. On dit quun composant, classe ou module, est fortement cohsif sil est conu autour dun ensemble de fonctions apparentes et quil est faiblement cohsif si les fonctions nont pas de lien entre elles. La cohsion est un principe plus gnral que celui de responsabilit unique, mais les deux sont troitement lis. Les classes qui respectent ce principe ont tendance tre plus facile maintenir que celles qui assument plusieurs responsabilits et sont moins cohsives.

Nous savons que nous devons viter comme la peste de changer quelque chose une classe: une modification du code peut fournir aux problmes toutes sortes occasions de simmiscer. Deux raisons augmentent les probabilits que la classe change lavenir, et, en ce cas, cela affectera deux aspects de votre conception. La solution? Ce principe nous enjoint daffecter une responsabilit une classe, et seulement une. Cest aussi simple que cela, tout en ne ltant pas: sparer les responsabilits est lune des tches les plus difficiles de la conception. Notre cerveau excelle percevoir un ensemble de comportements et les grouper, mme sil y a en ralit deux responsabilits ou plus. La seule faon de russir est dtre attentif lorsquon examine ses conceptions et guetter les signaux qui indiquent quune classe risque de changer de plusieurs faons quand le systme sera tendu.

vous tes ici

339

responsabilits multiples

A
feu()

Musclez vos neurones

Examinez ces classes et dterminez celles qui ont plusieurs responsabilits.


DistributeurDeBonbons
getNombre()

Personne
Jeu
connecter() quitter() bouger() pause()

setNom() setAdresse() setNumeroTlephone() enregistrer() charger()

Telephone
numeroter() raccrocher() parler() emettreDonnees() clignoter()

getEtat() getPosition()

Iterateur PaquetDeCartes
hasNext() next() remove() ajouterCarte() supprimerCarte() battre()

PanierDAchat
ajouter() supprimer() payer() ajouterASelection()

hasNext() next() remove()

Attention, chutes de certitudes! Port du casque obligatoire.

A
quitter() feu() pause()

2 Musclez vos neurones


Dterminez si ces classes sont fortement ou faiblement cohsives.
Jeu ActionsJoueur
SessionJeu
connecter() quitter() bouger() feu() pause()

connecter() bouger()

Joueur
getScores() getNom()

getScores() getNom()

340

Chapitre 9

les patterns Itrateur et Composite

Cela tombe bien que vous tudiiez le pattern Itrateur, parce que je viens dapprendre que Fusions & Acquisitions vient de conclure une nouvelle affaire... Nous fusionnons avec la Brasserie dObjectville et nous adoptons leur menu du soir.

Eh bien! Nous qui pensions que ctait dj assez compliqu comme a! Quallons-nous faire maintenant?

Allons, allons! Sois donc un peu plus positif, Je suis sr que nous trouverons une solution avec le pattern Itrateur.

vous tes ici

341

un nouveau menu

Jetons un coup dil au menu de la Brasserie


Voici le menu de la brasserie. On dirait que nous naurons pas trop de problmes pour lintgrer notre structure... Voyons un peu.

tre nouvelle implmente pas no corriger. n ie er ss ra uB en M mais cest facile s une Hashtable. interface Menu, La Brasserie mmorise ses plats dan public class MenuBrasserie { { s allons bientt le savoir... Hashtable plats = new Hashtable(); Prend-elle en charge Itrateur? Nou s Comme dans les autres Menus, les plat public MenuBrasserie() { . eur uct str con sont initialiss dans le ajouterPlat(Omelette sarladaise,
Omelette aux champignons et pommes sautes, true, 3.99); ajouterPlat(Soupe de poissons, Soupe de poissons, rouille et crotons, false, 3.69); ajouterPlat(Tagliatelles Primavera, Ptes fraches, brocoli, petits pois, crme frache, true, 4.29); } public void ajouterPlat(String nom, String description, Voil o nous crons un Plat et o boolean vegetarien, double prix) lajoutons la table de hachage. { Plat plat = new Plat(nom, description, vegetarien, prix); plats.put(plat.getNom(), plat); } public Hashtable getPlats() { return plats; }

nous

la cl est le

nom du pla

t.

La valeur est lobjet Plat. instructions

Nous nallons plus avoir besoin de ces

vos crayons Sharpen your pencil


Avant de regarder la page suivante, notez rapidement les trois modifications ncessaires pour adapter ce code la structure de notre application:

1. 2. 3.
342
Chapitre 9

les patterns Itrateur et Composite

Retravailler le code du menu de la Brasserie


Intgrer MenuBrasserie notre application nest pas compliqu. Pourquoi? Parce que Hashtable est lune des collections Java qui prennent en charge Itrateur. Mais pas exactement comme ArrayList

public class MenuBrasserie implements Menu { Hashtable plats = new Hashtable(); public MenuBrasserie() { // code du constructeur }

MenuBrasserie implmentant linterface Menu, la Serveuse peut lutiliser exactement comme les deux autres menus. Nous utilisons une Hashtable parce que cest une structure de donnes courante pour stocker des valeurs; nous pourrions galement employer une HashMap, plus rcente.

public void ajouterPlat(String nom, String description, boolean vegetarien, double prix) { Plat plat = new Plat(nom, description, vegetarien, prix); plats.put(plat.getNom(), plat); } public Hashtable getPlats() { return plats; } public Iterator creerIterateur() { return plats.values().iterator(); } }

s nous dbarrasser de getPlats() Tout comme auparavant, nous pouvon des plats la Serveuse. pour ne pas exposer limplmentation

Et voici o nous implmentons la mthode creerIterateur(). Remarquez que nous nobtenons pas un itrateur pour toute la table, mais uniquement pour les valeurs.

Code la loupe

Une Hashtable est un peu plus complexe quune ArrayList parce quelle associe des cls et des valeurs, mais nous pouvons toujours avoir un itrateur pour les valeurs (qui sont les Plats).

public Iterator creerIterateur() { return plats.values().iterator(); }

Dabord, nous accdons toutes les valeurs de la Hashtable, une simple collection de tous les objets de la table.

Heureusement, cette collection sup la mthode iterator() qui retourneporte objet de type java.util.Iterator. un
343

vous tes ici

tester le nouveau menu

Ajouter le menu de la Brasserie au code de la Ser veuse


Voil qui tait facile. Et si on modifiait la Serveuse pour quelle prenne en charge notre nouveau Menu? Maintenant que celle-ci attend des itrateurs, cela devrait tre tout aussi facile.

public Menu Menu Menu

class Serveuse { menuCreperie; menuCafeteria; menuBrasserie;

Le menu de la Brasserie est transmis la Serveuse dans les autres menus, et nous le plaons dans une variable dinstance.

public Serveuse(Menu menuCreperie, Menu menuCafeteria, Menu menuBrasserie) { this.menuCreperie = menuCreperie; this.menuCafeteria = menuCafeteria; this.menuBrasserie = menuBrasserie; } public void afficherMenu() { Iterator iterateurCrepe = menuCreperie.creerIterateur(); Iterator iterateurCafet = menuCafeteria.creerIterateur(); Iterator iterateurBrass = menuBrasserie.creerIterateur(); System.out.println(MENU\n----\nBRUNCH); afficherMenu(iterateurCrepe); System.out.println(\nDJEUNER); afficherMenu(iterateurCafet); System.out.println(\nDNER); afficherMenu(iterateurBrass); } private void afficherMenu(Iterator iterateur) { while (iterateur.hasNext()) { Plat plat = (Plat)iterateur.next(); System.out.print(plat.getNom() + , ); System.out.print(plat.getPrix() + --); System.out.println(plat.getDescription()); } } }

Nous utilisons le menu de la brasserie pour le dner. Pour lafficher, il suffit de crer litrateur et de le transmettre afficherMenu(). Et voil! Cette portion de code ne change pas.

344

Chapitre 9

les patterns Itrateur et Composite

Brunch, djeuner ET dner


Mettons jour notre test pour vrifier que tout ceci fonctionne.

public class TestMenu { public static void main(String args[]) { MenuCreperie menuCreperie = new MenuCreperie(); MenuCafeteria menuCafeteria = new MenuCafeteria(); MenuBrasserie menuBrasserie = new MenuBrasserie();

Crer un MenuBrasserie... ... et le transmettre la serveuse.

Serveuse serveuse = new Serveuse(menuCreperie, menuCafeteria, menuBrasserie); serveuse.afficherMenu(); }

Maintenant, laffichage doit montrer les trois

menus.

Excutons le test: voici le nouveau menu du dner!


Fichier dition Fentre Aide OmeletteAuFromage

% java TestMenu MENU ---BRUNCH Crpe luf, 2.99 -- Crpe avec uf au plat ou brouill Crpe complte, 2.99 -- Crpe avec uf au plat et jambon Crpe forestire, 3.49 -- Myrtilles fraches et sirop de myrtille Crpe du chef, 3.59 -- Crme frache et fruits rouges au choix

Dabord nous parcourons le menu de crpes. Puis le menu du djeuner.

DEJEUNER Salade printanire, 2.99 -- Salade verte, tomates, concombre, olives, pommes de terre Salade parisienne, 2.99 -- Salade verte, tomates, poulet, emmental Soupe du jour, 3.29 -- Soupe du jour et crotons grills Quiche aux fruits de mer, 3.05 -- Pte brise, crevettes, moules, champignons Quiche aux pinards, 3.99 -- Pte feuillete, pommes de terre, pinards, crme frache

DINER Soupe de poissons, 3.69 -- Soupe de poissons, rouille et crotons Et enfin le Tagliatelles Primavera, 4.29 -- Ptes fraches, brocoli, petits pois, crme frache nouveau menu Omelette sarladaise, 3.99 -- Omelette aux champignons et pommes sautes du dner, tout %

cela avec le mme code.


345

vous tes ici

quavons-nous fait?

Quavons-nous fait?
Nous voulions permettre la Serveuse de parcourir facilement les lments des menus...
Plat

ArrayList

Plat

Pla

Plat

... et nous ne voulions pas quelle sache comment ces lments taient implments.

s Pour litration, leenus m s lments de no tations ont deux implmen es et deux interfac diffrentes.

Tableau
1
Pl at

2
Plat

3
Plat

4
Plat

Nous avons dcoupl la Serveuse...


Nous avons donc donn la Serveuse un itrateur pour chaque type de groupe dobjets ... un pour quelle doit parcourir... lArrayList...
next()
Iterateu
r

ArrayList a un .. itrateur intgr. ArrayList

Plat

Plat

Plat

Plat

next()

... et un pour le tableau

... les tableaux nayant pas ditrateur intgr, nous construisons le ntre.

Tableau
1
Plat

2
Plat

Iterateu

3
Plat

Maintenant, elle na plus besoin de se soucier de limplmentation que nous avons choisie: elle utilise toujours la mme interface - Iterator pour parcourir les menus. Elle a t dcouple de limplmentation.
346
Chapitre 9

4
Plat

les patterns Itrateur et Composite

... et nous avons rendu la Serveuse plus extensible


En lui fournissant un itrateur, nous lavons dcouple de limplmentation des plats: nous pouvons donc ajouter de nouveaux Menus notre guise.
next()

Nous avons ajout sans problme une autre implmentation des plats. Comme nous avons fourni un itrateur, la Hashtable Serveuse a su quoi faire.
c l

Plat

c l

Plat

Ce qui vaut mieux quelle peut maint pour elle, parce le mme code pourenant utiliser nimporte quel grou parcourir ce qui vaut mieux pe dobjets. Et que les dtails de pour nous, parce ne sont pas exposlimplmentation s.

Iterato

c l

Plat

c l

Plat

Crer un itrateur pour les valeurs de la table de hachage a t facile: quand vous appelez values. iterator(), vous obtenez un itrateur.

Mais ce nest pas tout!


Java offre de nombreuses classes collection qui permettent de stocker des groupes dobjets et dy accder. Par exemple, Vector et LinkedList.

LinkedList Vector
Me nuItem
Me nuItem
Me nuItem
Men

La plupart ont des interfaces diffrentes.


Mais presque toutes disposent dun moyen dobtenir un itrateur.
Me nuItem

uItem

Me nuItem

Me nuItem

Men

uItem

e t dau t r

e s!

Et dans le cas contraire, tout va bien: vous savez maintenant comment en crire un.
vous tes ici

347

itrateurs et collections

Itrateurs et collections
Nous avons utilis deux classes qui font partie du framework Collections de Java. Ce framework nest autre quun ensemble de classes et dinterfaces, qui comprend notamment ArrayList, que nous avons employe, et de nombreuses autres telles que Vector, LinkedList, Stack et PriorityQueue. Chacune delles implmente linterface java.util. Collection, qui contient plusieurs mthodes utiles pour manipuler des groupes dobjets. Jetons un rapide coup dil cette interface:
ES IR TA ILI UT JAVA

<<interface>> Collection
add() addAll() clear() contains() containsAll() equals() hashCode() isEmpty() iterator() remove() removeAll() retainAll() size() toArray()

nstater, cette Comme vous pouvez le co que des bonnes interface ne contient ter des lments ou aj choses. Vous pouvez primer sans mme votre collection ou en sup implmente. savoir comment elle est

.
Regardez !
Hashtable est lune des classes qui supportent indirectement Itrateur. Comme vous lavez vu quand nous avons implment le MenuBrasserie, vous pouvez en obtenir un itrateur, mais seulement en extrayant dabord les valeurs. la rflexion, cest logique: la table contient deux ensembles dobjets des cls et des valeurs. Si lon veut accder aux valeurs, il faut dabord les extraire de la table, puis obtenir litrateur.

Voici notre vieille amie, la mthode iterator(). Grce elle, vous pouvez obtenir un itrateur pour nimporte quelle classe qui implmente linterface Collection.

Et voil deux autres mthodes pratiques: size(), qui permet debien connatre le nombre dlments, toArray(), qui transforme vot et re collection en tableau.
Ce qui est gnial avec le framework Collections et Itrateur, cest que chaque objet Collection sait comment crer son propre itrateur. Lappel diterator() sur une ArrayList retourne un itrateur concret conue pour une ArrayList, mais on na jamais besoin de se proccuper de la classe concrte quil utilise: il suffit dimplmenter linterface Iterator.

348

Chapitre 9

les patterns Itrateur et Composite

Itrateurs et collections en Java 5

Essayez ceci. Java5 permet ditrer sur des collections sans mme avoir besoin de demander un tableau.

Java5 dispose dune nouvelle forme dinstruction for, nomme for/in, qui permet de parcourir une collection ou un tableau sans crer explicitement ditrateur. Pour utiliser for/in, on crit une instruction for qui ressemble celle-ci:

Itre sur chaque objet de la collection.

obj est affect llment suivant de la collection chaque passage dans la boucle.

for (Object obj: collection) { ... } Voici comment parcourir un tableau avec for/in :

Remplir une ArrayList de Plats.

ArrayList items = new ArrayList(); items.add(new Plat(Crpes, dlicieuses crpes, true, 1.59); items.add(new Plat(Gaufres, succulentes gaufres, true, 1.99); items.add(new Plat(Toasts, excellents toasts, true, 0.59); for (Plat item: items) { System.out.println(Pour le brunch: + item); }

.
Regardez !
Vous devez utiliser les nouvelles caractristiques de gnricit de Java5 pour garantir un typage fiable avec for/ in. Lisez en dtail la documentation avant dutiliser la gnricit et for/in.

Parcourir la liste et afficher chaque lment.

vous tes ici

349

le frigo

le frigo
Les Chefs ont dcid quils voulaient pouvoir alterner les menus du djeuner.Autrement dit, ils proposeront certains plats le lundi, le mercredi, le vendredi et le dimanche et dautres le mardi, le jeudi et le samedi. Quelquun a dj crit le code dun nouvel IterateurMenuCafeteria alternateur qui permet dalterner les menus, mais il a mlang tous les morceaux et les a colls sur le frigo de la Cafeteria en guise de plaisanterie. Pouvez-vous le reconstituer? Certaines accolades sont tombes par terre et elles taient trop petites pour quon les ramasse. Nhsitez pas en ajouter autant que ncessaire.

Plat plat = elements[position]; position = position + 2; return plat;


import java.util.Iterator; import java.util.Calendar;

public Object next() {

r(Plat[] plats) public IterateurMenuCafeteriaAlternateu


this.elements = elements; Calendar maintenant = Calendar.get Instance(); position = maintenant.get(Calendar. DAY_OF_WEEK) % 2;

implemen ts Itera tor


Plat[] plats; int position;

public void remove() {

public class IterateurMenuCafeteriaAlternateur

public boolean hasNext() {

throw new UnsupportedOperationException( IterateurMenuCafeteriaAlternateur ne supporte pas remove ());


null) { if (position >= plats.length || plats[position] == return false; } else { return true; }

350

Chapitre 9

}
}

les patterns Itrateur et Composite

La Ser veuse est-elle prte pour le coup de feu?


La Serveuse a fait un bon bout de chemin, mais il faut bien admettre que ces trois appels dafficherMenu() font plutt mauvais effet. Soyons pratiques: chaque fois que nous ajouterons un nouveau menu, nous devrons modifier limplmentation de la Serveuse pour y insrer du code. Ne serait-ce pas une violation du principe Ouvert-Ferm

Trois appels de creerIterateur().


public void afficherMenu() Iterator iterateurCrepe Iterator iterateurCafet Iterator iterateurBrass { = menuCreperie.creerIterateur(); = menuCafeteria.creerIterateur(); = menuBrasserie.creerIterateur();

System.out.println(MENU\n----\nBRUNCH); afficherMenu(iterateurCrepe); System.out.println(\nDEJEUNER); afficherMenu(iterateurCafet); System.out.println(\nDINER); afficherMenu(iterateurBrass); }

Trois appels de afficherMenu().

Chaque fois que nous ajouterons ou que nous supprimerons un menu, nous devrons reprendre ce code et le modifier.
Ce nest pas la faute de la Serveuse. Nous avons fait du bon travail en dcouplant limplmentation des menus et en extrayant litration dans un itrateur. Mais nous grons toujours les menus avec des objets distincts et indpendants il nous faut un moyen de les manipuler ensemble.

Musclez vos neurones

La Serveuse a toujours besoin dappeler trois fois afficherMenu(), une fois pour chaque menu. Voyezvous une faon de combiner les menus pour quil ne faille plus quun seul appel? Ou peut-tre pour ne transmettre la Serveuse quun seul itrateur pour accder tous les menus?

vous tes ici

351

une nouvelle conception?

Pas si mal! Il suffit de grouper les menus dans une ArrayList puis dobtenir son itrateur pour accder chaque Menu. Le code de la Serveuse sera plus simple et il pourra grer un nombre quelconque de menus.

On dirait que le chef a une ide. Essayons-la:


public class Serveuse { ArrayList menus; public Serveuse(ArrayList menus) { this.menus = menus; }

Maintenant nous avons simplement une ArrayList de menus.


Et nous itrons sur les menus, en transmettant litrateur de chacun deux la mthode afficherMenu() surcharg.

public void afficherMenu() { Iterator iterateurMenus = menus.iterator(); while(iterateurMenus.hasNext()) { Menu menu = (Menu)iterateurMenus.next(); afficherMenu(menu.creerIterateur()); } } void afficherMenu(Iterator iterateur) { while (iterateur.hasNext()) { Plat plat = (Plat)iterateur.next(); System.out.print(plat.getNom() + , ); System.out.print(plat.getPrix() + -- ); System.out.println(plat.getDescription()); } } }

Cette partie du code ne change pas.

Lide semble bonne. Bien sr, nous avons perdu les noms des menus, mais rien ne nous empche den ajouter.

352

Chapitre 9

les patterns Itrateur et Composite

Juste au moment o nous pensions avoir termin...


Maintenant, ils veulent ajouter un sous-menu pour les desserts.
Et puis quoi encore? Maintenant, non seulement il va falloir prendre en charge plusieurs menus, mais aussi des menus dans des menus. Tout irait bien si nous pouvions nous contenter de faire de la carte des desserts un lment de la collection MenuCafeteria, mais cela ne marchera pas, vu la faon dont elle est implmente actuellement.
Je viens dapprendre que la Cafeteria allait crer une carte de desserts qui serait un encart dans leur menu standard.

Ce que nous voulons (en gros):

Tous les menus


en
uC

reperie

Di ner nu Me

en

uBrasseri

Voici notre Arraylist qui contient les menus de chaque restaurant.

Menu Creperie

Menu Brasserie
Menu Cafeteria
k ey

Me nuItem

Me nuItem

Me nuItem

Men

uItem

1
Plat

Tableau
Plat

Me nuItem

k ey

Me nuItem

k ey

Me nuItem

Hashtable

ArrayList

Dessert Menu
1
Me nuItem

3
Plat

k ey

Men

uItem

4
Plat

2
Me nuItem

3
Me nuItem

4
Men

uItem

Il faut que MenuCafeteria contienne un sousmenu, mais on ne peut pas affecter un menu un tableau de plats: cela ne peut pas fonctionner, parce quil sagit de deux types diffrents.

t es is c ible! a M oss imp

On ne peut pas affecter un menu de desserts un tableau de plats. Il faut modifier quelque chose!
vous tes ici

353

il est temps de remanier le code

Que nous faut-il?


Il est temps de prendre une dcision dterminante et de retravailler limplmentation des chefs afin dobtenir une conception suffisamment gnrale pour quelle fonctionne avec tous les menus (et maintenant les sous-menus). Oui, nous allons dire aux chefs que le temps est venu pour nous de rimplmenter leurs menus. Une fois un tel degr de complexit atteint, il nous faut retravailler la conception. Sinon, nous ne parviendrons jamais grer dautres types de menus ni des sous-menus. Alors? Que ncessite donc rellement notre nouvelle conception?

Il nous faut une forme ou une autre de structure


arborescente qui puisse prendre en compte des menus, des sous-menus et des lments de menus (des plats).

Nous devons tre srs de disposer dun moyen de


parcourir les lments de chaque menu qui soit aussi pratique que nos itrateurs actuels.

Nous devons pouvoir naviguer dans les menus


avec plus de souplesse. Par exemple, nous devrions pouvoir ne parcourir que la carte des desserts, ou bien itrer sur tout le menu de la Cafeteria, y compris la carte des desserts.

Il arrive un moment o nous devons remanier notre code pour quil puisse voluer. Faute de quoi, il deviendra rigide et inflexible, sans aucun espoir de donner naissance une nouvelle vie.

354

Chapitre 9

les patterns Itrateur et Composite

prsenter Comme nous devons reimbriqus des menus, des menus nus, et des lments de me le une une arborescence semb naturelle. structure tout fait
nu C

To

n us les Me

rperi

des plats

u B sse ra

ts

P l at

P l at

P l at

P l at

P l at

P l at

en

u D s se r e

Plat

rie
Plat

Nous devons grer des Menus

ia

en u Cafeter

us

Nous avons toujours us besoin de parcourir to . les lments de larbre


To
n us les Me
us

ia

Plat

P l at

P l at

P l at

P l at

Plat

en u

s Des

ert

P l at

P l at

P l at

P l at

P l at

P l at

P l at

P l at

P l at

ts

rperi

u B sse ra

rie

Comment traiteriez-vous cette nouvelle difficult introduite par la modification des spcifications? Rflchissez-y avant de tourner la page.

e M

en

P l at

P l at

P l at

P l at

P l at

et des sous-menus

Et naviguer de souple, par exemfaon plus ple dans un seul menu.


M
en

nu C

en u Cafeter

en

Musclez vos neurones

e M

u D s se r e

P l at

P l at

vous tes ici

355

dfinition du pattern Composite

Le pattern Composite: dfinition


Oui, nous allons introduire un nouveau pattern pour rsoudre ce problme. Nous navons pas abandonn Itrateur il fait toujours partie de notre solution mais le problme de la gestion des menus a atteint une nouvelle dimension quItrateur ne peut pas prendre en compte. Nous allons donc prendre du recul et faire appel au pattern Composite. Nous irons droit au but avec ce pattern, et nous allons immdiatement vous donner la dfinition officielle:
Le pattern Composite compose des objets en des structures arborescentes pour reprsenter des hirarchies composant/compos. Il permet aux clients de traiter de la mme faon les objets individuels et les combinaisons de ceux-ci.
Feuille

Voici une arborescen

Les lments qui ont des enfants sont des nuds.


no e u d

ce.

Feuille
Feuille

Les lment qui nont pas denfants sont des feuilles.

Rflchissons dans les termes de nos menus: ce pattern nous fournit un moyen de crer une arborescence capable de grer un groupe de menus imbriqus et des lments de menu dans la mme structure. En plaant les menus et les plats dans la mme structure, nous crons une hirarchie composant/compos, autrement dit une arborescence dobjets constitue de parties (menus et plats) mais qui peut tre traite comme un tout, linstar dun super-menu. Une fois que nous avons notre super-menu, nous pouvons appliquer ce pattern pour traiter de la mme faon des objets individuels et des combinaisons. Quest-ce dire? Cela signifie que si nous avons une arborescence de menus, de sous-menus, et ventuellement de sous-sous-menus ainsi que de plats, alors tout menu est une composition parce quil peut contenir la fois des menus et des plats. Les objets individuels sont les plats: ils ne contiennent pas dautres objets. Comme vous allez le voir, une conception conforme au pattern composite va nous permettre dcrire un code simple qui peut appliquer la mme opration (afficher par exemple!) toute la structure de menus.

Nous pouvons reprsenter un Menu et des Plats sous forme arborescente.

Menu

Plat

Pla t

Plat

s et Les menus sont des nud . les uil fe s de les plats sont

356

Chapitre 9

les patterns Itrateur et Composite

Nous pouvons crer une lexe que arborescence aussi comp ncessaire.
To

n us les Me

Menus
u B sse ra

us

nu C

ia

M
rperi

en u Cafeter

rie

Sous-menu
M

e M
Plat

en

Plats
P l at

Plat

ts

P l at

Plat

Plat

P la t

Plat

en

u D s se r e

P l at

P la t

Plat

Plat

Plat

Et la traiter comme un
To

tout... Menus
u B sse ra
us

n us les Me

Le pattern Composite nous permet de crer des arborescences dobjets qui contiennent des nuds: ce sont aussi bien des compositions dobjets que des objets individuels. Avec une structure composite, Nous pouvons appliquer les mmes oprations aux composites et aux composants. En dautres termes, nous pouvons ignorer dans la plupart des cas les diffrences entre les compositions dobjets et les objets individuels.

nu C

ia

M
rperi

en u Cafeter

MenuItems

Plat

P lat

ts

P l at

Plat

Plat

Plat

P l at

Pl a t

rie
Plat

Sous-menu
en

ns Les opratioe r t peuvent tout. appliques au

n us les Me

us

nu C

ia

rperi

u B sse ra

MenuItems

ts

Plat

Plat

Plat

P la t

P l at

P la t

rie

e M

en

u D s se r e

Plat

P la t

Plat

Plats

Ou aux parties.
print()
To

Menus Sous-menu
en

en u Cafeter

e M

en

u D s se r e

P l at

Plat

P la t

P la t

Plat

Plat

P l at

print()

Ou aux p

arties.
vous tes ici

357

le diagramme de classes du pattern Composite

nterface Le Client utilise li nipuler les ma Composant pour ition. objets de la compos

nit une Le Composant dfi les interface pour tous ie de la objets faisant part osite et composition: le comp les nuds feuilles.

Le Composant peut impl un comportement par d menter ajouter(), supprimer(),ge faut pour tEnfant() et ses oprations.

Client

Component
operation() add(Component)

Notez que la Feuille hrit galement de mthodes e que ajouter(), supprimer telles getEnfant(), Ce qui n () et ncessairement beaucouppas sens pour un nud feuille.de reviendrons ultrieureme Nous nt sur ce problme. Une Feuille na pas denfant.

remove(Component) getChild(int)

Leaf
operation()

Composite
add(Component)

Une Feuille dfinit le comportement des lments de la composition. Pour ce faire, elle implmente les oprations que le Composite prend en charge.

Q: R:

questions stupides
Quand vous organisez les donnes de cette manire, vous obtenez au final une arborescence (en ralit une arborescence inverse) dans laquelle la racine est un composite et dont les branches contiennent des composites qui se terminent par des nuds feuilles. Souvenez-vous que nous adoptons une nouvelle approche. Nous allons rimplmenter les menus avec une nouvelle solution: le pattern Composite. Ne vous attendez donc pas ce que les itrateurs se transforment comme par magie en composites. Cela dit, les deux saccommodent trs bien lun de lautre. Vous verrez bientt que nous pouvons utiliser des itrateurs dans une implmentation de composite.

Il ny a pas de

te Le Composi aussi les e t n implme lies aux oprations otez que Feuilles. N entre elles certaines d orcment nont pas f r un Le Composite a pour rle de de sens pou Dans ce cas, dfinir le comportement des Composite. nrer une composants qui ont des enfants on peut g et de mmoriser les composants exception. enfants.
getChild(int) operation()

remove(Component)

Composant, Composite, Arborescences? Je my perds un peu.

R:

Un composite contient des composants. Il y a deux sortes de composants: les composites et les feuilles. Vous avez dit rcursif? Oui. Un composite contient un ensemble denfants qui peuvent tre leur tour des composites ou des feuilles.

Q:

Et quel est le rapport avec les itrateurs?

358

Chapitre 9

les patterns Itrateur et Composite

Concevoir des Menus avec un Composite


Comment allons-nous donc appliquer le pattern Composite nos menus? Pour commencer, nous devons crer une interface composant; celle-ci servira dinterface commune aux menus et aux plats et nous permettra de les traiter de la mme manire. Autrement dit, nous pourrons appeler les mmes mthodes sur les menus et sur les plats. Maintenant, cela na peut-tre pas beaucoup de sens dappeler certaines mthodes sur un plat ou sur un menu, mais nous pouvons le grer et nous allons le faire dans un moment. Pour linstant, jetons un coup dil une esquisse de la faon dont les menus vont sinscrire dans la structure du pattern Composite:

rface utiliser linteder aussi va e us ve r e S cc La eMenu pour a ComposantD nus quaux Plats. bien aux Me
Serveuse

ComposantDeMenu reprsente linterface commune Plat et Menu. Nous avons utilis une classe abstraite parce que nous voulons produire des implmentations par dfaut de ces mthodes.

ComposantDeMenu
getNom() getDescription() getPrix() estVegetarien() afficher() ajouter(Composant) supprimer(Composant) getEnfant(int)

Voici les mthodes qui permettent de manipuler les composants.Les composants sont Plat et Menu.
Les classes Plat et Menu redfinissent toutes deux afficher().
Plat
getNom() getDescription() getPrix() estVegetarien() afficher()

Menu
composantsMenu getNom() getDescription() afficher() ajouter(Composant) supprimer(Composant) getEnfant(int)

Nous avons conserv certaines mthodes de nos prcdentes versions de Plat et de Menu et nous avons ajout afficher(), ajouter(), supprimer() et getEnfant(). Nous allons dcrire ces dernires bientt, quand nous crirons nos nouvelles classes Menu et Plat.

Plat redfinit les mthod utilise les implmentation es qui ont du sens, et dans ComposantDeMenu s par dfaut figurant pour celles qui nen ont pas (comme ajouter() ce dajouter un composant la na pas de sens un Plat... nous ne pouvons ajouter de composa nts qu un Menu).

mthodes qui Menu redfinit galement les servent ajouter ont un sens, comme celles qui utres menus!) et supprimer des plats (ou daEn outre, partir de composantsMenu. getNom() et nous utiliserons les mthodes ner le nom et la getDescription() pour retour description du menu.
vous tes ici

359

implmenter des menus composites

Implmenter ComposantDeMenu
Bien. Nous allons commencer par la classe abstraite ComposantDeMenu. Souvenez-vous: son rle consiste fournir une interface abstraite aux nuds feuilles et aux nuds composites. Maintenant, vous vous demandez peut-tre si ComposantDeMenu ne joue pas deux rles. Cela pourrait bien tre le cas et nous reviendrons sur ce point. Toutefois, nous allons fournir pour linstant une implmentation par dfaut des mthodes, de sorte que si le Plat (la feuille) ou le Menu (le composite) ne veut pas implmenter certaines mthodes (comme getEnfant() pour un nud feuille), ils puissent se rabattre sur un comportement de base:

Tous les composants doivent implmenter linterface ComposantDeMenu. Toutefois, comme les feuilles et les nuds jouent des rles diffrents, nous ne pouvons pas dfinir une implmentation par dfaut qui ait un sens pour chaque mthode. Parfois, le mieux que vous puissiez faire est de lancer une exception lexcution.

implmentation ComposantDeMenu fournit unede. par dfaut pour chaque mtho

public abstract class ComposantDeMenu{

Comme certaines de ces mthodes nont de sens que pour les Plats et que dautres nont de sens que pour les Menus, limplmentation par dfaut consiste lancer une UnsupportedOperationException. De cette faon, si Plat ou Menu ne prend pas en charge une opration, ils na rien dautre faire que dhriter limplmentation par dfaut.

public void ajouter(ComposantDeMenu composantDeMenu) { throw new UnsupportedOperationException(); } public void supprimer(ComposantDeMenu composantDeMenu) { throw new UnsupportedOperationException(); } public ComposantDeMenu getEnfant(int i) { throw new UnsupportedOperationException(); } public String getNom() { throw new UnsupportedOperationException(); } public String getDescription() { throw new UnsupportedOperationException(); } public double getPrix() { throw new UnsupportedOperationException(); } public boolean estVegetarien() { throw new UnsupportedOperationException(); } public void afficher() { throw new UnsupportedOperationException(); } }

Nous avons regroup les mthodes composites autrement dit celles qui permettent daccder aux ComposantDeMenu, den ajouter et den supprimer.
ion, Voici les mthodes oprat les par es celles qui sont utilis s Plats. Il savre que nous pouvonns da x deu en employer galement le le code du Menu, comme vous es. pag verrez dans quelques

afficher() est une mthode opration que nos Menus et nos Plats implmenteront, mais nous fournissons une implmentation par dfaut.

360

Chapitre 9

les patterns Itrateur et Composite

Implmenter le Plat
Bien, voyons maintenant la classe Plat. Souvenez-vous: cest la classe feuille dans le diagramme de Composite et elle implmente le comportement des lments du composite.

Je suis heureux que nous prenions cette direction. Je pense que cela va me permettre dimplmenter ce menu de crpes dont jai toujours rv.

public class Plat extends ComposantDeMenu { String nom; String description; boolean vegetarien; double prix; public Plat(String nom, String description, boolean vegetarien, double prix) { this.nom = nom; this.description = description; this.vegetarien = vegetarien; this.prix = prix; } public String getNom() { return nom; } public String getDescription() { return description; } public double getPrix() { return prix; } public boolean estVegetarien() { return vegetarien; } public void afficher() { System.out.print( + getNom()); if (estVegetarien() { System.out.print((v)); } System.out.println(, + getPrix()); System.out.println( -- + getDescription()); } }

Nous devons dabord tendre linterface ComposantDeMenu.

Le constructeur accepte le nom, la description, etc. et mmorise une rfrence chacun. Voil qui est trs semblable notre ancienne implmentation des plats.

Voici nos mthodes daccs tout comme dans notre implmentation prcdente.

Ce code diffre de limplmentation prcdente. Ici, nous redfinissons la mthode afficher() de la classe ComposantDeMenu. Pour Plat, cette mthode affiche la totalit de lentre de menu: nom, description, prix et (v) si cest un plat vgtarien.

vous tes ici

361

structure composite

Implmenter le Composite
Maintenons que nous avons le Plat, il nous reste la classe du composite, que nous allons nommer Menu. Souvenez-vous: le composite peut contenir des Plats ou dautres Menus. Il y a deux mthodes de ComposantDeMenu que cette classe nimplmente pas: getPrix() et estVegetarien(), parce quelle nont pas beaucoup de sens pour un Menu.

Menu est galement un ComposantDeMenu, tout comme Plat.


public class Menu extends ComposantDeMenu { ArrayList composantsMenu = new ArrayList(); String nom; String description; public Menu(String nom, String description) { this.nom = nom; this.description = description; }

lconque Menu peut avoir un nombre que eMenu: nous ntD osa denfants de type Comp r les contenir. utiliserons une ArrayList pou

Ceci diffre de notre ancienne implmentation: nous allons attribuer chaque Menu un nom et une description. Auparavant, nous nous reposions simplement sur le fait davoir des classes diffrentes.

public void ajouter(ComposantDeMenu composantDeMenu) { composantsMenu.ajouter(composantDeMenu); } public void supprimer(ComposantDeMenu composantDeMenu) { composantsMenu.supprimer(composantDeMenu); } public ComposantDeMenu getEnfant(int i) { return (ComposantDeMenu)composantsMenu.get(i); } public String getNom() { return nom; }

Voici comment on ajoute des Plats ou dautres Menus un Menu. Comme les Plats et les Menus sont des ComposantDeMenu, il nous suffit dune seule mthode pour les deux. Vous pouvez galement supprimer un ComposantDeMenu ou accder un ComposantDeMenu.

Voici nos mthodes get pour obtenir le nom et la description.

public String getDescription() { return description; } public void afficher() { System.out.print(\n + getNom()); System.out.println(, + getDescription()); System.out.println(---------------------); } }

Remarquez que nous ne redfinissons ni getPrix() ni estVegetarien() parce que ces mthodes nont pas de sens pour un menu (mme si lon peut soutenir que estVegetarien() en a un. Si quelquun essaie dappeler ces mthodes sur un Menu, il reoit une UnsupportedOperationException.

Pour afficher le menu, nous affichons son nom et sa description

362

Chapitre 9

les patterns Itrateur et Composite Une petite seconde, Je ne comprends pas limplmentation dafficher(). Je pensais que jtais cens pouvoir appliquer les mmes oprations un composite qu une feuille. Si japplique afficher() un composite avec cette implmentation, je nobtiens rien dautre quun simple nom de menu et une description. Je nobtiens pas laffichage du COMPOSITE.

Bien vu. Comme le menu est un composite et contient aussi bien des plats que dautres menus, sa mthode afficher() devrait afficher tout ce quil contient. Sinon, nous devrions parcourir tout le composite et afficher chaque lment nous mmes. On dirait que cela compromet lintrt davoir une structure composite. Comme vous allez le voir, il est facile dimplmenter correctement afficher() parce que nous pouvons compter sur le fait que chaque composant sait safficher lui-mme. Une petite merveille de rcursivit! Vrifions:

Corriger la mthode afficher()


public class Menu extends ComposantDeMenu { ArrayList composantsMenu = new ArrayList(); String nom; String description; // code du constructeur // autres mthodes public void afficher() { System.out.print(\n + getNom()); System.out.println(, + getDescription()); System.out.println(---------------------); Iterator iterateur = composantsMenu.iterator(); while (iterateur.hasNext()) { ComposantDeMenu composantDeMenu = (ComposantDeMenu)iterateur.next(); composantDeMenu.afficher(); } } }

ur mthode afficher() po la ier dif mo de fit suf sur Il ent les informations quelle affiche non seulemses composants: dautres ce Menu, mais aussi tous Menus et des Plats.

Regardez! Nous utilisons un itrateur. Il nous sert parcourir tous les lments du Menu... Il peut sagir dautres Menus ou bien de Plats. Comme les Menus ET les Plats implmentent afficher(), nous nous contentons dappeler afficher() et cest eux de faire le reste. NOTE: Si, durant cette itration, Nous rencontrons un autre objet Menu, sa mthode afficher() lancera une autre itration, et ainsi de suite.
vous tes ici

363

tester le menu composite

Prparer le test
Il est presque temps de tester ce code, mais il faut dabord mettre jour le code de la Serveuse. Aprs tout, cest elle le principal client:

public class Serveuse { ComposantDeMenu tousMenus; public Serveuse(ComposantDeMenu tousMenus) { this.tousMenus = tousMenus; } public void afficherMenu() { tousMenus.afficher(); } }

se nest pas Et voil! Le code de la Serveu fit de lui suf il plus compliqu. Maintenant, , celui qui transmettre le composant racine us lavons No . nus contient tous les autres me nomm tousMenus.
Pour afficher toute la hirarchie de menus tous les menus et tous les plats elle na rien dautre faire quappeler afficher() sur le menu racine. Nous allons avoir une Serveuse tr s heureuse.

Parfait! Il nous reste une dernire chose faire avant dexcuter notre test: nous faire une ide de laspect quaura le menu composite au moment de lexcution:

Chaque Menu et chaque Plat implmentent linterface ComposantDeMenu.

Composite
To

Le menu racine cont les menus et les plat ient tous s

Composite
Chaque Menu contient des plats... ...ou des plats et dautres menus.
Plat

n us les Me

nu C

ia

us

rperi

en u Cafeter

Composite

u B sse ra

feuille feuille
Pl at
P l at

ts

P la t

Plat

P lat

Plat

Plat

rie

e M

en

en

u D s se r e

P lat

P l at

Plat

Plat

P la t

feuille

feuille
364
Chapitre 9

les patterns Itrateur et Composite

crire le code du test...


Il ny a plus qu crire le test. Contrairement la prcdente version, nous allons grer toute la cration des menus dans le code du test. Nous pourrions demander chaque chef de nous fournir son nouveau menu, mais mieux vaut tester le tout dabord. Voici le code:

public class TestMenu { public static void main(String args[]) { Crons dabord les ComposantDeMenu menuCreperie = new Menu(MENU CRPERIE, Brunch); objets menus. ComposantDeMenu menuCafeteria = new Menu(MENU CAFETERIA, Djeuner); Il nous faut galement ComposantDeMenu menuBrasserie = un menu racine que nous new Menu(MENU BRASSERIE, Dner); ComposantDeMenu menuDesserts = allons nommer tousMenus. new Menu(MENU DESSERT, Rien que des desserts!); ComposantDeMenu tousMenus = new Menu(TOUS LES MENUS, Toutes nos offres); tousMenus.ajouter(menuCreperie); tousMenus.ajouter(menuCafeteria); tousMenus.ajouter(menuBrasserie); // ajouter des plats menuCafeteria.ajouter(new Plat( Pasta al pesto, Spaghetti, ail, basilic et parmesan, true, 3.89)); menuCafeteria.ajouter(menuDesserts);

Nous utilisons la mthode ajouter() du Composite pour ajouter chaque menu au menu racine, tousMenus. Il faut maintenant ajouter tous les plats. Voici un exemple. Pour le reste, consultez le code source complet.
. Et nous ajoutons galement un menu un menu tout ia: eter Caf menu e ress Une seule chose int ce quil contient, quil sagisse dun plat ou dun menu, est un ComposantDeMenu.

menuDesserts.ajouter(new Plat( Tarte du chef, Tarte aux pommes avec une boule de glace vanille, true, 1.59)); // ajouter des plats Serveuse serveuse = new Serveuse(tousMenus); serveuse.afficherMenu(); } }

On ajoute une tarte aux pommes la carte des desserts...


Une fois que nous avons construit toute la hirarchie de menus, nous transmettons le tout la Serveuse. Comme vous lavez constat, laffichage va tre du gteau!.
vous tes ici

365

les responsabilits dun composite

Excuter le test...
Fichier dition Fentre Aide OmeletteAuxFinesHerbes

NOTE: ce rsultat est bas sur le code source complet.

% java TestMenu TOUS LES MENUS, Toutes nos offres --------------------MENU CRPERIE, Brunch --------------------Crpe loeuf(v), 2.99 -- Crpe avec oeuf au plat ou brouill Crpe complte, 2.99 -- Crpe avec oeuf au plat et jambon Crpe forestire(v), 3.49 -- Myrtilles fraches et sirop de myrtille Crpe du chef(v), 3.59 -- Crme frache et fruits rouges au choix

Voici tous nos menus... Nous avons affich tout ceci en appelant simplement afficher() sur le menu racine.

MENU CAFETERIA, Djeuner --------------------Salade printanire(v), 2.99 -- Salade verte, tomates, concombre, olives, pommes de terre Salade Parisienne, 2.99 -- Salade verte, tomates, poulet, emmental Soupe du jour(v), 3.29 -- Soupe du jour et crotons grills Quiche aux fruits de mer, 3.05 -- Pte brise, crevettes, moules, champignons Quiche aux pinards(v), 3.99 -- Pte feuillete, pommes de terre, pinards, crme frache Pasta al pesto(v), 3.89 -- Spaghetti, ail, basilic, parmesan MENU DESSERT, Rien que des desserts ! --------------------Tarte du chef(v), 1.59 -- Tarte aux pommes et boule de glace la vanille Charlotte maison(v), 1.99 -- Charlotte aux poires et sauce au chocolat Duos de sorbets(v), 1.89 -- Une boule fraise et une boule citron vert MENU BRASSERIE, Dner --------------------Omelette sarladaise(v), 3.99 -- Omelette aux champignons et pommes sautes Soupe de poissons, 3.69 -- Soupe de poissons, rouille et crotons Tagliatelles Primavera(v), 4.29 -- Ptes fraches, brocoli, petits pois, crme frache % 366
Chapitre 9

La nouvelle carte de desserts est affiche quand nous affichons tous les composants du menu de la Cafeteria.

les patterns Itrateur et Composite

Quest-ce que cest que cette histoire? Dabord vous nous dites une classe, une responsabilit et puis vous nous apprenez un pattern o il y a une responsabilit dans une seule classe. Le pattern Composite gre une hirarchie ET excute des oprations lies aux Menus.

Il y a du vrai dans cette observation. Nous pourrions dire que le pattern Composite sacrifie le principe de Responsabilit unique la transparence. Quest-ce que la transparence? Eh bien, en permettant une interface Composant de contenir les oprations de gestion des enfants ET les oprations des feuilles, nous permettons galement un client de traiter les composites et les nuds feuilles de manire uniforme. La nature dun lment composite ou nud feuille devient ainsi transparente pour le client. Maintenant, tant donn que nous avons les deux types doprations dans la classe Composant, nous y perdons quelque peu en scurit parce quun client peut essayer dappliquer une opration inapproprie ou sans objet un lment (par exemple tenter dajouter un menu un plat). Cest l un choix de conception: nous pourrions prendre une autre direction, sparer les responsabilits et les attribuer des interfaces. Notre conception serait plus sre, au sens o tout appel de mthode injustifi serait intercept lors de la compilation ou de lexcution, mais nous perdrions de la transparence et notre code devrait contenir des instructions conditionnelles et utiliser loprateur instanceof. Pour revenir votre question, il sagit l dun cas classique de compromis. Nous nous guidons sur des principes, mais nous devons toujours observer leffet quils ont sur nos conceptions. Parfois, nous procdons dessein dune faon qui semble enfreindre le principe, mais cest dans certains cas une question de perspective. Par exemple, il peut paratre incorrect davoir des oprations de gestion des lments enfants au niveau des feuilles (comme ajouter(), supprimer() et getEnfant()), mais, l encore, vous pouvez toujours changer de point de vue et voir une feuille comme un nud qui na pas denfants.

vous tes ici

367

flash-back sur Itrateur

Flash-back sur Itrateur


Quelques pages en arrire, nous vous avons promis de vous montrer comment combiner un Itrateur et un Composite. Vous savez que nous utilisons dj Itrateur dans notre implmentation interne de la mthode afficher(), mais nous voulons galement permettre la Serveuse de parcourir la totalit dun composite si elle en a besoin, par exemple si elle veut lire tout le menu et extraire les plats vgtariens. Pour implmenter un itrateur avec Composite, ajoutons une nouvelle mthode creerIterateur() dans chaque composant. Nous commencerons par la classe abstraite ComposantDeMenu:
ComposantDeMenu
getNom() getDescription() getPrix() estVegetarien() afficher() ajouter(Composant) supprimer(Composant) getEnfant(int) creerIterateur()

Nous avons ajout une mthode creerIterateur() au ComposantDeMenu. Cela signifie que chaque Menu et chaque Plat devront implmenter cette mthode. Cela signifie galement que lappel de creerIterateur() sur un composite doit sappliquer tous les enfants du composite.

Nous devons maintenant implmenter cette mthodes dans les classes Menu et Plat:
public class Menu extends ComposantDeMenu { // le reste du code ne change pas

public Iterator creerIterateur() { return new IterateurComposite(composantsMenu.iterator()); } }

Ici, nous utilisons un nouvel itrateur nomm IterateurComposite. Il sait comment parcourir nimporte quel composite. Nous lui passons litrateur courant du composite.

public class Plat extends ComposantDeMenu { // le reste du code ne change pas public Iterator creerIterateur() { return new IterateurNull(); } }

Hein? Do sort cet IterateurNull? Vous allez bientt le savoir.

Et maintenant, le Plat...

368

Chapitre 9

les patterns Itrateur et Composite

LIterateurComposite
LIterateurComposite est un itrateur SRIEUX. Il a pour tche de parcourir les lments du composant et de sassurer que tous les Menus enfants (et les petits-enfants, etc.) sont inclus. Voici le code. Attention, il nest pas bien long mais il peut tre un peu cassette. Il suffit de rpter ce mantra: la rcursion est mon amie, la rcursion est mon amie.

import java.util.*;

Comme pour tous les itrateurs, nous implmentons l interface java. util.Iterator.

Rcursivit 100mtres

public class IterateurComposite implements Iterator { Stack pile = new Stack(); public IterateurComposite(Iterator iterateur) { pile.push(iterateur); }

Litrateur du composite racine que nous allons parcourir est transmis. Nous utilisons une pile comme structure de donnes. Bien. Quand le client veut accder llment suivant, nous vrifions quil y en a un en appelant hasNext()...

public Object next() { if (hasNext()) { Iterator iterateur = (Iterator) pile.peek(); ComposantDeMenu composant = (ComposantDeMenu) iterateur.next(); if (composant instanceof Menu) { pile.push(composant.creerIterateur()); Sil y a un lment, nous dpilons } return composant; litrateur courant et nous lisons } else { son lment suivant. return null; } Si cet lment est un menu, nous avons un } autre composite qui doit tre inclus dans public boolean hasNext() { pile. Dans les deux if (pile.empty()) { le composant. return false; } else { Iterator iterateur = (Iterator) pile.peek(); if (!iterateur.hasNext()) { pile.pop(); return hasNext(); } else { return true; Sinon, il y a un lment et nous } retournons true. } } public void remove() { throw new UnsupportedOperationException(); } }

litration. Nous le plaons donc sur la cas, nous retournons

Pour savoir sil y a un lment suivant, nous regardons si la pile est vide. Si oui, il ny en a pas. Sinon, nous extrayons litrateur du sommet de la pile et nous regardons sil a un lment suivant. Sil nen a pas, nous le dpilons et nous appelons hasNext() rcursivement.

Nous ne prenons en charge que la navigation, pas la suppression.


vous tes ici

369

interne et externe

Voil du code srieux... Jessaie de comprendre pourquoi parcourir un composite de cette manire est plus difficile que le code de litration quon a crit pour afficher() dans la classe ComposantDeMenu?

Quand nous avons crit la mthode afficher() dans la classe ComposantDeMenu, nous avons utilis un itrateur pour parcourir chaque lment du composant. Si cet lment tait un Menu (et non un Plat), nous appelions rcursivement la mthode afficher(). Autrement dit, le ComposantDeMenu grait litration lui-mme, en interne. Dans ce code, nous implmentons un itrateur externe et il y a beaucoup dautres choses prendre en compte. Pour commencer, un itrateur externe doit maintenir sa position dans litration, afin quun client externe puisse piloter litration en appelant hasNext() et next(). Mais, dans ce cas, notre code a galement besoin de maintenir cette position sur une structure composite, rcursive. Cest pourquoi nous utilisons une pile pour maintenir notre position mesure que nous nous dplaons dans la hirarchie du composite.

370

Chapitre 9

les patterns Itrateur et Composite

A
}

Musclez vos neurones

Tracez un diagramme des Menus et des Plats. Puis imaginez que vous tes lIterateurComposite et que votre tche consiste grer les appels de hasNext() et de next(). Indiquez la faon dont lIterateurComposite navigue dans cette structure quand ce code est excut: public void testIterateurComposite(ComposantDeMenu composant) { IterateurComposite iterateur = new IterateurComposite(composant.iterateur); while(iterateur.hasNext()) { ComposantDeMenu composant = iterateur.next() ; }

vous tes ici

371

litrateur null

Litrateur null
Maintenant, revenons litrateur null. On peut le voir de la manire suivante. Un Plat na rien quon puisse parcourir, daccord? Alors comment gre-t-on limplmentation de sa mthode creerIterateur()? Eh bien, nous avons deux solutions:

Solution 1 :
Retourner null Nous pourrions retourner null depuis creerIterateur(), mais il faudrait des instructions conditionnelles dans le code client pour savoir si null a t retourn ou non.

emple du NOTE: Un autre ex Objet nul. n design patter

Solution 2 :
Retourner un itrateur qui retourne toujours false quand hasNext() est appele Cela semble plus judicieux. Nous pouvons toujours retourner un itrateur, mais le client na jamais se soucier de savoir si null a t retourn ou non. En effet, nous crons un itrateur mais il ny a pas dopration.

La seconde solution semble bien meilleure. Appelons-la IterateurNull et implmentons-la.


import java.util.Iterator; public class IterateurNull implements Iterator { public Object next() { return null; } public boolean hasNext() { return false; } public void remove() { throw new UnsupportedOperationException(); } }

Voil litrateur le plus paresseux du monde: chaque tape, il se tourne les pouces.

Quand next() est appele, nous retournons null. Plus important encore: quand hasNext() est appele, nous retournons toujours false.

Et IterateurNull ne songe mme pas soccuper des suppressions.

372

Chapitre 9

les patterns Itrateur et Composite

Donnez-moi le menu vgtarien


Maintenant, nous disposons dune faon de parcourir chaque lment du Menu. Utilisons-la et donnons notre Serveuse une mthode qui nous indique exactement quels plats sont vgtariens.

public class Serveuse { ComposantDeMenu tousMenus; public Serveuse(ComposantDeMenu tousMenus) { this.tousMenus = tousMenus; } public void afficherMenu() { tousMenus.afficher(); } public void afficherMenuVegetarien() { Iterator iterateur = tousMenus.creerIterateur(); System.out.println(\nMENU VGTARIEN\n----); while (iterateur.hasNext()) { ComposantDeMenu composantDeMenu = (ComposantDeMenu)iterateur.next(); try { if (composantDeMenu.estVegetarien()) { composantDeMenu.afficher(); } } catch (UnsupportedOperationException e) {} } } }

La mthode afficherMenuVegetarien() prend le composite tousMenus et obtient son itrateur. Ce sera notre IterateurComposite. Parcourir chaque lment du composite.

Appeler la mthode estVegetarien() de chaque lment. Si vrai, appeler sa mthode afficher().


afficher() nest appele que sur les Plats, jamais sur les composites. Voyezvous pourquoi?

Nous avons implment estVegetarien() pour que les Menus lancent toujours une exception. Si cela se produit, nous interceptons l exception mais nous poursuivons litration.

vous tes ici

373

la magie dItrateur et Composite

La magie dItrateur & Composite en duo...


Eh bien! Il en a fallu un effort de dveloppement pour parvenir jusquici! Maintenant, nous avons une structure de menus gnrale qui devrait durer un certain temps au nouvel empire de la restauration. Cest le moment de se reposer et de commander quelque chose:

Fichier dition Fentre Aide OmeletteAuxChampignons

% java TestMenu MENU VGTARIEN Le menu vgtarien est compos de tous ---les plats vgtariens de chaque menu. Crpe loeuf(v), 2.99 -- Crpe avec oeuf au plat ou brouill Crpe forestire(v), 3.49 -- Myrtilles fraches et sirop de myrtille Crpe du chef(v), 3.59 -- Crme frache et fruits rouges au choix Salade printanire(v), 2.99 -- Salade verte, tomates, concombre, olives, pommes de terre Soupe du jour(v), 3.29 -- Soupe du jour et crotons grills Quiche aux pinards(v), 3.99 -- Pte feuillete, pommes de terre, pinards, crme frache Pasta al pesto(v), 3.89 -- Spaghetti, ail, basilic, parmesan Tarte du chef(v), 1.59 -- Tarte aux pommes et boule de glace la vanille Charlotte maison(v), 1.99 -- Charlotte aux poires et sauce au chocolat Duo de sorbets(v), 1.89 -- Une boule fraise et une boule citron vert Tarte du chef(v), 1.59 -- Tarte aux pommes et boule de glace la vanille Charlotte maison(v), 1.99 -- Charlotte aux poires et sauce au chocolat Duo de sorbets(v), 1.89 -- Une boule fraise et une boule citron vert Omelette sarladaise(v), 3.99 -- Omelette aux champignons et pommes sautes Tagliatelles Primavera(v), 4.29 -- Ptes fraches, brocoli, petits pois, crme frache %

374

Chapitre 9

les patterns Itrateur et Composite Dans votre mthode afficherMenuVegetarien(), jai remarqu que vous utilisiez un bloc try/catch pour grer la logique des Menus qui ne supportent pas la mthode estVegetarien(). Jai toujours entendu dire que ce ntait pas une bonne forme de programmation.

Voyons un peu ce dont vous parlez:


try { if (composantDeMenu.estVegetarien()) { composantDeMenu.afficher(); } } catch (UnsupportedOperationException) {}

egetarien() sur Nous appelons estV eMenu, mais les tous les ComposantD exception parce Menus lancent une s en charge quils ne prennent pa lopration.

Si le composant de menu ne prend pas en charge lopration, nous nous contentons de lancer lexception et de lignorer.
En gnral, nous sommes daccord: try/catch sert grer les erreurs, non la logique applicative. Quelles sont nos autres options? Nous aurions pu vrifier le type du composant lexcution avec instanceof pour vrifier que cest un Plat avant dappeler estVegetarien(). Mais nous perdrions du mme coup en transparence parce que nous ne traiterions pas les Menus et les Plats de la mme manire. Nous pourrions galement modifier la mthode estVegetarien() dans les Menus pour quelle retourne faux. Cest une solution simple qui prserve la transparence. Dans notre solution, nous recherchons la clart: nous voulons rellement communiquer lide que cette opration nest pas prise en charge sur le Menu (ce qui nquivaut pas dire que estVegetarien() est faux). Cela permet galement un dveloppeur dimplmenter une mthode estVegetarien() raisonnable pour le Menu et quelle fonctionne avec le code existant. Cest notre choix, et nous nous y tenons.
vous tes ici

375

interview de Composite

Interview
Cette semaine: Le pattern Composite nous parle de problmes dimplmentation DPTLP : Nous nous entretenons ce soir avec le pattern Composite. Si vous nous parliez un peu de vous, Composite? Composite : Bien sr... Je suis le pattern utiliser quand vous avez des collections dobjets avec des relations tout-partie et que vous voulez pouvoir traiter ces objets de la mme manire. DPTLP : Bien, entrons dans le vif du sujet... Quentendez-vous par relations tout-partie? Composite : Imaginez une interface utilisateur graphique, une IHM. On y trouve souvent un composant principal, comme un cadre ou un panneau, qui contient dautres composants: menus, zones de texte, barres de dfilement et boutons. Votre IHM est donc compose de plusieurs parties, mais, quand vous laffichez, vous vous la reprsentez gnralement comme un tout. Vous dites au composant principal de safficher et vous comptez sur lui pour afficher les parties qui le composent. Nous appelons objets composites les composants qui contiennent dautres composants, et les objets qui ne contiennent pas dautres composants sont les objets feuilles. DPTLP : Cest ce que vous entendez par traiter les objets de la mme manire? Avoir des mthodes communes que vous pouvez appeler sur les composites et les feuilles? Composite : Exactement. Je peux dire un objet composite de safficher ou un objet feuille de safficher et ils se comporteront comme il faut. Le composite saffichera en disant tous ses composants de safficher. DPTLP : Cela implique que tous les objets aient la mme interface. Que se passe-t-il si les objets de votre composite font des choses diffrentes? Composite : Eh bien, pour que le composite fonctionne de manire transparente pour le client, vous devez implmenter la mme interface pour tous les objets du composite, sinon le client doit savoir quelle interface chaque objet implmente, ce qui est contraire au but 376
Chapitre 9

recherch. De toute vidence, cela signifie que vous aurez un moment donn des objets pour lesquels les appels de mthodes nauront pas de sens. DPTLP : Et comment rsolvez-vous le problme? Composite : Eh bien, il y a plusieurs solutions. Vous pouvez vous contenter de ne rien faire ou de retourner null ou false, selon ce qui qui convient votre application. Vous pouvez aussi tre plus proactif et lancer une exception. Bien sr, il faut que le client accepte de cooprer un peu et vrifie que lappel de mthode na rien provoqu dinattendu. DPTLP : Mais si le client ne sait pas quel objet il a affaire, comment pourra-t-il jamais savoir quel appel effectuer sans vrifier le type? Composite : Si vous tes quelque peu cratif, vous pouvez structurer vos mthodes pour que limplmentation par dfaut fasse quelque chose qui ait un sens. Par exemple, si le client appelle getEnfant() sur le composite, cela a un sens. Et cela a galement un sens sur une feuille, si vous voyez la feuille comme un objet qui na pas denfants. DPTLP : Ah... fut! Mais jai entendu dire que certains clients taient si contraris par ce problme quils exigeaient des interfaces spares pour les diffrents objets, afin quil soit impossible deffectuer des appels de mthode qui nont pas de sens. Est-ce que cest toujours le pattern Composite? Composite : Oui. Cest une version beaucoup plus sre du pattern Composite, mais elle ncessite que le client vrifie le type de chaque objet avant de raliser un appel, pour que lobjet soit typ correctement. DPTLP : Dites-nous en un peu plus sur la faon dont le composite et les objets feuilles sont structurs. Composite : Cest gnralement une arborescence, une sorte de hirarchie. La racine est le composite de haut niveau, et tous ses enfants sont dautres composites ou des feuilles.

les patterns Itrateur et Composite

DPTLP : Est-ce quil arrive que les enfants pointent dans lautre sens, vers leurs parents? Composite : Oui, un composant peut avoir un pointeur vers un parent pour faciliter la navigation dans la structure. Et si vous avez une rfrence vers un enfant et que vous devez le supprimer, il faudra que le parent supprime lenfant. La rfrence au parent facilite galement cette opration. DPTLP : Il y a vraiment beaucoup de choses prendre en compte dans votre implmentation. Y a-t-il dautres facteurs considrer quand on implmente le pattern Composite? Composite : Oui, il y en a... Lun deux est lordre des enfants. Que se passe-t-il si un composite doit avoir des enfants ordonns de faon particulire? Dans ce cas, vous aurez besoin dun schma plus labor pour ajouter et supprimer les enfants, et vous devrez prter une attention particulire la faon de naviguer dans la hirarchie. TLP : Un point intressant. Je ny avais pas pens. Composite : Et aviez-vous pens la mise en cache? DPTLP : La mise en cache? Composite : Oui, la mise en cache. Parfois, si la structure composite est complexe ou coteuse traverser, il est utile dimplmenter une mise en cache des nuds. Par exemple, si vous parcourez en permanence un composite pour calculer un rsultat donn, vous pouvez implmenter un cache qui mmorise le rsultat temporairement pour conomiser des traverses. DPTLP : Eh bien, je naurais pas pens que le pattern Composite recelait tant de surprises. Avant de nous quitter, encore une question. Selon vous, quel est votre point fort le plus significatif ? Composite : Assurment, le fait de simplifier la vie de mes clients. Comme ils nont pas besoin de savoir sils ont affaire un objet composite ou un objet feuille, ils nont pas besoin dcrire des instructions if partout pour tre srs dappeler les bonnes mthodes sur les bons objets.

Ils peuvent souvent effectuer un seul appel de mthode et excuter une opration sur toute une structure. DPTLP : Cela semble un bnfice important. Vous tes sans nul doute un pattern trs utile pour collectionner et manipuler des objets. Mais nous sommes en retard sur lhoraire... Merci dtre venu et bientt pour une prochaine mission.

vous tes ici

377

mots-croiss

Il est de nouveau temps...


2 3 4

7 9

10 11 12

13 14 15 16

18

17 18

Horizontalement 1. Les packages dinterfaces utilisateurs utilisent souvent ce pattern pour leurs composants. 3. Les menus de la Brasserie serviront pour le ___________. 5. Parcours. 7. Peut tre abstraite. 9. Les plats dObjectville nen manquent pas. 12. Parcourt une collection. 14. On en trouve normalement dans une caftria. 16. La Caftria et la Crperie ont ralis une ___________. 17. Hashtable et ArrayList implmentent cette interface. 18. Il utilise une ArrayList pour implmenter ses menus.

Verticalement 1. Contenus dans des composites. 2. Maintenant, elle connat Java! 3. Ce menu nous a oblig modifier toute notre implmentation. 4. Il utilise un tableau pour implmenter ses menus. 5. Nous lavons encapsule. 6. A remplac Enumeration. 7. On utilise gnralement ce pattern pour crer des itrateurs. 8. Litrateur composite en utilise beaucoup. 10. Cette classe permet de crer des tales de hachage. 11. Collection et Iterator sont dans ce package. 13. Nous avons supprim IterateurMenueCreperie parce que cette classe fournit dj un itrateur. 15. Collection ordonne.

378

Chapitre 9

les patterns Itrateur et Composite

Qui fait quoi ?


g

Faites correspondre chaque pattern avec sa description:

Pattern Stratgie

Description Les clients traitent les collections dobjets et les objets individuels ^ manire de la meme Fournit un moyen de naviguer dans une collection dobjets sans en exposer limplmentation Simplifie linterface dun groupe de classes Modifie linterface dune ou plusieurs classes Permet un groupe dobjets dtre inform du changement dun tat Encapsule des comportements interchangeables et emploie la dlgation pour dcider lequel utiliser

Adaptateur

Itrateur

Faade Composite

Observateur

vous tes ici

379

votre bote outils de concepteur

POINTS DIMPACT

Un itrateur permet daccder

Votre bote outils de concepteur


Deux nouveaux patterns pour votre bote outils: deux faons gniales de grer des collections dobjets.

aux lments dun agrgat sans exposer sa structure interne.

Un itrateur a pour tche


de parcourir un agrgat et encapsule litration dans un autre objet.

O s Principes O OO Bactsioinc

Abstra ce qui varie. Encapsulez ncapsulation E sulation ap nc le ez Prfr me Polymorphis lhritage non , es ac f er t des in Hritage Programmez ations. t en m pl im des ler vous de coup ui q Efforcezs t je les ob faiblement nt. se is ag er t in ouvertes oivent tre d s se as s la cl e Les mais ferm er lextension e pour gr n. n. ip c in r p modificatio e r aut ptio ctions. Ne tes. encore un ent dans une conce des abstra r nc co m s e se g Dpendez pa s des clas le chan dpendez u vos amis Ne parlez q us vous pelez pas, no Ne nous aps. appelleron r quune ne doit avoi se as cl ne .U de changer seule raison c

Lorsque nous utilisons un


itrateur, nous soulageons lagrgat de la responsabilit de prendre en charge les oprations ncessaires pour naviguer dans ses donnes.

Un itrateur fournit une


interface commune pour parcourir les lments dun agrgat. Il permet ainsi dexploiter le polymorphisme pour crire le code qui utilise ces lments.

Il convient de sefforcer
de naffecter quune seule responsabilit chaque classe.

Le pattern Composite fournit


une structure qui peut contenir la fois des objets individuels et des composites.

Patterns OOune f ille une relation fa it inm

Un autre chapitre ave deux patterns pour le prix dun.

Le pattern Composite permet


aux clients de traiter les composites et les objets individuels de la mme manire.

s,rface it- d hacuc re sut eein f dsi de r n unin pulu -e h t ie ctu a -in na v tg t lep r ab e e e su n e a s gt p s rface n a Str e y c a it t s. O h ir e c le e a r b in d t s, u f t a e e n e e s D je g n m e t b n u h a ao m je it h r b n it l c o ne o un ind rp io eu c e e lg p t e t f .la is u u a trr in a ec s D dae t nD e sq ic m n d s r r , e n iq lo t e n b e d r m n sse na , n a it m je io e e e a s h b il F u p t o n b it is d q a y r a amr non d o na s ic m u e lg , d le e a o t e b ap l ux r n i n i u je ft u s s io u u b e q ded q F t u jo a ere o d t q s f x e a n t u r ix it u n e c e o r is e t d c h e d li n m c la p s n c a tmme u r t t r io s le ie o a e u n e e g t d o s j d g ie poeinco p e a , b t n r t t ix so ss a n o c n n u io et heuor t ta o cla n t c la e mn Str it re t t u n m ss esqu r lu d e le a u le un o m i o d s un g s r so o p e n if e f e in p ss t e x ul u S o t p la n ps a n e c u d io ca t t e in t t la se c t en n a u r nm e sa ic ricatio ie ae rst ie so t.erface ce r is m be st e p x la a lnin vare u in e d nIlso .ur a F n p t iq it ge .m le in a t nu rn t t niv e ra a re era m ie a se m t sa c t n ve e m n nit t is m nn .r io p u e a b n o s o t la t c y a c iq u n C st e o le ia F t e pa c in q a . l e n le r a t m a r i u fac b ie e o a s d st c ns n u t lo e n lut e n n e ai g in io r t a ss m n e ailuis l t linst e rr rd in la t st a p u oile ran ce y e os cria in t is ut nceo f n alt p r or g eta u se s d t sc d l it e o d eut au nn r d p ss ,t ra A u t e ,ff ues c la e u n r uf o je c je es a et b a io e u ob o t e d t ss n un g qu e n un a u c la l re c a e n iv d Itr f e d r e m e r e n t t a r e e d c u e t .. da en n ss p e s ereu e in la r dif ula ss r es, et dt fr mb cpa F ste qa ut t la e eune ea s c li d n a n e u le n n t s. d p e n a s ss sa io d se nt r t d n c A ie e c c e qu cl c L l l n s it se re . u at bjets en v r s.uic da fodes sog e ad r d u nt eoll eli da so e sllu un ss sf fsoo q ded la if if sa e cn ne se d sul.at lor ues p e pite , ae m .e rs so e rit le cam e ge c nsr a r r io o L n b at et r a e r ll e t i o t s op n u c e o q en s st c es e t de type ad p u ts-ja a m au t o t ses dla r iv ili ne C sssy scente pou sant/ ibf so usef la re rs cu d u a ve bo ion d ed hau rit esso ir aat s lu p t r le o u e .. an p t d r t u c e m p e et o is c r s c il reprsentat plusd a rm a s st f p u peu rnt des s fib ,na es hirarchie ed ie in ilr ae c rt at s. le lu at p p e m e e o d m c n s e in st t n s s sy e e r li c p sc urfa re te dinso ts permet aux compos. Il la mme faon les objeceux-ci e e d d s r aison traite t les combin individuels e
Chapitre 9

Un composant est un objet


quelconque dans une structure de composite. Les composants peuvent tre dautres composites ou des nuds feuilles.

Une conception implmentant


un composite entrane de nombreux compromis. Vous devez quilibrer transparence et sret en fonction de vos besoins.

380

les patterns Itrateur et Composite

Solutions des exercices

vos your crayons Sharpen pencil


Par rapport notre implmentation dafficherMenu(), quelles sont les affirmations qui sont vraies?

A. Nous codons pour des implmentations


concrtes de MenuCreperie et de MenuCafeteria, non pour une interface.

D. La Serveuse doit savoir comment chaque


menu reprsente sa collection de plats interne, ce qui viole lencapsulation.

B. La Serveuse nimplmentant pas lAPI


Java Waitress, elle nadhre pas un standard.

E. Nous avons du code dupliqu: la


mthode afficherMenu() a besoin de deux boucles spares pour parcourir les deux types de menus. Et si nous ajoutions un troisime menu, il nous faudrait une nouvelle boucle.

C. Si nous dcidions de passer de lemploi


de MenuCafeteria un autre type de menu qui implmenterait sa liste de plats sous forme de Hashtable, le code de Serveuse ncessiterait beaucoup de modifications.

F. Limplmentation ntant pas base sur


MXML (Menu XML), elle ne prsente pas linteroprabilit dsirable.

vos crayons Sharpen your pencil


Avant de regarder la page suivante, notez rapidement les trois modifications ncessaires pour adapter ce code la structure de notre application:

1. implmenter linterface Menu 2. nous dbarrasser de getPlats() 3. ajouter creerIterateur() et retourner un itrateur capable de parcourir les valeurs de la table.

vous tes ici

381

solutions des exercices

Solution du jeu du frigo


La classe IterateurMenuCafeteriaAlternateur reconstitue:
import java.util.Iterator; import java.util.Calendar;

public class IterateurMenuCafeteriaAlternateur


Plat[] plats; int position;

this.plats = plats; Calendar maintenant = Calendar.get Instance(); position = maintenant.get(Calendar. DAY_OF_WEEK) % 2;

}
public boolean hasNext() {

null) { if (position >= plats.length || plats[position] == return false; } else { return true; }

}
public Object next() {

Plat plat = plats[position]; position = position + 2; return menuItem;

}
public void remove() {

Remarquez que cette implmentation ne supporte pas remove()

throw new UnsupportedOperationException( IterateurMenuCafeteriaAlternateur ne supporte pas remove ());


} }

382

Chapitre 9

r(Plat[] plats) { public IterateurMenuCafeteriaAlternateu

implemen ts Itera tor

les patterns Itrateur et Composite

Qui fait quoi ?


g

Faites correspondre chaque pattern avec sa description:

Pattern Stratgie

Description Les clients traitent les collections dobjets et les objets individuels de ^ la mme manire Fournit un moyen de naviguer dans une collection dobjets sans en exposer limplmentation Simplifie linterface dun groupe de classes Modifie linterface dune ou plusieurs classes Permet un groupe dobjets dtre inform du changement dun tat Encapsule des comportements interchangeables et emploie la dlgation pour dcider lequel utiliser

Adaptateur

Itrateur

Faade Composite

Observateur

vous tes ici

383

solution de mots-croiss

Solutions des mots-croiss

C O

O
5

M I

P T

O E

S E

I A

T T
6

E I T E R A T O R
7

D E

O E L
8

M P O
9

R V E U S E

N F A B R I Q U

S S E R T
11

M P E L E M E N T
14

R E
10

A B R
12

A N T S

C U R Y L R S I O N S

H S

J T R E R U A

A H T A B L E

A V
13

C A T I O
16

A T I

15

. F U T I
18

I S L L E C T I O

17

O N

384

Chapitre 9

10 le pattern tat

Ltat des choses

Javais tellement cru que la vie serait facile Objectville Mais maintenant, chaque fois que jarrive au bureau, je reois une autre demande de changement. Je suis sur le point de craquer! Ah, si javais t plus assidue au groupe de patterns de Lucie du mercredi soir Je suis dans un de ces tats!

Un fait mconnu: les patterns Stratgie et tat sont des jumeaux qui ont t spars la naissance. Comme vous le savez,
le pattern Stratgie a cr une affaire des plus florissantes sur le march des algorithmes interchangeables. Quant lui, tat a peut-tre suivi une voie plus noble: il aide les objets contrler leur comportement en modifiant leur tat interne. On le surprend souvent dire ses clients Rptez aprs moi: Je suis bon, je suis intelligent, ma simple prsence suffit...

nouveau chapitre

385

faites la connaissance de Distribon

Nouveau casse-tte
Les grille-pain Java font terriblement annes90. Aujourdhui, on intgre Java de vraies machines, comme les distributeurs de bonbons. Oui! Les distributeurs de bonbons sont la pointe de la technologie. Les plus grands fabricants se sont rendu compte que linsertion dune unit centrale dans leurs appareils permettait daugmenter les ventes, de grer les stocks en ligne et de mesurer la satisfaction des consommateurs avec une prcision accrue. Mais ces fabricants sont des experts en distributeurs de bonbons, pas des dveloppeurs. Cest pourquoi ils ont sollicit votre aide:
t. Nous ce quils disen ne t es c , ns oi Du m us du taient fatig e au pensons quils esqu pr i remonte technologie qu le et quil leur fallait sic ur travail dix-neuvime de rendre le en oy m un er trouv . plus motivant

Distribon, SARL

Le monde dans lequel les distributeurs ne sont jamais vides

ibuteur s que le contrle du distr lmenter Voici comment nous penson imp rons que vous pourrez tdoit fonctionner. Nous esp peu tre des Comme nous ajouteronsre ple et ceci en Java pour nous!ir, la conception doit t aussi sou comportements laven sible! aussi facile maintenir que pos - Les ingnieurs de Distribon

e Plus d s o bonb n
ice insrer p

A unee pic
pice

tou

rne

rp

oig

ne

Pas dee pic


bon bon s>

jecter
r dlivren bonbo

bonbo

n Bonbo vendu

ns =

386

Chapitre 10

le pattern tat

Jetons un coup dil ce diagramme et voyons ce que les gens de Distribon attendent de nous...

Conversation dans un box

Line : On dirait un diagramme dtats. Guy: Oui, chacun de ces cercles est un tat... Line : ... et chaque flche reprsente une transition. Paul: Pas si vite, vous deux. Jai tudi les diagrammes dtat il y a longtemps. Pouvez-vous me rappeler de quoi vous parlez? Line : Bien sr, Paul. Regarde ces cercles: ce sont les tats. Pas de pice est probablement ltat initial du distributeur de bonbons, celui o il se contente dattendre que tu insres une pice. Tous les tats sont simplement les diffrentes configurations de lappareil qui se comportent dune certaine manire, et qui ont besoin quune action donne les fasse passer dans un autre tat.

Guy

Line

Paul

Guy: Oui, tu vois, pour quil y ait un autre tat, tu dois faire quelque chose, par exemple insrer une pice dans la machine. Tu vois la flche entre Pas de pice et A une pice? Paul: Oui... Guy: Cela signifie simplement que lappareil est dans ltat Pas de pice, et que si tu insres une pice il passera a ltat A une pice. Cest ce quon appelle une transition. Paul: Oh, Je vois! Et si je suis dans ltat A une pice, je peux tourner la poigne et passer ltat Bonbon vendu ou jecter la pice et revenir ltat Pas de pice. Line : Cest cela, Paul! Pour en revenir au diagramme, il na pas lair trop mal. De toute vidence, nous avons quatre tats, et je pense que nous avons aussi quatre actions: insrer pice, jecter pice, tourner poigne et dlivrer bonbon. Mais... quand nous dlivrons le bonbon, nous testons pour savoir sil reste zro bonbon ou plus dans ltat Bonbon vendu, puis nous passons soit ltat Plus de bonbons soit ltat Pas de pice. Nous avons donc cinq transitions dun tat un autre. Line: Ce test pour savoir sil reste des bonbons implique galement quon doit mmoriser le nombre de bonbons. Chaque fois que la machine nous donne un bonbon, ce peut tre le dernier. Et si cest le cas, nous devons passer ltat plus de bonbons. Guy : Noubliez pas non plus que lutilisateur pourrait faire quelque chose dabsurde, par exemple essayer djecter la pice quand lappareil est dans ltat Pas de pice ou encore insrer deux pices. Paul: Oh, je ny avais pas pens. Nous devrons galement nous en occuper. Guy: Pour chaque action possible, il suffit de regarder dans quel tat nous sommes et dagir en consquence. Nous pouvons le faire! Commenons par faire correspondre le diagramme dtats au code...

vous tes ici

387

vue densemble des machines tats

Machines tats: premire mouture


Comment allons-nous passer de ce diagramme dtats au code? Voici une rapide introduction limplmentation des machines tats:
1

Commenons par rassembler nos quatre tats:

Pas de pice

A une pice

n Bonbo vendu e Plus d s bonbon

Voici les tats - quatre en tout.

Puis crons une variable dinstance pour contenir ltat courant et dfinissons des valeurs pour chaque tat:

Appelons Plus de bonbons Epuis pour abrger.


final final final final static static static static int int int int EPUISE = 0; SANS_PIECE = 1; A_PIECE = 2; VENDU = 3;

Voici chaque tat reprsent par un entier unique.


...et voil la variable dinstance qui contient ltat courant. Nous la positionnons Epuise puisque lappareil est vide quand on le sort de sa bote et quon le met en service.

int etat = EPUISE;

Nous recherchons maintenant toutes les actions qui peuvent se produire dans le systme.

tourner poigne insrer pice jecter pice dlivrer


de lune Par rapport au diagramme, lappel lencher une dc t quelconque de ces actions peu transition.
388
Chapitre 10

Ces actions reprsentent linterface du distributeur: ce que vous pouvez faire avec.

dlivrer est plutt une action interne que la machine invoque sur elle-mme.

le pattern tat

Maintenant, nous crons une classe qui va jouer le rle de machine tats. Pour chaque action, nous crons une mthode qui utilise des instructions conditionnelles pour dterminer le comportement appropri chaque tat. Par exemple, pour laction dinsrer une pice, nous pourrions crire une mthode comme celle-ci:

public void insererPiece() { if (etat == A_PIECE) { System.out.println(Vous ne pouvez plus insrer de pices); } else if (etat == EPUISE) {

Chaque tat possible est test laide dune instruction conditionnelle...

System.out.println(Vous ne pouvez pas insrer de pice, nous sommes en rupture de stock); } else if (etat == VENDU) { System.out.println(Veuillez patienter, le bonbon va tomber); } else if (etat == SANS_PIECE) { etat = A_PIECE; System.out.println(Vous avez insr une pice); } }

comportement ...et prsente le chaque tat possible... appropri pour


...mais peut galement dclencher une transition vers dautres tats, comme le montre le diagramme..

Il sagit ici dune technique courante: modliser un tat dans un objet en crant une variable dinstance qui contient les valeurs de ltat et crire des instructions conditionnelles dans nos mthodes pour grer les diffrents tats.

Aprs ce bref aperu, implmentons notre distributeur de bonbons!


vous tes ici

389

implmenter le distributeur de bonbons

crire le code
Il est temps dimplmenter notre Distributeur. Nous savons que nous allons avoir une variable dinstance qui contiendra ltat courant. partir de l, il suffit de grer toutes les actions, tous les comportements et toutes les transitions qui peuvent se produire. Pour les actions, nous devons implmenter linsertion dune pice, ljection dune pice, laction sur la poigne et la dlivrance du bonbon. Il nous faut galement implmenter le code qui teste si la machine est vide ou non.

public class Distributeur { final final final final static static static static int int int int EPUISE = 0; SANS_PIECE = 1; A_PIECE = 2; VENDU = 3;

t s: ils correspondenn. at t re at qu s le ribo Voici e dtats de Dist ceux du diagramm orise Voici la variable dinstance qui va mm ltat s dan s on men ltat courant. Nous com EPUISE.

int etat = EPUISE; int nombre = 0; public Distributeur(int nombre) { this.nombre = nombre; if (nombre > 0) { etat = SANS_PIECE; } }

Nous avons une deuxime variable dinstance qui mmorise le nombre de bonbons restant dans la machine. Le constructeur accepte le stock de bonbons initial. Si le stock est diffrent de zro, la machine entre dans ltat SANS_PIECE, signifiant quelle attend que quelquun insre une pice. Sinon, elle reste dans ltat EPUISE.

Quand une pice est insre, si.... une autre pice est dj public void insererPiece() { insre, nous lindiquons au if (etat == A_PIECE) { System.out.println(Vous ne pouvez plus insrer de pices); client ; } else if (etat == SANS_PIECE) { sinon, nous acceptons la pice etat = A_PIECE; et dclenchons une transition System.out.println(Vous avez insr une pice); vers ltat A_PIECE. } else if (etat == EPUISE) {
System.out.println(Vous ne pouvez pas insrer de pices, nous sommes en rupture de stock); } else if (etat == VENDU) { System.out.println(Veuillez patienter, le bonbon va tomber); } } } Si le client vient dacheter un bonbon, et si la machine est en rupture

enons Maintenant, nous comm ns sous tio ac implmenter les forme de mthodes....

il doit attendre que la transaction soit termine avant dinsrer une autre pice.

de stock, nous jectons la pice.

390

Chapitre 10

le pattern tat public void ejecterPiece() { ... Maintenant, si le client essaie de rcuprer sa pice if (etat == A_PIECE) { pi Sil y a une ce, nous la lui System.out.println(Pice retourne); rendons et nous retournons etat = SANS_PIECE; ltat SANS_PIECE. } else if (etat == SANS_PIECE) { System.out.println(Vous navez pas insr de pice); Sinon, sil ny en a pas, nous } else if (etat == VENDU) { pouvons pas la lui rendre. System.out.println(Vous avez dj tourn la poigne); } else if (etat == EPUISE) { System.out.println(jection impossible, vous navez pas insr de pice); } }

ne

Vous ne pouvez pas jecter: puisquil ny a pas de bonbons, la machine naccepte pas de pices!

Le client essaie de tourner la poigne...


Si le client vient de tourner la poigne, nous ne pouvons pas le rembourser: il a dj le bonbon!

public void tournerPoignee() { Quelquun essaie de tricher if (etat == VENDU) { System.out.println(Inutile de tourner deux fois!); Il faut dabord } else if (etat == SANS_PIECE) { System.out.println(Vous avez tourn mais il ny a pas de pice); une pice } else if (etat == EPUISE) { System.out.println(Vous avez tourn, mais il ny a pas de bonbons); Nous ne pouvons pas } else if (etat == A_PIECE) { dlivrer de bonbons, System.out.println(Vous avez tourn...); etat = VENDU; il ny en a pas. delivrer(); Victoire! Le client va avoir un } bonbon. On passe ltat VENDU et } dlivrer un bonbon

Appele pour

public void delivrer() { if (etat == VENDU) { System.out.println(Un bonbon va sortir); nombre = nombre - 1; if (nombre == 0) { System.out.println(Ae, plus de bonbons!); etat = EPUISE; } else { etat = SANS_PIECE; } } else if (etat == SANS_PIECE) { System.out.println(Il faut payer dabord); } else if (etat == EPUISE) { System.out.println(Pas de bonbon dlivr); } else if (etat == A_PIECE) { System.out.println(Pas de bonbon dlivr); } } // autres mthodes comme toString() et remplir() }

on appelle la mthode delivrer(). s Nous sommes dan on ltat VENDU: on! lui donne le bonb ition raitons la cond Voici o nous t ons. Si ctait le plus de bonb sitionnons ltat de dernier, nous po UISE; sinon nous la machine EP SANS_PIECE. revenons ltat
Rien de ceci ne devrait arriver. Mais si cest le cas, nous retournons une erreur, non un bonbon.

vous tes ici

391

tester le distributeur

Test maison
Ne dirait-on pas une bonne petite conception bien solide sappuyant sur une mthodologie bien pense? Faisons-lui subir un petit test maison avant de la livrer Distribon pour quils chargent le code dans leurs distributeurs. Voici le code:

public class TestDistributeur { public static void main(String[] args) { Distributeur distributeur = new Distributeur(5); System.out.println(distributeur); distributeur.insererPiece(); distributeur.tournerPoignee(); System.out.println(distributeur); distributeur.insererPiece(); distributeur.ejecterPiece(); distributeur.tournerPoignee(); System.out.println(distributeur); distributeur.insererPiece(); distributeur.tournerPoignee(); distributeur.insererPiece(); distributeur.tournerPoignee(); distributeur.ejecterPiece(); System.out.println(distributeur); distributeur.insererPiece(); distributeur.insererPiece(); distributeur.tournerPoignee(); distributeur.insererPiece(); distributeur.tournerPoignee(); distributeur.insererPiece(); distributeur.tournerPoignee(); System.out.println(distributeur); } }

Remplir avec cinq bonbons

Afficher ltat de la machine. Insrer une pice... Tourner la poigne; nous devrions recevoir notre bonbon.

Afficher de nouveau ltat de la machine Insrer une pice... Demander la rcuprer.. Tourner la poigne. Nous ne devons pas avoir de bonbon.. Afficher de nouveau ltat de la machine.

Insrer une pice.... Tourner la poigne; nous devrions recevoir notre bonbonl Insrer une pice... Tourner la poigne; nous devrions recevoir notre bonbon Demander la pice que nous navons pas insre.

Afficher de nouveau ltat de la machine. Insrer deux pices... Tourner la poigne; nous devrions recevoir notre bonbon.
Maintenant, le test de charge... Afficher encore une fois ltat de la machine.

392

Chapitre 10

le pattern tat

Fichier Fentre dition Aide distribon.com

%java TestDistributeur Distribon, SARL. Distributeur compatible Java, modle 2004 Stock: 5 bonbons Lappareil attend une pice Vous avez insr une pice Vous avez tourn... Un bonbon va sortir Distribon, SARL. Distributeur compatible Java, modle 2004 Stock: 4 bonbons Lappareil attend une pice Vous avez insr une pice Pice retourne Vous avez tourn mais il ny a pas de pice Distribon, SARL. Distributeur compatible Java, modle 2004 Stock: 4 bonbons Lappareil attend une pice Vous avez insr une pice Vous avez tourn... Un bonbon va sortir Vous avez insr une pice Vous avez tourn... Un bonbon va sortir Vous navez pas insr de pice Distribon, SARL. Distributeur compatible Java, modle 2004 Stock: 2 bonbons Lappareil attend une pice Vous avez insr une pice Vous ne pouvez plus insrer de pices Vous avez tourn... Un bonbon va sortir Vous avez insr une pice Vous avez tourn... Un bonbon va sortir Ae, plus de bonbons ! Vous ne pouvez pas insrer de pice, nous sommes en rupture de stock Vous avez tourn, mais il ny a pas de bonbons Distribon, SARL. Distributeur compatible Java, modle 2004 Stock: 0 bonbons Lappareil est en rupture de stock

vous tes ici

393

ajoutons un jeu

Vous vous y attendiez... une demande de changement!


Distribon, SARL a charg votre code dans leur distributeur le plus rcent, et leurs experts en assurance qualit sont en train de le mettre lpreuve. Jusque l, tout est parfait de leur point de vue. En fait, tout est tellement parfait quils veulent passer la vitesse suprieure...

Nous pensons que transformer lachat de bonbons en jeu dbouchera sur une augmentation significative des ventes. Nous allons placer lun de ces autocollants sur chaque distributeur. Heureusement que nous avons adopt Java. Cela va tre facile, non?

ner! Be a WinTen One in E get a FRE L GUMBAL

ant! n g a g Soyez NBON O B n U IT GRATU sur dix


PDG, Distribon, SARL. Dur ou mou? es de Mystre et boul gomme!
394
Chapitre 10

, quand 10% du temps la le client tourneit deux poigne, il reo dun. bonbons au lieu Bonbons

le pattern tat

Problme de conception
Tracez un diagramme dtats du Distributeur qui gre le concours. Dans ce concours, 10% du temps, ltat Vendu conduit librer deux bonbons au lieu dun. Comparez votre rponse la ntre (en fin de chapitre) pour tre sr que nous sommes daccord avant de poursuivre...

Distribon, SARL.
Le monde dans lequel les distributeurs ne sont jamais vides

Utilisez le papier en-tte de Distribon pour tracer votre diagramme dtats

vous tes ici

395

les choses se compliquent

Nous sommes dans un drle dtat


Ce nest pas parce que vous avez crit votre code en vous appuyant sur une mthodologie bien pense quil est extensible pour autant. En fait, si lon revient en arrire et quon regarde le nombre de modifications ncessaires, eh bien...

final final final final

static static static static

int int int int

EPUISE = 0; SANS_PIECE = 1; A_PIECE = 2; VENDU = 3;

Dabord, il faudrait ajouter ici un Pas impossible...

tat GAGNANT.

public void insererPiece() { // code pour insrer une pice } public void ejecterPiece() { // code pour jecter la pice } public void tournerPoignee() { // code pour tourner la poigne } public void delivrer() { // code pour librer le bonbon }

uctions .. mais alors, il faudrait ajouter de nouvelles instr t lta r gre pour ode mth conditionnelles dans chaque GAGNANT. Cela fait beaucoup de code modifier.

tournerPoignee() va tre particulirement dlicate, car vous allez devoir ajouter du code pour tester si vous avez un GAGNANT, puis passer soit ltat GAGNANT soit ltat VENDU.

vos crayons Sharpen your pencil


Dites si les affirmations suivantes dcrivent ltat de notre implmentation. (Plusieurs rponses possibles.)

A. Ce code nadhre pas au principe


Ouvert-Ferm.

D. Les transitions ne sont pas explicites.


Elles sont enfouies au milieu dun tas dinstructions conditionnelles.

B. Ce code ferait la fiert dun


programmeur FORTRAN.

C. Cette conception nest pas oriente


objet.

E. Nous navons pas encapsul ce qui varie. F. Les ajouts ultrieurs sont susceptibles de
provoquer des bogues dans le code.

396

Chapitre 10

le pattern tat

Bon, cela ne va pas. Je pense que notre premire version tait bonne, mais quelle ne va pas tenir la route si Distribon continue demander de nouveaux comportements. Le taux de bogues va endommager notre image, sans parler du fait que le PDG va nous rendre fous.

Guy : Tout fait daccord ! Il faut remanier ce code pour quil soit facile maintenir et modifier. Line: Il faut vraiment essayer disoler le comportement de chaque tat, de sorte que si nous en modifions un, nous ne risquions pas de mettre la pagaille dans le reste du code. Guy : Bien, autrement dit appliquer le bon vieux principe encapsuler ce qui varie. Line: Exactement. Guy : Si nous plaons le comportement de chaque tat dans sa propre classe, chaque tat nimplmentera que ses propres actions. Line: Tout fait. Et peut-tre que le Distributeur peut simplement dlguer lobjet tat qui reprsente ltat courrant. Guy : Ah, bravo: prfrer la composition... encore un bon principe luvre. Line: Oui. Eh bien, je ne suis pas sre 100% de la faon dont cela va fonctionner, mais je crois quon est sur la bonne piste. Guy : Je me demande si cela facilitera lajout de nouveaux tats. Line: Je pense que oui... Nous devrons toujours modifier le code, mais la porte des changements sera beaucoup plus restreinte, parce que lajout dun nouvel tat se limitera linsertion dune nouvelle classe et peut-tre la modification de quelques transitions ici et l. Guy : Voil qui me plat bien. Attaquons-nous cette nouvelle conception!

vous tes ici

397

une nouvelle conception des tats

La nouvelle conception
On dirait que nous avons un nouveau plan: au lieu de maintenir notre code existant, nous allons le retravailler pour encapsuler les objets tat dans leur propre classe, puis dlguer ltat courant quand une action a lieu. Comme nous appliquons nos principes de conception, nous devrions aboutir une solution plus facile maintenir en aval. Voici comment nous allons procder:
1

Tout dabord, nous allons dfinir une interface Etat qui contiendra une mthode pour chaque action lie au Distributeur. Ensuite, nous allons implmenter une classe pour chaque tat de la machine. Ces classes seront responsables du comportement du distributeur quand il se trouvera dans ltat correspondant. Enfin, nous allons nous dbarrasser de toutes nos instructions conditionnelles et les remplacer par une dlgation la classe tat qui travaillera notre place.

Non seulement nous appliquons les principes de conception, mais, comme vous allez le voir, nous implmentons de fait le pattern tat. Mais nous ne nous occuperons pas de la dfinition officielle du pattern avant davoir retravaill notre code...

Maintenant, nous allons placer tout le comportement de ltat dans une classe. Ainsi, nous isolons le comportement et nous rendons le code plus facile maintenir et comprendre.

398

Chapitre 10

le pattern tat

Dfinir linterface et les classes pour les tats


Crons dabord une interface Etat que tous nos tats implmenteront:
correspondent Voici linterface pour tous les tats. Les mthodes distributeur (ce du s celle tre t raien directement aux actions qui pour t). sont les mmes mthodes que dans le code prcden

Puis nous encapsulons chaque tat de notre conception dans une classe qui implmente linterface Etat.

<<interface>>

Etat
insererPiece() ejecterPiece() tournerPoignee() delivrer()

Pour savoir quels tats il nous faut, nous nous reportons au code prcdent...

EtatVendu
insererPiece() ejecterPiece() tournerPoignee() delivrer()

EtatEpuise
insererPiece() ejecterPiece() tournerPoignee() delivrer()

EtatSansPiece
insererPiece() ejecterPiece() tournerPoignee() delivrer()

EtatAPiece
insererPiece() ejecterPiece() tournerPoignee() delivrer()

public class Distributeur { final final final final static static static static int int int int VENDU = 3; EPUISE = 0; SANS_PIECE = 1; A_PIECE = 2;

... et nous faisons correspondre chaque tat directement une classe.

int etat = EPUISE; int nombre = 0;

Noubliez pas quil va nous falloir aussi un tat e gagnant qui implmente galement linterfac Etat. Nous y reviendrons aprs avoir rimplment la premire version du Distributeur.
EtatGagnant insererPiece() ejecterPiece() tournerPoignee() delivrer()

vous tes ici

399

mais que font tous les tats?

vos crayons Sharpen your pencil

Pour implmenter nos tats, nous devons dabord spcifier le comportement des classes quand chaque action est appele. Annotez le diagramme cidessous en inscrivant le comportement de chaque action dans chaque classe. Nous en avons dj indiqu quelques-uns pour vous.
EtatSansPiece
insererPiece() ejecterPiece() tournerPoignee() delivrer()

Aller EtatAPiece Dire au client quil na pas insr de pice

EtatAPiece
insererPiece() ejecterPiece()

Aller EtatVendu

tournerPoignee() delivrer()

Dire au client de patienter car nous sommes en train de lui donner son bonbon

EtatVendu
insererPiece() ejecterPiece() tournerPoignee() delivrer()

Dlivrer un bonbon. Vrifier le nombre de bonbons. Sil est suprieur zro, aller EtatSansPiece, sinon aller EtatEpuise

EtatEpuise

Dire au client quil ny a plus de bonbons

insererPiece() ejecterPiece() tournerPoignee() delivrer()

EtatGagnant
insererPiece() ejecterPiece() tournerPoignee() delivrer()

Allez-y, et remplissez celui-l, mme si nous ne limplmenterons que plus tard.


400
Chapitre 10

le pattern tat

Implmenter les classes pour les tats


Il est temps dimplmenter un tat. Nous connaissons les comportements ncessaires et il faut maintenant les traduire en code. Nous allons nous calquer troitement sur le code de la machine tats que nous avons dj crit, mais, cette fois, tout est rparti dans des classes diffrentes. Commenons par EtatSansPiece:

implmen Nous devons dabord

ter linterface Etat

public class EtatSansPiece implements Etat { Distributeur distributeur; public EtatSansPiece(Distributeur distributeur) { this.distributeur = distributeur; } public void insererPiece() { System.out.println(Vous avez insr une pice); distributeur.setEtat(distributeur.getEtatAPiece()); }

On transmet une rfrence au Distributeur dans le constructeur et on la place dans une variable dinstance.

Si quelquun insre une pice, nous affichons un message indiquant quelle a t accepte et nous passons dans ltat EtatAPiece.
Vous allez voir dans une seconde comment ceci fonctionne... Vous ne pouvez pas reprendre votre pice: vous ne nous lavez pice); jamais donne!
Et si vous ne payez pas, vous ne pouvez pas avoir de bonbon.

public void ejecterPiece() { System.out.println(Vous navez pas insr de pice); } public void tournerPoignee() { System.out.println(Vous avez tourn, mais il ny a pas de } public void delivrer() { System.out.println(Il faut payer dabord); } }

Impossible de donner gratuitement des bonbons..

Quavons-nous fait? Nous avons implment les comportements qui conviennent ltat dans lequel nous sommes. Dans certains cas, cela implique une transition du Distributeur vers un nouvel tat.

vous tes ici

401

objets tat dans le distributeur

Retravaillons le distributeur
Avant de terminer les classes Etat, nous allons retravailler le Distributeur pour que vous voyiez comment tout cela sarticule. Nous commencerons par les variables dinstance et nous abandonnerons les entiers pour adopter des objets tat:

public class Distributeur { final final final final static static static static int int int int EPUISE = 0; SANS_PIECE = 1; A_PIECE = 2; VENDU = 3;

int etat = EPUISE; int nombre = 0;

in asse Distributeur, af cl la ns iso al tu ac s s de ou e N les classes la plac ilaire dutiliser les nouvelL sim s tr t es code e s. ue iq at st s s des er ti en dans un cas nous avon : s pr n io pt ce ex e un des objets... entiers, dans lautre

Ancien code

public class Distributeur { Etat Etat Etat Etat etatEpuise; etatSansPiece; etatAPiece; etatVendu;

Nouveau code

Etat etat = etatEpuise; int nombre = 0;}

Tous les objets Etat sont crs et affects dans le constructeur.

Cette variable ne tier, contient plus un en . mais un objet Etat

402

Chapitre 10

le pattern tat

Voyons maintenant la classe Distributeur termine...


public class Distributeur { Etat Etat Etat Etat etatEpuise; etatSansPiece; etatAPiece; etatVendu;

Etat etat = etatEpuise; int nombre = 0; public Distributeur(int nombreBonbons) { etatEpuise = new EtatEpuise(this); etatSansPiece = new EtatSansPiece(this); etatAPiece = new EtatAPiece(this); etatVendu = new EtatVendu(this); this.nombre = nombreBonbons; if (nombreBonbons > 0) { etat = etatSansPiece; } } public void insererPiece() { etat.insererPiece(); } public void ejecterPiece() { etat.ejecterPiece(); } public void tournerPoignee() { etat.tournerPoignee(); etat.delivrer(); } void setEtat(Etat etat) { this.etat = etat; }

tats... Voici de nouveau les ...et la variable dinstance etat. La variable dinstance nombre contient le nombre de bonbons initialement, le distributeur est vide. accepte le Notre constructeur initial et le stocke nombre de bonbons stance. dans une variable din s instances des Il cre galement le . tats, une pour chacun

Sil y a plus de0 bonbons, nous positionnons ltat EtatSansPiece.

es intenant, ellr a M s. n io t c Il a s Au tour de FACILES implmente . . sont TRS lguer ltat courant suffit de d

void liberer() { System.out.println(Un bonbon va sortir...); if (nombre != 0) { La machine prend en charge une mthode nombre = nombre - 1; auxiliaire, liberer(), qui libre le bonbon et } dcrmente variable dinstance nombre. } // Autres mthodes, dont une mthode get pour chaque tat... }

Notez que nous navons pas besoin de mthode daction pour delivrer() dans la classe Distributeur parce que ce nest quune action interne: lutilisateur ne peut pas demander directement la machine de dlivrer. Mais nous appelons bien delivrer() sur lobjet tat depuis la mthode tournerPoignee(). Cette mthode permet dautres objets (comme nos objets tat) de dclencher une transition de la machine vers un autre tat.

Par exemple des mthodes comme getEtatSansPiece() pour obtenir chaque objet tat et getNombre() pour obtenir le nombre de bonbons.
vous tes ici

403

dautres tats pour le distributeur

Implmenter dautres tats


Maintenant que vous commencez voir comment le Distributeur et les tats sarticulent, implmentons les classes EtatAPiece et EtatVendu...

public class EtatAPiece implements Etat { Distributeur distributeur; public EtatAPiece(Distributeur distributeur) { this.distributeur = distributeur; }

us instanci, noc st e t a t l d Quan e rfren e un s n o t t e sm n a t lui tr eur. Cela serd la au Distribut n io transit et. a dclencher la t e autr machine vers un Une action pour inapproprie cet tat.
On rend sa pice au client et on retourne EtatSansPiece. Quand la poigne a t tourne, nous dclenchons la transition vers EtatVendu en appelant la mthode setEtat() et en lui transmettant lobjet EtatVendu. Lobjet EtatVendu est extrait par la mthode getEtatVendu() (il y a une de ces mthodes get pour chaque tat).

public void insererPiece() { System.out.println(Vous ne pouvez pas insrer dautre pice); } public void ejecterPiece() { System.out.println(Pice retourne); distributeur.setEtat(distributeur.getEtatSansPiece()); } public void tournerPoignee() { System.out.println(Vous avez tourn...); distributeur.setEtat(distributeur.getEtatVendu()); } public void delivrer() { System.out.println(Pas de bonbon dlivr); } }

Autre action inapproprie at.. pour cet t

404

Chapitre 10

le pattern tat

Voyons maintenant la classe EtatVendu...

public class EtatVendu implements Etat { //constructeur et variables dinstance public void insererPiece() { System.out.println(Veuillez patienter, le bonbon va tomber); } public void ejecterPiece() { System.out.println(Vous avez dj tourn la poigne); } public void tournerPoignee() { System.out.println(Inutile de tourner deux fois!); } public void delivrer() { distributeur.liberer(); if (distributeur.getNombre() > 0) { distributeur.setEtat(distributeur.getEtatSansPiece()); } else { System.out.println(Ae, plus de bonbons!); distributeur.setEtat(distributeur.getEtatEpuise()); } } }

Voici toutes les actions s inapproprie at pour cet t

vrai Et voil o leence... travail comm

du, ans EtatVenn ta e Nous sommesdd li c ire que le ire la ce qui veut e d vons donc pay. Nous dlibrer un bonbon. machine de

est emandons quel Puis nous lui edbonbons, et nous le nombre d EtatSansPiece soit passons soit se. EtatEpui

Musclez vos neurones

Regardez de nouveau limplmentation du Distributeur. Si la poigne a t tourne et que lopration choue (par exemple parce que le client na pas insr de pice), nous appelons quand mme delivrer() alors que cest inutile. Comment corrigeriez-vous cela?

vous tes ici

405

votre tour dimplmenter un tat

vos crayons Sharpen your pencil

Il reste une classe que nous navons pas implmente: EtatEpuise. Pourquoi ne le feriez-vous pas? Pour ce faire, rflchissez soigneusement la faon dont le Distributeur doit se comporter dans chaque situation. Vrifiez votre rponse au lieu de continuer... {

public class EtatEpuise implements Distributeur distributeur; public EtatEpuise(Distributeur distributeur) {

} public void insererPiece() {

} public void ejecterPiece() {

} public void tournerPoignee() {

} public void delivrer() {

} }

406

Chapitre 10

le pattern tat

Quavons-nous fait jusquici?


Pour commencer, nous avons maintenant une implmentation du Distributeur qui diffre beaucoup de la premire version du point de vue structurel, tout en tant fonctionnellement identique. En modifiant la structure de cette implmentation, vous avez:

isol le comportement de chaque tat dans sa propre classe; supprim toutes ces fcheuses instructions conditionnelles qui auraient t difficiles
maintenir;

ferm chaque tat la modification et laiss le Distributeur ouvert lextension en ajoutant


de nouvelles classes tat (nous allons le faire dans une seconde);

cr une base de code et une structure de classes qui correspondent plus troitement au
diagramme de Distribon, et qui sont plus faciles lire et comprendre. Regardons maintenant dun peu plus prs laspect fonctionnel:

aintenant une m t n ie t n o c ur Le Distributcehaque classe tat. instance de

t ats du Dist ri bu te ur

San

sPiece

tat courant

Di st r

ibuteur

APiece

Ltat courant de la machine est toujours lune de ces instances de classe.

Vendu

Epuise

vous tes ici

407

transitions entre tats

Quand une action est appele, elle est dlgue ltat courant.
tournerPoignee()

tats du dis tri buteur

S an

sPiece

tournerPoignee()

tat courant

Di st r

ibuteur

APiece

En loccurrence, la mthode tournerPoignee() est appele quand la machine est dans ltat APiece. En consquence, la machine effectue une transition vers ltat Vendu.

Vendu

Epuise

TRANSITION VERS LTAT VENDU

ntre La machine e endu et V t dans lta st dlivr... le bonbon e


delivrer()

tats du dis tri buteur

encore des bonbons


...puis la machine passera ltat Epuise ou ltat SansPiece selon le nombre de bonbons restant dans la machine.

Sa

nsPiece

Di st r

ta

t co

ura

ibuteur

nt

A Pie c e

Vendu

rupture de stock
Epuise

408

Chapitre 10

le pattern tat

vos crayons

Sharpen your pencil

Dans les coulisses: Visite libre

Tracez les tapes du Distributeur en commenant par ltat SansPiece. Annotez galement le diagramme en indiquant les actions et les rsultats. Pour cet exercice, vous pouvez supposer quil y a plein de bonbons dans le distributeur. 1
tats du dist ribu teu r

2
tats du dist ribu teu r

Sa nsPiece

Sa nsPiece

Di st r

AP

ibuteur

iece

Di st r

AP

ibuteur

iece

Vendu

Vendu

Epuise

Epuise

tats du dist ribu teu r

tats du dist ribu teu r

Sa nsPiece

Sa nsPiece

Di st r

AP

ibuteur

iece

Di st r

AP

ibuteur

iece

Vendu

Vendu

Epuise

Epuise

vous tes ici

409

dfinition du pattern tat

Le pattern tat: dfinition


Oui, cest la vrit: nous venons dimplmenter le pattern tat! Voyons maintenant de quoi il sagit:
Le pattern tat permet un objet de modifier son comportement quand son tat interne change. Tout se passera comme si lobjet changeait de classe.

La premire partie de la description est limpide, non? Puisque le pattern encapsule ltat dans des classes spares et dlgue lobjet reprsentant ltat courant, nous savons que le comportement change avec ltat interne. Le Distributeur en est un bon exemple: quand la machine est dans lEtatSansPiece et que vous insrez une pice, vous obtenez un comportement diffrent (elle accepte la pice) que si elle avait t dans lEtatAPiece (la machine rejette la pice). Mais quen est-il de la seconde partie de la dfinition? Que veut dire tout se passera comme si lobjet changeait de classe? Reprsentez-vous le point de vue dun client: si un objet que vous utilisez peut changer compltement de comportement, il vous semble alors instanci partir dune autre classe. Mais, en ralit, vous savez que nous utilisons une composition pour donner lapparence dun changement de classe en rfrenant simplement des objets tat diffrents. Bien. Il est maintenant temps dtudier le diagramme de classes du pattern tat:

Le Contexte est la classe qui peut avoir plusieurs tats internes. Dans notre exemple, le Contexte correspond au Distributeur..
Contexte
requte()

Linterface Etat dfinit une interface commune pour tous les tats concrets. Si les tats implmentent tous la mme interface, ils sont interchangeables.
Etat
grer()

etat.gerer()

EtatConcretA
grer()

EtatConcretB
grer()

Chaque fois que la requt effectue sur le Contex e() est gestion est dlgue lte, sa tat.

De nombreux t concrets sont poats ssibles.

Chaque EtatConcret gre les requtes du Contexte et fournit sa propre implmentation de la requte. De cette at, manire, quand le Contexte change dt son comportement change galement.

410

Chapitre 10

le pattern tat Une petite seconde! Si jai bonne mmoire, le diagramme de classes du pattern Stratgie, est EXACTEMENT le mme.

Vous tes trs observateur! Oui, les diagrammes de classes sont globalement identiques, mais lintention des deux patterns est diffrente. Avec le pattern tat, nous sommes en prsence dun ensemble de comportements encapsuls dans des objets tat. tout moment, le contexte dlgue lun de ces tats. Au fil du temps, ltat courant change pour lun de ces objets tat afin de reflter ltat interne du contexte, si bien que le comportement du contexte change galement. Le client ne sait gnralement pas grand-chose, voire rien, des objets tat. Avec Stratgie, le client spcifie gnralement lobjet stratgie avec lequel le contexte est compos. Maintenant, si le pattern permet de changer lobjet stratgie au moment de lexcution, il y a souvent un objet stratgie qui est particulirement adapt un objet contexte. Par exemple, au chapitre1, certains de nos canards taient configurs pour voler avec un comportement de vol classique (comme les colverts), tandis que dautres taient configurs avec un comportement de vol qui les clouait au sol (comme les canards en plastique et les leurres). En gnral, vous pouvez voir le pattern Stratgie comme une solution de rechange souple au sous-classement. Si vous utilisez lhritage pour dfinir le comportement dune classe, vous tes coinc si vous avez besoin de le modifier. Avec Stratgie, vous pouvez changer le comportement en composant avec un objet diffrent. Reprsentez-vous le pattern tat comme une solution qui permet dviter de placer une foule dinstructions conditionnelles dans votre contexte. En encapsulant les comportements dans des objets tat, il suffit de modifier ces derniers dans le contexte pour changer son comportement.

vous tes ici

411

questions et rponses sur le pattern tat

questions stupides

il ny a pas de

Q: R:

Dans le Distributeur, ce sont les tats qui dcident de ltat dans lequel ils doivent tre ensuite. Est-ce que cest toujours un EtatConcret qui dcident des transitions?

Pour partager vos tats, vous affecterez gnralement chaque tat une variable dinstance statique. Si un tat a besoin dutiliser des mthodes ou des variables de votre Contexte, vous devez galement lui fournir une rfrence au contexte dans chaque mthode gerer().

Non, pas toujours. Lautre solution consiste laisser le Contexte dcider du flux des transitions. En rgle gnrale, lorsque les transitions sont fixes, il est judicieux de les placer dans le Contexte. En revanche, si les transitions sont plus dynamiques, on les place classiquement dans les classes tat elles-mmes. Par exemple, dans le Distributeur, le choix de la transition vers SansPiece ou vers Epuise dpend du nombre de bonbons au moment de lexcution. Le fait de placer des transitions dans les classes tat prsente linconvnient de crer des dpendances entre ces classes. Dans notre implmentation du Distributeur, nous avons tent de le rduire en utilisant des mthodes get sur le Contexte au lieu de coder en dur des classes tat concrtes explicites. Remarquez que, en prenant cette dcision, vous dcidez galement quelles sont les classes fermes la modification le Contexte ou les classes tat mesure que le systme volue.

Q: R:

On dirait que lemploi du pattern tat augmente toujours le nombre de classes dans nos conceptions. Regardez combien notre Distributeur a de classes de plus que la version dorigine!

Vous avez raison. En encapsulant les comportements dans des classes tat distinctes, on finit toujours par avoir plus de classes. Cest souvent le prix payer pour conserver de la souplesse. moins que votre code ne soit une implmentation provisoire que vous jetterez ensuite, envisagez de le construire avec des classes supplmentaires et vous vous en fliciterez probablement en aval. Notez que le point important est souvent le nombre de classes que vous exposez vos clients, et quil existe des moyens pour leur cacher ces classes supplmentaires (par exemple en dclarant une visibilit package). Mais rflchissez. Si votre application a beaucoup dtats et que vous dcidez de ne pas utiliser des objets spars, vous allez vous retrouver avec dnormes instructions conditionnelles monolithiques, et votre code sera plus difficile maintenir et comprendre. En utilisant des objets, vous rendez les tats explicites et votre code est plus lisible et plus facile maintenir.

Q: R:

Est-ce quil arrive que les clients interagissent directement avec les tats?

Non. Comme le Contexte utilise les tats pour reprsenter son tat interne et son comportement, toutes les requtes aux tats proviennent du Contexte. Les clients ne modifient pas directement ltat du Contexte. Il appartient au Contexte de surveiller son tat, et lon ne veut gnralement pas quun client modifie ltat dun Contexte sans que celui-ci ne le sache.

Q: R:

Le diagramme de classes du pattern tat reprsente ltat comme une classe abstraite. Mais vous avez utilis une interface dans limplmentation de ltat du distributeur

Q: R:

Si jai beaucoup dinstances du Contexte dans mon application, est-il possible de partager les objets tat entre eux?

412

Oui, absolument. En fait, cest un scnario trs courant. La seule exigence est que vos objets tat ne mmorisent pas leur propre tat interne. Sinon, il vous faut une instance unique par contexte. Chapitre 10

Oui. tant donn que nous navions aucune fonctionnalit commune placer dans une classe abstraite, nous avons choisi une interface. Dans votre propre implmentation, vous prfrerez peut-tre une classe abstraite. Cette technique a lavantage de vous permettre dajouter des mthodes la classe abstraite plus tard, sans toucher aux implmentations des tats concrets.

le pattern tat

Terminons le jeu
Souvenez-vous, nous navons pas encore fini. Il nous reste le jeu programmer. Mais, maintenant que nous avons implment le pattern tat, nous allons le faire en un clin dil. Tout dabord, nous devons ajouter un tat la classe Distributeur:
public class Distributeur { Etat Etat Etat Etat Etat etatEpuise; etatSansPiece; etatAPiece; etatVendu; etatGagnant;

Il suffit dajouter ici le nouvel EtatGagnant et de linitialiser dans le constructeur Noubliez pas quil faudra galement ajouter ici une mthode getEtatGagnant.

Etat etat = etatEpuise; int nombre = 0; // mthodes }

Implmentons maintenant la classe EtatGagnant elle-mme. Elle est remarquablement similaire la classe EtatVendu:
public class EtatGagnant implements Etat { // variables dinstance et constructeur // message derreur insererPiece // message derreur ejecterPiece // message derreur tournerPoignee

me da Exactement com

ns EtatVendu.

Ici, nous en librons deux et nous passons . uise EtatSansPiece ou EtatEp

public void delivrer() { System.out.println(VOUS AVEZ GAGN! Deux bonbons pour le prix dun!); distributeur.liberer(); if (distributeur.getNombre() == 0) { distributeur.setEtat(distributeur.getEtatEpuise()); } else { distributeur.liberer(); Tant quil reste if (distributeur.getNombre() > 0) { un second bonbon, distributeur.setEtat(distributeur.getEtatSansPiece()); nous le librons. } else { System.out.println(Ae, plus de bonbons!); distributeur.setEtat(distributeur.getEtatEpuise()); } } } } vous tes ici

413

implmenter le jeu

Terminons le jeu
Il reste une dernire modification apporter: nous devons implmenter le jeu de hasard et ajouter une transition vers EtatGagnant. Nous allons ajouter les deux EtatAPiece, puisque cest l o le client tourne la poigne:

public class EtatAPiece implements Etat { Random hasard = new Random(System.currentTimeMillis()); Distributeur distributeur; public EtatAPiece(Distributeur distributeur) { this.distributeur = distributeur; } public void insererPiece() { System.out.println(Vous ne pouvez pas insrer dautre pice); } public void ejecterPiece() { System.out.println(Pice retourne); distributeur.setEtat(distributeur.getEtatSansPiece()); } public void tournerPoignee() { System.out.println(Vous avez tourn...); int gagnant = hasard.nextInt(10); if ((gagnant == 0) && (distributeur.getNombre() > 1)) { distributeur.setEtat(distributeur.getEtatGagnant()); } else { distributeur.setEtat(distributeur.getEtatVendu()); } } public void delivrer() { System.out.println(Pas de bonbon dlivr); } }

d Nous ajoutons dabor de un gnrateur nombres alatoires x pour gnrer les dice pour cent de chan de gagner...

...puis nous dterminons si le client a gagn.

Sil a gagn et quil reste assez de bonbons pour quil en ait deux, nous passons EtatGagnant. Sinon, nous passons EtatVendu (comme nous lavons toujours fait).

Eh bien, il ny avait pas plus simple crire! Nous avons simplement ajout un nouvel tat au Distributeur puis nous lavons implment. partir de l, il nous a suffi dimplmenter le jeu de hasard et la transition vers ltat correct. On dirait que notre nouvelle stratgie de codage porte ses fruits...

414

Chapitre 10

le pattern tat

Dmonstration au PDG de Distribon, SARL


Le PDG de Distribon est pass voir une dmo de notre nouveau code. Esprons que ces tats sont tous en ordre! La dmonstrations sera courte (la faible capacit dattention du PDG est bien connue), mais suffisamment longue pour quon puisse esprer gagner au moins une fois.

vraiment chang. Non, le code na pas ent raccourci un peu.. Nous lavons simplem

public class TestDistributeur { public static void main(String[] args) { Distributeur distributeur = new Distributeur(5); System.out.println(distributeur); distributeur.insererPiece(); distributeur.tournerPoignee(); System.out.println(distributeur); distributeur.insererPiece(); distributeur.tournerPoignee(); distributeur.insererPiece(); distributeur.tournerPoignee(); System.out.println(distributeur); } }

Cette fois encore, nous commenons avec cinq bonbons.

Comme nous voulons un tat gagnant, nous narrtons pas dinvestir des pices et de tourner la poigne. De temps autre, nous affichons ltat du distributeur....

ment au grand Lquipe de dveloppe porte de la salle complet derrire late ndant de voir de confrences, at ti on base sur le si la nouvelle concep nc tionner!! pattern tat va fo

vous tes ici

415

tester le distributeur

Ouiii! a swingue!

Fichier Fentre dition Aide AnnieAime LesSucettes

%java TestDistributeur Distribon, SARL. Distributeur compatible Java, modle 2004 Stock: 5 bonbons Lappareil attend une pice Vous avez Vous avez VOUS AVEZ Un bonbon Un bonbon insr une pice tourn... GAGN! Deux bonbons pour le prix dun! va sortir... va sortir...

Distribon, SARL. Distributeur compatible Java, modle 2004 Stock: 3 bonbons Lappareil attend une pice Vous avez Vous avez Un bonbon Vous avez Vous avez VOUS AVEZ Un bonbon Un bonbon Ae, plus insr une pice tourn... va sortir... insr une pice tourn... GAGN! Deux bonbons pour le prix dun! va sortir... va sortir... de bonbons!

de la chance a alors!, On a eu e dmo au ou quoi? Dans notr s gagn une pa PDG, nous navons fois mais deux!

Distribon, SARL. Distributeur compatible Java, modle 2004 Stock: 0 bonbons Lappareil est en rupture de stock %

questions stupides
Excellente question. EtatVendu et EtatGagnant sont presque identiques, sauf quEtatGagnant dlivre deux bonbons au lieu dun. Vous pourriez sans doute placer le code qui dlivre les deux bonbons dans EtatVendu. Bien sr, il y a un inconvnient: vous avez maintenant DEUX tats reprsents dans une seule classe: ltat o vous tes gagnant et celui o vous ne ltes pas. Vous sacrifiez donc la clart pour rduire la duplication de code. Autre facteur considrer, le principe que vous avez appris au chapitre prcdent: une classe, une responsabilit. En plaant la responsabilit dEtatGagnant dans EtatVendu, vous avez attribu EtatVendu DEUX responsabilits. Que se passe-t-il quand la promotion se termine? Ou quand les enjeux du concours changent? Cest donc une situation de compromis qui entrane une dcision de conception.

Il ny a pas de

Q: R:

Pourquoi faut-il un EtatGagnant? Est-ce quEtatVendu ne pourrait pas juste dlivrer deux bonbons?

416

Chapitre 10

le pattern tat Bravo! Beau travail, lquipe. Nos ventes sont dj en train de crever le plafond avec ce nouveau jeu. Vous savez, nous faisons aussi des distributeurs de boissons, et je me disais quon pourrait leur ajouter des leviers de machines sous et en faire aussi un jeu. Nous avons des gosses de quatre ans qui parient avec les distributeurs de bonbons. Pourquoi nous arrter en si bon chemin?

Bilan de sant...
Oui, le PDG de Distribon a probablement besoin dun bilan de sant, mentale du moins, mais l nest pas notre propos. Rflchissons aux aspects du Distributeur que nous aimerions consolider avant de livrer la version de luxe:

Nous avons beaucoup de code dupliqu dans les tats Vendu et


Gagnant et nous gagnerions y faire un peu de mnage. Comment procder? Nous pourrions transformer Etat en classe abstraite et lui intgrer un comportement par dfaut pour les mthodes. Aprs tout, des messages derreur comme Vous avez dj insr une pice ne seront pas vus par le client. En consquence, tout le comportement li la rponse aux erreurs pourrait tre gnrique et hrit de la classe abstraite Etat.

is un Menfin, Paulo, je su ons, nb bo distributeur de ! ur pas un ordinate

La mthode delivrer() est toujours appele, mme si la poigne


est tourne quand il ny a pas de pice. Tant que la machine fonctionne correctement et ne dlivre rien moins dtre dans le bon tat, ce serait facile corriger en codant tournerPoignee() pour quelle retourne un boolen ou en introduisant des exceptions. Selon vous, quelle est la meilleure solution?

Toute la logique des transitions est dans les classes tat. Quels
sont les problmes qui pourraient en rsulter? Allons-nous la transfrer dans le Distributeur? Quels seraient les avantages et les inconvnients de cette approche?

Allez-vous instancier beaucoup dobjets Distributeur? Si oui, vous


voudrez peut-tre dplacer les instances des tats dans des variables dinstance statiques et les partager. En ce cas, quelles seraient les modifications ncessaires dans le Distributeur et dans les tats?

vous tes ici

417

face face : tat et Stratgie

Face face :

Ce soir : les patterns Stratgie et tat enfin runis.

Stratgie
H, frangin. Tu as su que jtais dans le chapitre1? Jtais parti donner un coup de main aux types du Patron de mthode: ils avaient besoin de moi pour finir leur chapitre. Alors, quoi soccupe mon noble frre ces temps derniers?

tat
Oui, cest ce que dit la rumeur publique.

Comme dhabitude: jaide les classes prsenter des comportements diffrents dans diffrents tats. Je ne sais pas, mais on dirait toujours que tu viens de pomper ce que je fais et que tu emploies dautres mots pour le dcrire. Rflchis: je permets aux objets dincorporer diffrents comportement ou algorithmes grce la composition ou la dlgation. Tu ne fais que me copier.

Ah oui? Comment cela? Je ne vois pas.

Jadmets que nous avons des pratiques apparentes, mais mon intention est totalement diffrente de la tienne. Et la faon dont jenseigne mes clients utiliser la composition et la dlgation nest absolument pas la mme. Eh bien, si tu passais un peu plus de temps penser autre chose qu toi, tu le saurais. Rflchis un peu ta faon de fonctionner: tu instancies une classe et tu lui donnes gnralement un objet stratgie qui implmente un comportement quelconque. Comme au chapitre1 o tu distribuais aux canards des comportements pour cancaner, daccord? Les vrais canards faisaient coin-coin pour de bon et les canards en plastique couinaient.

Oui, ctait du bon travail... et je suis sr que tu vois que cest beaucoup plus puissant que dhriter de ton comportement, non?

Dsol, il va falloir que tu texpliques. 418


Chapitre 10

Oui, naturellement. Maintenant, rflchis ma faon de fonctionner: elle est compltement diffrente.

le pattern tat

Stratgie

tat
Bien. Quand mes objets Contexte sont crs, je peux leur indiquer leur tat initial, mais ils peuvent modifier leur propre tat ultrieurement.

Oh, arrte! Moi aussi, je peux changer de comportement lors de lexcution! Cest tout lintrt de la composition!

Bien sr, mais mon fonctionnement est construit autour dtats discrets. Mes objets Contexte changent dtat au fil du temps selon des transitions bien dfinies. Autrement dit, le changement dtat est intgr mon schma. Voil comment je fonctionne!

Bon, je te laccorde, je nencourage pas mes objets avoir des ensembles de transitions bien dfinis entre les tats. En fait, je prfre gnralement contrler la stratgie quils appliquent. coute, nous avons dj dit que notre structure tait semblable mais que notre intention tait diffrente. Admets-le: il y a assez de place pour nous deux. Oui, oui, je te laisse tes fantasmes. Tu te comportes comme si tu tais un grand pattern comme moi, mais, regarde bien: je suis dans le chapitre1 alors quils tont relgu au chapitre10. Tu peux me dire combien de lecteurs vont vraiment aller jusque l?

Tu plaisantes? Cest un ouvrage de la collection Tte la premire, et les lecteurs de Tte la premire assurent. Bien sr quils vont arriver au chapitre10!

Cest mon frre! Le rveur de la famille.

vous tes ici

419

exercice de remplissage

Nous avons failli oublier!

Distribon, SARL

Le monde dans lequel les distributeurs ne sont jamais vides

e Plus d s bonbon

ns les ntionner une transition da Nous avons oubli de me... remplir la de yen mo un ut fa il nous spcifications dorigine vid mme. Pouvezgra e! Voici le nouveau dia si bon travail machine quand elle est nou du it s? Vous avez fa ajouter z vous limplmenter pour eur rre pou s vou que doute nul : but tri dis du te res le sur cette fonction en un clin dil! - Les ingnieurs de Distribon remplir

ice

A unee pic
pice

tou

rne

insrer p

rp

oig

ne

Pas dee pic


bon bon s>

jecter
r dlivren o b bon

bonbo

n Bonbo u vend

ns =

420

Chapitre 10

le pattern tat

vos crayons Sharpen your pencil


Nous avons besoin de vous pour crire la mthode remplir(). Elle na quun seul argument le nombre de bonbons que nous allons ajouter dans le distributeur et elle doit mettre jour le compteur de bonbons et rinitialiser ltat de la machine.

Vous avez fait un travail stupfiant! Jai encore quelques ides qui vont rvolutionner lindustrie des distributeurs et je veux que vous les implmentiez. Chut! Je vous en parlerai dans le prochain chapitre.

vous tes ici

421

qui fait quoi?

Qui fait quoi ?

Faites correspondre chaque sa description:

Pattern tat

Description Encapsule des comportements interchangeables et utilise la dlgation pour dcider lequel utiliser Les sous-classes dcident de la faon dimplmenter les tapes dun algorithme Encapsule des comportements bass sur des tats et dlgue le comportement ltat courant

Stratgie

Patron de mthode

422

Chapitre 10

le pattern tat

Votre bote outils de concepteur


Cest la fin dun autre chapitre: vous avez suffisamment de patterns pour passer nimporte quel entretien dembauche les doigts dans le nez!

POINTS DIMPACT

Abstraction n Encapsulatio ce qui varie. Encapsulez e. me lhritag Polymorphis ncapsulation Prfrez le non des interfaces, Hritagee grammez des
Pro ions. implmentat ler vous de coup ui q Efforcezs t je les ob faiblement nt. interagisse re ouvertes doivent tm s se as s la cl es L mais fer e lextension n. modificatio ctions. Ne tes. des abstra s concr Dpendez pa as s des cl se ez d en p d . u vos amis Ne parlez q us vous pelez pas, no Ne nous aps. appelleron e r quune seul ne doit avoi Une classe ch anger. raison de

Principes OO

Bases de lOO

Le pattern tat permet un objet


davoir plusieurs comportements diffrents selon son tat interne.

la diffrence dune machine


tats procdurale, le pattern tat reprsente les tats comme des classes part entire.

Le Contexte obtient son


comportement en dlguant lobjet reprsentant ltat courant avec lequel il est compos.

En encapsulant chaque tat dans


ipe veau princ Pas de nou apitre pour dans ce chr le temps de vous laisse r. les assimile
Voici notre nouveau pattern. Si vous devez grer un tat dans une classe, le pattern tat vous fournit une technique pour lencapsuler.
une classe, nous isolons tous les changements qui seront ncessaires.

Les diagrammes dtat des patterns


tat et Stratgie sont identiques mais leur intention diffre.

Le pattern Stratgie configure


gnralement les classes Contexte avec un comportement ou un algorithme.

Le pattern tat permet un Contexte


de modifier son comportement mesure que son tat change.

O Patterns O lation ille une ree fa it ef inm init-und esurs,

f e ce dsi de n uc unr pulu farcf c -e re an ee h t ie hu tn ct a a v in tg t lep r ab e e n ae s a g psu s a Str y c a it t s. O e h ir e in t c le e a r b in d t s, u f t a e e n e e s D je g n m e t b n u h a a o m je it h r b n it r l n c o ne o up ind ru io eu ca e e lg p t eut f . is a tr in a s D dae t nD e sq ic m n d s r r , e n iq lo t e n b e d r m n , n it m je io e e e a s h b il F u e na p t o n le b it is q a y r a a n d t o n s la d m u e lg r , o e an n e b ap l ux rc ia o nd iuix a ue je fs ud s ess io u u b q ded qt F tx jo ere ot t q sit f eic n u r m u nn e c e o r is e t d ca h eb d li m c la p s n c a tmme u r s t t r io s le ie o a u n e e g t d o s j d g ie poeinco p e a , t n r t t ix so ss a n o c n o a n u io la e h qu t t o c n t c la e m Str it re t uie n m ss us et t r lu d le a le un o iso ur oe d seca un g rm o psa e n if ee e f in p ss t e x ul u S o t n p la n ps a n c u d io t t e in t t la se c t en n a u r n m e e ic a r ie so t c r is m e r n so b st e in p e x r lat a a abricle l ge .erface va enIl p u u in ea d n . a st n t io it .F le in a t n t riq u t t n e ra a r e m ie a se m t sa c t n r ve e m n t is m n . io p u e a n o s o la t c ra c iq u n C st e ia F t e pa c in q iv . is l e n le r il a t m a r i fac b ie e oce s es in d st c ns ancia n u tlt lo ea n lut e n nr e g t a ss m et aua l tinai e rr in lar t st a p oile an y e eo f t is u ncio n p r or g eta u s e ys t st d l itrqu o d eut au nre d in p ss f ,t r l A s t e , u c la u n r o d c c je es a e f a io e u ob t e end d t ss n un n g un u a o c la l c a e m n iv d e f e dediffrenm r u m dt ner e r neune n tes fd ass up e d t .. d e s a it in la n ss r c F s r u t la e u e e e c li o t n a a r u le t s. et d pa b p e n , bejets e a s ss r o es io d n se nt uat la c sje t nL ces Aifs de iesse e cstsous-d e.e qu clla d t b l st it o eu a re n ll r se t r s. n fon u o n e e u o e p u i li f q m It c o de if s e n u r c d u d n a sul lo e u t a e e u r a pi ll te et r qer, ns .e een le ca s ct as r io dou n snp of des so dat po e e b at .o O t ait e m r r nins O llL er sa om e op u c r d eq st c e es e er cC ra usc l d t d sy o a v e it s b at n d t sr e a r P iv / so u ili a f n s ss ib n so s u la t iernt e rs c d sa u t r if a ve u s a e t h p e t od r a t c d rmettan s fair un mm osa u la g r rlu p d..eco t g s st le a e t ie s d r u je e h e ge. e p p c e d ob e is y an r c s t il a t a a ch n t r f p e e u pe r i c d ne , e t h n ja t us er et s e son t pl nau in ils. sint rm d c u raie epe ae pe rt so at e angeait de f le e d n t s ib n s io lu e at t a t p t n p s a e e r t d m li n o c m an c r x s qu in st tmet au si lobjet ch ts p sy ea ces comport sur em f er te pen dinso . Il les obje pos a comme sser com pala ouetr se mme faon ons de ceux-ci Tit e d traclasse. t les combinais individuels e

Les transitions entre tats peuvent


tre contrles par les classes tat ou par les classes Contexte.

Lapplication du pattern tat


entranera gnralement une augmentation du nombre des classes dans votre conception.

Les classes tat peuvent tre


partages entre les instances du Contexte.

vous tes ici

423

solutions des exercices

Solutions des exercices

Distribon, SARL
Le monde dans lequel les distributeurs ne sont jamais vides

d deu livre bon x r bon s

Gagnant
tur have ns cran a wi k, we nner !

gumballs = 0

Plus de bonbons

gumba

lls >

pice

A une pice

poi

gn

et

our

insrer

ne

pice

,p

as

de

Pas de pice
bon

jecter

gag

nan t

bonbo n

bon

s=0

s>

r dlivren o b bon

n Bonbo u vend

424

Chapitre 10

le pattern tat

Solutions des exercices


vos crayons Sharpen your pencil
Dites si les affirmations suivantes dcrivent ltat de notre implmentation. (Plusieurs rponses possibles)

A. Ce code nadhre pas au principe


Ouvert-Ferm.

D. Les transitions ne sont pas explicites.


Elles sont enfouies au milieu dun tas dinstructions conditionnelles.

B. Ce code ferait la fiert dun


programmeur FORTRAN.

E. Nous navons pas encapsul ce qui varie. F. Les ajouts ultrieurs sont susceptibles de
provoquer des bogues dans le code.

C. Cette conception nest pas oriente


objet.

vos crayons Sharpen your pencil

Il reste une classe que nous navons pas implmente: EtatEpuise. Pourquoi ne le feriez-vous pas? Pour ce faire, rflchissez soigneusement la faon dont le Distributeur doit se comporter dans chaque situation. Vrifiez votre rponse avant de continuer...

public class EtatEpuise implements Etat { Distributeur distributeur; public EtatEpuise(Distributeur distributeur) { this.distributeur = distributeur; }

is, nous ne Dans ltat Epu ent rien faire pouvons absolumpas rempli le a n tant quon Distributeur..

public void insererPiece() { System.out.println(Vous ne pouvez pas insrer de pices, nous sommes en rupture de stock); } public void ejecterPiece() { System.out.println(jection impossible, vous navez pas insr de pice); } public void tournerPoignee() { System.out.println(Vous avez tourn, mais il ny a pas de bonbons); } public void delivrer() { System.out.println(Pas de bonbon dlivr); } } public String toString() { return sold out; } }

vous tes ici

425

solutions des exercices

vos crayons Sharpen your pencil

Pour implmenter nos tats, nous devons dabord spcifier le comportement des classes quand chaque action est appele. Annotez le diagramme cidessous en inscrivant le comportement de chaque action dans chaque classe. Nous en avons dj indiqu quelques-uns pour vous..

Aller EtatAPiece Dire au client vous navez pas insr de pices

EtatSansPiece
insererPiece() ejecterPiece() tournerPoignee() delivrer()

Dire au client vous avez tourn mais il ny a pas de pice Dire au client il faut payer dabord Dire au client vous ne pouvez pas insrer dautre pice Rendre la pice, aller EtatSansPiece Aller EtatVendu Dire au client pas de bonbon dlivr Dire au client attendez, le bonbon va sortir

EtatAPiece
insererPiece() ejecterPiece() tournerPoignee() delivrer()

Dire au client vous avez dj tourn la poigne

EtatVendu
insererPiece() ejecterPiece() tournerPoignee() delivrer()

Dire au client inutile de tourner deux fois Dlivrer un bonbon. Vrifier le nombre de bonbons. Sil est suprieur zro, aller EtatSansPiece, sinon aller EtatEpuise Dire au client vous navez pas insr de pice Dire au client il ny a plus de bonbons Dire au client pas de bonbon dlivr Dire au client pas de bonbon dlivr Dire au client attendez, le bonbon va sortir Dire au client vous avez dj tourn la poigne Dlivrer deux bonbons. Vrifier le nombre de bonbons restants. Sil est suprieur 0, aller EtatSansPiece, sinon aller EtatEpuise
426
Chapitre 10

Dire au client nous sommes en rupture de stock


EtatEpuise
insererPiece() ejecterPiece() tournerPoignee() delivrer()

EtatGagnant
insererPiece() ejecterPiece() tournerPoignee() delivrer()

le pattern tat

Dans les coulisses: visite libre


Dlgue ltat courant
1 2
insererPiece())
tats du dist ribu teu r

dlgue
tournerPoignee()

tats du dist ribu teu r

insererPiece()

ura at co

nt

Sa nsPiece

Sa nsPiece

tournerPoignee()
AP

tat courant
AP

Di st r

ibuteur

iece

Di st r

ibuteur

iece

action de la machine

Vendu

action de la machine

Vendu

Epuise

Epuise

action de la machine transition vers ltat APiece

transition vers ltat Vendu


tats du dist ribu teu r

tats du dist ribu teu r

dispense()

Sa nsPiece

tat

an our

Sa nsPiece

Di st r

ta

t co

ibuteur

ura

nt

AP

iece

Di st r

AP

ibuteur

iece

La machine donne un bonbon en appelant laction interne delivrer().

Vendu

Vendu

Epuise

puis passe ltat SansPiece

Epuise

vous tes ici

427

solutions des exercices

Qui fait quoi ?


g

Faites correspondre chaque pattern sa description:

Pattern tat

Description Encapsule des comportements interchangeables et utilise la dlgation pour dcider lequel utiliser Les sous-classes dcident de la faon dimplmenter les tapes dun algorithme
Encapsule des comportements bass sur des tats et dlgue le comportement ltat courant

Stratgie

Patron de mthode

vos crayons Sharpen your pencil


Nous avons besoin de vous pour crire la mthode remplir(). Elle na quun seul argument, le nombre de bonbons que nous allons ajouter dans le distributeur, et elle doit mettre jour le compteur de bonbons et rinitialiser ltat de la machine.

void remplir(int nombre) { this.nombre = nombre; etat = etatSansPiece; }

428

Chapitre 10

11 le pattern Proxy

Contrler laccs g aux objets


Avec toi comme Proxy, je vais pouvoir racketter trois fois plus dargent mes copains lheure de la cantine !

h
g

Avez-vous dj jou gentil flic, mchant flic ? Vous tes le gentil policier et
vous rendez volontiers service de bonne grce, mais comme vous ne voulez pas que qui que ce soit vous sollicite, vous demandez au mchant de servir dintermdiaire. Cest l le rle dun proxy : contrler et grer les accs. Comme vous allez le voir, un proxy peut se substituer un objet de bien des manires. Les proxies sont connus pour transporter des appels de mthodes entiers sur lInternet la place dautres objets. On les connat galement pour remplacer patiemment un certain nombre dobjets bien paresseux.

nouveau chapitre

429

quel est lobjectif

Bonjour tout le monde. Jaimerais vraiment bien disposer dun meilleur contrle sur les distributeurs de bonbons. Pouvezvous trouver un moyen de mafficher des rapports sur ltat du stock et sur celui des appareils ?

Cela semble assez facile. Si vous vous en souvenez, le code du Distributeur contient dj des mthodes qui permettent de connatre le nombre de bonbons (getNombre()) et ltat courant de la machine (getEtat()). Il nous suffit donc de crer un rapport que nous pourrons afficher et transmettre au PDG. Mmmm... nous devrons galement sans doute ajouter un champ emplacement chaque distributeur. Ainsi, le PDG pourra entretenir ses machines.

Vous souvenez-vous du P.D.G. de Distribon, SARL ?

Allons-y, il ny a plus qu coder. Lextrme brivet de nos dlais va impressionner le PDG.

430

Chapitre 11

le pattern proxy

Coder le contrleur
Commenons par ajouter la classe Distributeur du code qui lui permettra de grer les emplacements :
public class Distributeur { // autres variables dinstance String emplacement;

Un emplacement nest autre quune chane de caractres.

public Distributeur(String emplacement, int nombre) { // reste du constructeur Lemplacement est transmis this.emplacement = emplacement; } constructeur et stock public String getEmplacement() { return emplacement; } // autres mthodes }

dinstance.

dans le dans la variable

pour Ajoutons galement une mthode get ns avo en s nou trouver lemplacement quand besoin.

Crons maintenant une autre classe, ControleurDistrib, qui extrait lemplacement de la machine, le nombre de bonbons quelle a en stock et son tat courant, puis qui affiche un petit rapport bien propre :

public class ControleurDistrib { Distributeur machine; public ControleurDistrib(Distributeur machine) { this.machine = machine; }

Le contrleur accepte la machine dans son constructeur et laffecte la variable dinstance machine.

public void rapport() { System.out.println(Distributeur : + machine.getEmplacement()); System.out.println(Stock Courant : + machine.getNombre() + bonbons); System.out.println(tat courant : + machine.getEtat()); } }

Notre mthode rapport() affiche simplement lemplacement, le stock de la machine. et ltat

vous tes ici

431

contrle local

Tester le contrleur
Nous avons implment cela en un rien de temps. Le PDG va tre ravi et stupfait par notre expertise en dveloppement. Maintenant, il suffit dinstancier un ControleurDistrib et de lui donner une machine contrler :
public class TestDistributeur { public static void main(String[] args) { int nombre = 0;

Transmettre un emplacement et un nombre de bonbons initial en ligne de commande.

if (args.length < 2) { System.out.println(TestDistributeur <nom> <stock>); System.exit(1); } nombre = Integer.parseInt(args[1]); Distributeur distributeur = new Distributeur(args[0], nombre); ControleurDistrib controleur = new ControleurDistrib(distributeur); // reste du code controleur.rapport(); } }

Ne pas oublier de donner au constructeur un emplacement et un nombre...

r et lui ...puis instancier un contrleulaq uelle il doit transmettre la machine sur faire un rapport.
Fichier dition Fentre Aide Bouillabaisse

Quand nous avons besoin dun rapport sur la machine, nous appelons la mthode rapport().

%java TestDistributeur Marseille 112 Distributeur : Marseille Stock Courant : 112 bonbons tat courant : En attente dune pice

Le rsultat a lair parfait, mais je crois que je nai pas t clair. Je veux contrler mes distributeurs DISTANCE ! Dailleurs, nous avons dj des rseaux en place pour cela. Allons les gars, vous tes censs tre la gnration Internet !

Et voil le rsultat !

432

Chapitre 11

le pattern proxy

Eh bien, cela nous apprendra analyser les besoins avant de nous prcipiter sur le codage. Jespre quil ne va pas falloir tout recommencer...

Ne vous inquitez pas. Je viens de rviser mes design patterns. Il suffit quon cre un proxy distant et nous serons fin prts.

Guy

Luc

Paul

Guy : Un quoi distant ? Paul : Un proxy distant. Rflchissez : nous avons dj crit le code du contrleur, exact ? Nous donnons ControleurDistrib une rfrence a une machine et il nous retourne un rapport. Le problme est que le contrleur sexcute dans la mme JVM que le distributeur et que le PDG veut rester tranquillement assis son bureau et contrler les machines distance ! Mais si nous laissions notre classe ControleurDistrib telle quelle en lui transmettant un proxy un objet distant ? Guy : Je ne suis pas sr de comprendre. Luc : Moi non plus. Paul : Commenons par le commencement... un proxy est un remplaant dun objet rel. En loccurrence, le proxy se comporte exactement comme sil tait un objet Distributeur, mais, dans les coulisses, il communique sur le rseau et converse avec le vrai Distributeur distant. Luc : Donc, tu dis que nous conservons le code tel quel et que nous passons au contrleur une rfrence une version proxy du Distributeur... Guy : Et ce proxy fait semblant dtre lobjet rel, mais il ne fait que communiquer sur le rseau avec celui-ci. Paul : Oui, cela rsume assez bien. Guy : Cela semble plus facile dire qu faire. Paul : Peut-tre, mais je suis plutt optimiste. Nous devons nous assurer que le distributeur peut se comporter comme un service et accepter des requtes sur le rseau. Nous devons galement donner notre contrleur un moyen dobtenir une rfrence un objet proxy, mais Java contient dj dexcellents outils intgrs qui vont nous aider. Mais dabord, parlons un peu plus des proxies distants... vous tes ici 433

proxy distant

Le rle du proxy distant


Un proxy distant joue le rle de reprsentant local dun objet distant. Questce quun objet distant ? Rien dautre quun objet qui rside sur le tas dune autre JVM (ou, plus gnralement, un objet qui sexcute dans un autre espace dadressage). Quest-ce quun reprsentant local ? Cest un objet sur lequel vous pouvez appeler des mthodes locales qui seront relayes lobjet distant.

Ordinateur du P

DG

Tas local

mblant dtre Le proxy faitt.se En ralit, ce lobjet distan prsentant de nest quun re Tas lobjet rel !

Distributeur distant av ec une JVM.


distant

urDistrib Ici, le Controlent : il croit est lobjet clieributeur parler au dist parle quau rel, mais il ne proxy qui proxy. Cest lebuteur rel parle au distri sur le rseau.

Co

tr ib

ntr s oleurDi

Proxy

ist

ributeu

cien code, Comme votre an se avec un sauf quil conver proxy.

T tant ESlu is d t e j b Lo rel. Cest i qui lobjet thodes qui font a les mtravail. le vrai

Votre objet client se comporte comme si ctait lui qui excutait les appels de mthodes distance. En ralit, il appelle les mthodes sur un objet proxy local qui gre tous les dtails de bas niveau de la communication sur le rseau.

434

Chapitre 11

le pattern proxy

Voil une ide drlement astucieuse. Nous allons crire un code qui prend une invocation de mthode, qui la transfre par un moyen quelconque sur le rseau et qui invoque cette mme mthode sur un objet distant. Puis, je suppose que, quand lappel est termin, le rsultat est retransmis sur le rseau notre client. Mais il me semble que ce code va tre trs dlicat crire.

Pas si vite ! Nous nallons pas crire ce code nousmmes. Il fait pratiquement partie des fonctionnalits de Java. Nous navons rien dautre faire que de modifier notre code pour pouvoir tirer parti de RMI.

A A

Musclez vos neurones

Avant daller plus loin, rflchissez la faon dont vous concevriez un systme qui permettrait les appels de mthode distance. Comment faciliteriez-vous le travail du dveloppeur pour quil ait crire le moins de code possible ? Comment feriez-vous pour que linvocation distance soit transparente ?

Musclez vos neurones

Linvocation distance doit-elle tre totalement transparente ? Est-ce une bonne ide ? Quel est le problme potentiel de cette approche ?

vous tes ici

435

dtour par rmi

Ajouter un proxy distant au code de contrle du Distributeur


Sur le papier, tout va bien. Mais comment cre-t-on un proxy qui sait comment invoquer une mthode qui rside dans une autre JVM ? Mmmm... Vous ne pouvez pas obtenir de rfrence un objet qui rside sur un autre tas, non ? Autrement dit, vous ne pouvez pas crire : Canard c = <objet sur un autre tas> Ce que la variable c rfrence doit tre dans le mme espace dadressage que le code qui excute linstruction. Alors, comment procder ? Eh bien, cest l que RMI (Remote Method Invocation) de Java entre en scne... RMI nous fournit un moyen daccder aux objets appartenant une JVM distante et nous permet dappeler leurs mthodes. Vous avez peut-tre rencontr RMI dans Java tte la premire. Si ce nest pas le cas, nous allons faire un lger dtour et prsenter RMI avant dajouter la prise en charge du proxy au code du Distributeur. Voici donc ce que nous allons faire :

Premirement, nous allons faire un dtour et ltudier un peu. Mme si vous connaissez bien RMI, vous voudrez peut-tre nous accompagner et admirer le paysage. Puis nous allons reprendre notre Distributeur et le transformer en un service distant fournissant un ensemble de mthodes qui peuvent tre appeles distance. Enfin, nous crerons un proxy capable de converser avec un Distributeur distant, en utilisant RMI, et nous rassemblerons le systme de contrle pour que le PDG puisse surveiller un nombre quelconque de machines distantes.

Dtour par RMI


Si vous ne connaissez pas RMI, lisez les quelques pages suivantes. Sinon, vous pouvez les parcourir rapidement pour faire une petite rvision.

436

Chapitre 11

le pattern proxy

Mthodes distantes : les bases


Supposons que nous voulions concevoir un systme qui nous permette dappeler un objet local qui fait suivre chaque requte un objet distant. Comment procderions-nous ? Il nous faut une paire dobjets auxiliaires qui vont soccuper des communications notre place. Ces objets auxiliaires permettent au client de se comporter comme sil appelait une mthode sur un objet local (ce quil est en fait). Le client appelle une mthode sur lauxiliaire du client, comme si cet auxiliaire tait le service rel. Puis lauxiliaire se chargera de transmettre la requte pour nous. Autrement dit, lobjet client pense quil appelle une mthode sur le service distant, parce que lauxiliaire fait semblant dtre lobjet service, comme sil tait lobjet qui possde la mthode que le client veut appeler. Mais lauxiliaire du client nest pas rellement le service distant. Mme sil se comporte comme si ctait le cas (parce quil a la mme mthode que le celle que le service annonce), il ne possde en ralit rien de la logique laquelle le client sattend. En lieu et place, il contacte le serveur, transfre les informations sur lappel de mthode (nom de la mthode, arguments, etc.) et attend un retour du serveur. Ct serveur, lauxiliaire du service reoit la requte de lauxiliaire du client (via une connexion Socket), lit les informations sur lappel et invoque la mthode relle sur lobjet service rel. Ainsi, pour lobjet service, lappel est local. Il vient de lauxiliaire du service, non dun client distant. Lauxiliaire du service obtient alors la valeur de retour du service, l emballe et la renvoie (sur le flot de sortie du Socket) lauxiliaire du client. Ce dernier dballe linformation et retourne la valeur lobjet client.

Dtour par RMI

Ceci devrait vous

sembler familier...
Tas du

client fait Lauxiliaire dure le service, semblant dt un remplamais il nest qu rel. client ant de lobjet

Tas du serveur

client

Lobjet client vrai croit parler aue service. Il pens est que lauxiliaire faire lentit qui va le vrai travail.

Au xil Ob iaire du jet t c l i en

Au

xiliaire du

serveur

Ob

jet servic

Voici notre proxy.

Lauxiliaire du service reoit la requte de lauxiliaire du client, la dballe et appelle la mthode sur le service rel.

le vice EST r e s t e j i b lu Lo vice. Cest qui vrai ser la mthode qui possde vrai travail. fait le

vous tes ici

437

RMI

Comment fonctionne lappel de mthode ?


1

Lobjet client appelle faireTravailCapital() sur lauxiliaire du client. Tas du client


faireTravailCapital()

Tas du serveur

C li ent

Ob

jet client

Au xiliaire du

Au xiliaire du

se rvice

Ob

v jet du ser

Lauxiliaire du client emballe les informations sur lappel (nom de mthode, arguments, etc.) et lenvoie sur le rseau lauxiliaire du service. Tas du serveur Tas du client le client veut appeler une mthode
faireTravailCapital()

C li ent

Ob

jet client

Au xiliaire du

Au xiliaire du

se rvice

Ob

v jet du ser

Lauxiliaire du service dballe les informations quil a reues de lauxiliaire du client, dtermine quelle mthode appeler (et sur quel objet) et invoque la mthode relle sur lobjet service rel. Tas du client
le client veut appeler une mthode

Tas du serveur
faireTravailCapital()

faireTravailCapital()

se rvice

ice
v

ice

Ob

jet clie

nt

Au xiliaire du

Au xiliaire du

Ob

jet du ser

voici Souvenez-vous :ient la lobjet qui cont E, celui qui logique RELL vail ! fait le VRAI tra

C li ent

438

Chapitre 11

ice

le pattern proxy

La mthode est appele sur lobjet service, qui retourne un rsultat lauxiliaire du service. Tas du client Tas du serveur
rsultat

Dtour par RMI

C li ent

se rvice

Ob

jet clie

nt

Au xiliaire du

Au xiliaire du

Ob

jet du ser

Lauxiliaire du service emballe les informations retournes par lappel et les renvoie sur le rseau lauxiliaire du client. Tas du serveur
rsultat emball

Tas du client

C li ent

Ob

jet client

Au xiliaire du

Au xiliaire du

se rvice

Ob

v jet du ser

Lauxiliaire du client dballe les valeurs retournes et les transmet lobjet client. Pour lobjet client, tout ce processus a t transparent. Tas du client
rsultat

Tas du serveur

C li ent

Ob

jet client

Au xiliaire du

Au xiliaire du

se rvice

Ob

v jet du ser

ice

ice

ice
v

vous tes ici

439

RMI: vue densemble

RMI: vue densemble


Bien. Vous savez maintenant comment fonctionnent les mthodes distantes. Il ne vous reste plus qu apprendre comment utiliser RMI pour effectuer des appels de mthodes distance. Que fait RMI pour vous? Il construit les objets auxiliaires du client et du service, allant jusqu crer un objet auxiliaire du client ayant les mmes mthodes que le service distant. La bonne nouvelle avec RMI, cest que vous navez pas besoin dcrire une seule ligne de code pour le rseau ni pour les entres/sorties. Votre client peut appeler les mthodes distantes (celles du service rel) exactement comme dans des appels de mthode normaux sur des objets sexcutant sans leur JVM locale. RMI fournit galement toute linfrastructure qui assure le fonctionnement de lensemble lors de lexcution, y compris un service de recherche que le client peut utiliser pour localiser les objets distants et y accder. Mais les appels RMI et les appels de mthodes locaux (normaux) prsentent une diffrence. Noubliez pas que mme si le client a limpression que lappel de mthode est local, lauxiliaire du client transmet lappel de mthode sur un rseau. Il faut donc des mthodes pour le rseau et les E/S. Et que savons-nous sur les mthodes rseau et les mthodes dE/S? Elles sont risques! Elles peuvent chouer! Et alors elles lancent des exceptions en pagaille. En consquence, le client doit en accepter le risque. Nous allons voir comment dans quelques pages.

Terminologie RMI: pour RMI, lauxiliaire du client est une souche et lauxiliaire du client est un squelette.

Tas du client

Cest cet objet qui va nous servir de proxy!

Tas du serveur SQUELETTE RMI

c li ent

se rvice

SOUCHE RMI

Ob

jet client

Au xiliaire du

Au xiliaire du

Ob

jet service

Voyons maintenant les tapes ncessaires pour transformer un objet en service qui accepte des appels distants, et celles qui permettent au client deffectuer des appels distants. Attachez vos ceintures: les tapes sont nombreuses et la route parseme de bosses et de virages. Rien de vraiment inquitant toutefois. 440
Chapitre 11

Les versions rcentes de Java nexigent pas de squelette explicite, mais il y a toujours quelque chose c serveur qui gre t comportement dule squelette.

le pattern proxy

Crer le service distant


Voici une vue densemble des cinq tapes de la cration du service distant. Autrement dit, les tapes ncessaires pour prendre un objet ordinaire et faire en sorte quil puisse tre appel par un client distant. Nous ferons ceci plus tard dans notre Distributeur. Pour linstant, nous allons parcourir les tapes que nous expliquerons ensuite chacune en dtail.
Dtour par RMI

tape 1 :

Crer une Interface distante Linterface distante dfinit les mthodes quun client peut appeler. Cest elle que le client va MonService.java utiliser comme type pour votre service. La souche et le service rel limplmentent tous les deux!

public interface MonService extends Remote { }

it les min f d e c terfa s que vous voulez Cette ind istante thodes clients appellent. que les

tape 2:
Crer une Implmentation distante Cest la classe qui fait le VRAI travail. Cest elle qui possde limplmentation relle des mthodes distantes dfinies dans linterface distante. Cest lobjet sur lequel le client va appeler les mthodes (par exemple notre Distributeur!).

public interface MonService extends Remote { }

MonServiceImpl.java

sse qui posLe service rel ; la cla nt le vrai fo i sde les mthodes qunte linterface travail. Elle implme distante.

tape 3:
Gnrer les souches et les squelettes en excutant rmic Ce sont les auxiliaires du client et du serveur. Vous navez pas crer ces classes ni mme regarder le code source qui les gnre. Tout est gr automatiquement quand vous excutez loutil rmic livr avec votre kit de dveloppement Java.

ande Lexcution de la comm classe rmic suivi du nom de la dimplmentation...


Fichier dition Fentre Aide manger

..gnre deux no velles classes po uobjets auxiliaireur les s


101101 10 110 1 0 11 0 001 10 001 01

%rmic MonServiceImpl

MonServiceImpl_Stub.class
101101 10 110 1 0 11 0 001 10 001 01

tape 4:
Excuter le registre RMI (rmiregistry) Le registre RMI est comparable aux pages blanches dun annuaire tlphonique. Cest l que le client va chercher le proxy (lobjet auxiliaire/souche).
Fichier dition Fentre Aide Boire

MonServiceImpl_Skel.class

%rmiregistry

tape 5:
Lancer le service distant Vous devez mettre lobjet service en service. Vote classe dimplmentation cre une instance du service et lenregistre dans le registre RMI. Ce faisant, elle met ce service la disposition des clients.

ceci dans Excuteztre de une fen es spare. command

Fichier dition Fentre Aide Danser

%java MonServiceImpl

vous tes ici

441

crer une interface distante

tape 1: crer une interface distante


1

tendre java.rmi.Remote Remote est une interface marqueur, ce qui veut dire quelle na pas de mthodes. Toutefois, cela a une signification spciale pour RMI et vous devez appliquer cette rgle. Remarquez que nous avons crit extends: une interface est autorise tendre une autre interface.

public interface MonService extends Remote {


2

Dclarer que toutes les mthodes lancent une RemoteException Linterface distante est celle que le client utilise comme type pour le service. Autrement dit, le client invoque des mthodes sur une entit qui implmente linterface distante. Cette entit est la souche, bien sr, et comme cest la souche qui se charge du rseau et des E/S, toutes sortes de Gros Problmes peuvent survenir. Le client doit reconnatre lexistence de ces risques en grant ou en dclarant les exceptions distantes. Si les mthodes dune interface dclarent des exceptions, tout code appelant des mthodes sur une rfrence de ce type (le type de linterface) doit grer ou dclarer les exceptions. .

ue que nous Ceci nous indiqli nterface pour allons utiliser ar des appels prendre en ch ge distants.

import java.rmi.* ;

t dans Linterface Remote es

java.rmi

Tout appel de mthode public interface MonService extends Remote { public String direBonjour() throws RemoteException; distant est considr comme risqu. Dclarer une }
3

Sassurer que les arguments et les valeurs de retour sont de type primitif ou srialisables Les arguments et les valeurs de retour dune mthode distante doivent tre de type primitif ou srialisable. Rflchissez: tout argument dune mthode distante doit tre emball et expdi sur le rseau et cela se fait au moyen de la srialisation. Il en va de mme pour les valeurs de retour. Si vous utilisez des types primitifs, des chanes de caractres et la majorit des types de lAPI (y compris les tableaux et les collections), tout ira bien. Si vous transmettez vos propres types, vrifiez que vos classes implmentent Serializable.

RemoteException pour chaque mthode oblige le client faire attention et reconnatre quil pourrait y avoir un dysfonctionnement.
Consultez Java-Tte la premire si vous avez besoin de vous rafrachir le mmoire sur Serializable.

public String direBonjour() throws RemoteException;

Comme le serveur va rexpdie cet te valeur de retour sur le rseau au client, celle-ci doir t tr cette faon que les arguments et e srialisable. Cest de les valeurs de retour sont emballs et transmis.
442
Chapitre 11

le pattern proxy

tape2: crer une implmentation distante


1

Implmenter linterface distante Votre service doit implmenter linterface distante celle qui possde les mthodes que votre client va appeler.

Dtour par RMI

public class MonServiceImpl extends UnicastRemoteObject implements MonService { public String direBonjour() { return Le serveur dit, Bonjour; Le compilateu r va vrifier que vous } avez implment toutes les mthod // reste du code de la classe de linterface que vous implment es ez. } Dans ce cas, il

ny en a quune.

tendre UnicastRemoteObject Pour pouvoir fonctionner comme service distant, votre objet doit disposer dune fonctionnalit lie la distance. Le moyen le plus simple consiste tendre UnicastRemoteObject (du package java.rmi.server) et laisser cette classe (votre superclasse) travailler votre place. public class MonServiceImpl extends UnicastRemoteObject implements MonService {

crire un constructeur sans arguments qui dclare une RemoteException Votre nouvelle superclasse, UnicastRemoteObject, prsente un petit problme: son constructeur lance une RemoteException. Le seul moyen de le rsoudre est de dclarer un constructeur pour votre implmentation distante, dans le seul but de disposer dun endroit pour dclarer une RemoteException. Souvenez-vous: lorsquune classe est instancie, le constructeur de a superclasse est toujours appel. Si celui-ci lance une exception, vous navez pas dautre choix que de dclarer que votre constructeur lance galement une exception. public MonServiceImpl() throws RemoteException { } Enregistrer le service dans le registre RMI

in de placer quoi Vous navez pas beso constructeur. Il que ce soit dans le t un moyen de e vous faut simplemen nstructeur de votr dclarer que le coun exception. superclasse lance e

Maintenant que vous avez un service distant, vous devez le mettre la disposition des clients distants. Pour ce faire, vous linstanciez et vous le placez dans le registre RMI (qui doit tre en train de sexcuter, sinon cette ligne de code choue). Quand vous enregistrez lobjet implmentation, le systme RMI place la souche dans le registre, puisque cest delle dont le client a besoin en ralit. Enregistrez votre service grce la mthode statique rebind() de la classe java.rmi.Naming. le e service (pour quere tr vo m no try { un z ne Don dans le regist ) et MonService service = new MonServiceImpl(); client puisse le rechercher nd vous registre RMI. Quaic le ns da le zNaming.rebind(BonjourDistant, service); re par la st gi enre I remplace le serv e RM e, ic rv se } catch(Exception ex) {...} et bj lo liez registre.

souche et place celle

-ci dans le

vous tes ici

443

souches et squelettes

tape 3: gnrer des souches et des squelettes


1

Excutez rmic sur la classe de limplmentation distante (non sur linterface distante) Loutil rmic, livr avec le kit de dveloppement Java, prend une implmentation de service et cre deux nouvelles classes, la souche et le squelette. Il applique une convention de nommage: le nom de votre implmentation avec soit _Stub soit _Skel ajout la fin. rmic dispose dautres options, notamment ne pas gnrer de squelettes, lire le code source de ces classes et mme utiliser IIOP comme protocole. Ici, nous procdons comme vous procderez gnralement. Les classes atterriront dans le rpertoire courant (celui dans lequel vous vous tes dplac avec la commande cd). Souvenez-vous: puisque rmic doit tre capable de voir la classe de votre implmentation distante, vous excuterez probablement rmic depuis le rpertoire dans lequel elle se trouve. (Cest exprs que nous nutilisons pas de packages ici pour simplifier. Dans le monde rel, vous devriez prendre en compte les structures de rpertoires des packages et les noms qualifis.)

pas Remarquez quil ny aJu e le nom .class la fin. st de la classe.


Fichier dition Fentre Aide Whuffie

RMIC gnre eu x nouvelles classed s po les objet auxiliai ur res..


101101 10 110 1 0 11 0 001 10 001 01

%rmic MonServiceImpl

MonServiceImpl_Stub.class
101101 10 110 1 0 11 0 001 10 001 01

MonServiceImpl_Skel.class

tape 4: excuter rmiregistry


1

Ouvrez une fentre de commandes et lancez rmiregistry. Vrifiez bien que vous partez dun rpertoire qui a accs vos classes. Le plus simple consiste partir de votre rpertoire classes.

Fichier dition Fentre Aide Comment?

%rmiregistry

tape 5: dmarrer le service


1 Ouvrez une autre fentre de commandes et lancez votre service Fichier dition Fentre Aide Comment? Vous pouvez le faire parti de la mthode main() de votre %java MonServiceImpl implmentation distante ou partir dune classe lanceur spare. Dans cet exemple simple, nous avons plac le code qui dmarre le service dans la classe de limplmentation, dans une mthode main() qui instancie lobjet et lenregistre dans le registre RMI.

444

Chapitre 11

le pattern proxy

Code complet ct serveur


Dtour par RMI

Linterface distante: interface publique

import java.rmi.*;

public interface MonService extends Remote {

terface Remote RemoteException et lin .rmi. sont dans le package java java.rmi.Remote Votre interface DOIT tendre

public String direBonjour() throws RemoteException; }

Toutes vos mthodes distantes doivent dclarer une RemoteException.

Le service distant (limplmentation) :

import java.rmi.*; import java.rmi.server.*;

public class MonServiceImpl extends UnicastRemoteObject implements MonService {

ns ct est da e j b O e t o us m est le moyen le pl UnicastRgee java.rmi.server. t ec bj eO ot em R tendre Unicastun objet distant. le packa simple de crer

public String direBonjour() { return Le serveur dit Bonjour; }

Vous devez bien sr implmenter toutes les mthodes de linterface. Mais remarquez que vous navez PAS dclarer de RemoteException.

Vous DEVEZ pl menter votre interfaceimd istante!!

public MonServiceImpl() throws RemoteException { }

public static void main (String[] args) { try { MonService service = new MonServiceImpl(); Naming.rebind(BonjourDistant, service); } catch(Exception ex) { Comme le constructeur de votre superclasse (pou ex.printStackTrace(); Un icastRemoteObject) d r } devez crire un construc clare une exception, VOUS } teur, parc

sse (pour Comme le constructeur de votre supercla eption, UnicastRemoteObject) dclare une exc e que parc ur, cte tru cons VOUS devez crire un du elle app ur cte tru cons cela signifie que votre . eur) ruct onst r-c code risques (son supe

e que cela signifie que votre constructe appelle du code risqu (son super-constructeur es ur).
vous tes ici

445

commet obtenir lobjet souche

Comment le client obtient-il lobjet souche?


Le client doit obtenir lobjet souche (notre proxy), puisque cest sur lui que le client va appeler les mthodes. Et cest l que le registre RMI intervient. Le client effectue une recherche, comme dans les pages blanches dun annuaire tlphonique, et dit en substance Voici un nom, et je voudrais la souche qui va avec. Jetons un coup dil au code qui permet de rechercher et dextraire une souche.

ment Voil com

a marche

Code la loupe
Le client utilise toujours linterfac e distante comme type du servic e. En fait, il na jamais besoin de conna tre le vrai nom de classe du servic e distant.

lookup() est une mthode statique de la classe Naming.

Ceci doit tre le nom sous lequel le serv ic ea t enregistr

MonService service = (MonService) Naming.lookup(rmi://127.0.0.1/BonjourDistant);

Vous devez donner le type de linterface, puisque la mthode lookup() retourne un type Object.

Le nom dhte ou ladresse IP de la machine sur laquelle le service sexcute.

446

Chapitre 11

le pattern proxy

Dtour par RMI

Client
r() onjou direB

Serveur

Ob

jet client

S ou c h e

1 lo oku p()

so uc he re to ur n 2 e

S qu e

lette Obje ce t servi

registre RMI (sur le serveur) BonjourDistant

Souche

Comment a marche...
1 Le client consulte le registre RMI
Naming.lookup(rmi://127.0.0.1/BonjourDistant) ;

2 Le registre RMI retourne lobjet souche (comme valeur de retour de la mthode lookup ()) et RMI le dsrialise automatiquement. Vous DEVEZ avoir la classe de la souche (que rmic a gnre pour vous) sur client ou la souche ne sera pas dsrialise.

Le client invoque une mthode sur la souche, comme si elle TAIT le service rel

vous tes ici

447

the remote client

Code complet ct serveur


La classe Nam g (pour la recher registre RMI) in che dans le est dans le pack age java.rmi.
re ie du regist t r o s la . bject De type O caster le e d s a p noubliez :

import java.rmi.*;

public class monClientDistant { public static void main (String[] args) { new monClientDistant().go(); } public void go() {

try { MonService service = (MonService) Naming.lookup(rmi://127.0.0.1/BonjourDistant); String s = service.direBonjour(); System.out.println(s); } catch(Exception ex) { ex.printStackTrace(); } } }

On dirait vraiment un bon vieil appel de mthode! (s au dclarer la RemoteEx f quil doit ception.)

Adresse IP ou nom dhte.

ur et le nom utilis poic e. rv se le r lier/re-lie

Trucs de geeks

Comment le client obtient-il la classe de la souche?

Voici maintenant la question intressante. Dune manire ou dune autre, le client doit avoir la classe de la souche (que vous avez gnre plus tt avec rmic) au moment o il effectue la recherche, sinon la souche ne sera pas dsrialise sur le client et tout explosera. Le client doit galement avoir les classes de tous les objets srialiss retourns par les appels de mthode lobjet distant. Dans un systme simple, vous pouvez vous contenter de fournir ces classes directement au client. Mais il existe un moyen beaucoup plus cool, bien quil dpasse la porte de cet ouvrage. Nanmoins, si vous tes intress, ce moyen beaucoup plus cool se nomme tlchargement dynamique des classes. Avec le tlchargement dynamique des classes, les objets srialiss (comme la souche) sont estampills avec une URL qui indique au systme RMI rsidant sur le client o trouver le fichier .class correspondant lobjet. Puis, au cours du processus de dsrialisation, si RMI ne trouve pas la classe localement, il utilise cette URL pour mettre une requte HTTP GET et rcuprer le fichier .class. Il vous faut donc un serveur web simple pour servir les fichiers .class. Vous devez galement modifier certains paramtres de scurit sur le client. Le tlchargement dynamique des classes prsente encore quelques aspects un peu dlicats, mais vous savez le principal. En ce qui concerne spcifiquement lobjet souche, le client dispose dun autre moyen dobtenir la classe, mais seulement en Java5, Nous en parlerons brivement vers la fin de ce chapitre.

448

Chapitre 11

le pattern proxy

Dtour par RMI

.
Regardez !
Les trois principales erreurs que les programmeurs commettent avec RMI: 1) Oublier de lancer rmiregistry avant de dmarrer le service distant (quand le service est enregistr au moyen de Naming.rebind(), le registre RMI doit tre en train de sexcuter!) 2) Oublier de rendre les arguments et les types de retour srialisables (vous ne le saurez pas avant lexcution parce que le compilateur ne le dtectera pas). 3) Oublier de donner la classe souche au client.

Client

Serveur

S qu e
Ob
jet client
101101 10 110 1 0 11 0 001 10 001 01

lette

S ou c h e

Ob

jet service

S ou c h e
101101 10 110 1 0 11 0 001 10 001 01 101101 10 110 1 0 11 0 001 10 001 01 101101 10 110 1 0 11 0 001 10 001 01

Noubliez pas que le client utilise linterface distante pour appeler les mthodes sur la souche. La JVM du client Client.class MonServiceImpl_Stub.class a besoin de la classe de la souche, mais le client ny fait jamais rfrence dans le code. Le client utilise MonService.class toujours linterface distante, Comme si elle ETAIT lobjet distant rel.
101101 10 110 1 0 11 0 001 10 001 01

MonServiceImpl.class
101101 10 110 1 0 11 0 001 10 001 01

MonServiceImpl_Stub.class
101101 10 110 1 0 11 0 001 10 001 01

MonServiceImpl_Skel.class

MonService.class

Le serveur a besoin des classes de la souche et du squelette ainsi que du service et de linterface distante. Il a besoin de la classe de la souche parce que, souvenez-vous, la souche est substitue au service rel quand celui-ci est li au registre RMI.

vous tes ici

449

contrler le distributeur distance

Revenons notre Distributeur et au proxy


Bien. Maintenant que vous possdez les bases de RMI, vous disposez des outils ncessaires pour implmenter le proxy distant du distributeur. Voyons un peu comment le Distributeur sinsre dans ce cadre:

FIN DV DE IATIO N

D Ordinateur du P

un proxy La souche est bu eur pour le Distri t distant.


Tas du client

Distributeur distant q uip dune JVM.


Tas du serveur

Voici le code de notre con- se trleur. Il utili un proxy pour converser avec rs les distributeu distants.

ib So ucheDistr Co ntr oleurDist

Sq st D ueletteDi ist ributeur

rib

rib

Le squelette accepte les appels distants et assure tout le fonctionnement ct serveur.

ributeur e Le Distr servic est not .eIl va distant une exposer ce distante interfa lient pourra que le c utiliser.

450

Chapitre 11

le pattern proxy

Prparer le Distributeur tre un service distant


Pour convertir notre code afin quil puisse utiliser le proxy distant, la premire tape consiste permettre au Distributeur de servir les requtes distantes des clients. Autrement dit, nous allons le transformer en service. Pour ce faire, nous devons: 1) Crer une interface distante pour le Distributeur. Elle va nous fournir un ensemble de mthodes qui pourront tre appeles distance. 2) Nous assurer que tous les types de retour de linterface sont srialisables. 3) Implmenter linterface dans une classe concrte. Nous allons commencer par linterface distante.

Noubliez pas dimporter


import java.rmi.*;

java.rmi.*

Voici linterface distante.

public interface DistributeurDistant extends Remote { public int getNombre() throws RemoteException; public String getEmplacement() throws RemoteException; public Etat getEtat() throws RemoteException; }

Tous les types de retour sont soit primitifs soit srialisables...

prendre en charge. Et voil les mthodes que nous allons ept Chacune delles lance une RemoteExc ion..

Nous navons quun seul type de retour qui nest pas srialisable: la classe Etat. Arrangeons cela...
import java.io.*;

Serializable est dans le package java.io. erface Nous nous contentons dtendre lintmt hode). ne aucu Serializable (qui ne contient Maintenant, ltat de toutes les sous-classes peut tre transfr sur le rseau.

public interface Etat extends Serializable { public void insererPiece(); public void ejecterPiece(); public void tournerPoignee(); public void delivrer(); }

vous tes ici

451

une interface distante pour le distributeur

En ralit, nous nen avons pas encore fini avec Serializable: nous avons un problme avec Etat. Comme vous vous en souvenez peut-tre, chaque objet Etat maintient une rfrence un distributeur pour pouvoir appeler ses mthodes et modifier son tat. Mais nous ne voulons pas que tout le distributeur soit srialis et transfr avec lobjet Etat. La solution est simple:
public class EtatSansPiece implements Etat { transient Distributeur distributeur; // toutes les autres mthodes }

Dans chaque implmentation dEtat, nous ajoutons le mot-cl transient ce la dclaration de la variable dinstan distributeur. Nous indiquons ainsi la JVM quelle ne doit pas srialiser ce champ.

Nous avons dj implment notre Distributeur, mais nous devons nous assurer quil peut se comporter en service et grer les requtes qui lui parviennent sur le rseau. Pour ce faire, nous devons vrifier que le Distributeur fait tout ce quil faut pour implmenter linterface DistributeurDistant. Comme vous lavez vu dans notre dtour par RMI, lopration est trs simple: il suffit dajouter deux ou trois lignes de code...

Tout dabord, il faut importer les packages rmi.


import java.rmi.*; import java.rmi.server.*;

Le Distributeur va sousclasser UnicastRemoteObject, ce qui va lui permettre de se comporter en service distant.

Le Distributeur doit galement implmenter linterface distante...

public class Distributeur extends UnicastRemoteObject implements DistributeurDistant { // variables dinstance public Distributeur(String emplacement, int nombreBonbons) throws RemoteException { // reste du code du constructeur } public int getNombre() { return nombre; } public Etat getEtat() { return etat; } public String getEmplacement() { return emplacement; } // autres mthodes }

Cest tout! Rien dautre ne change!

...et le constructeur doit lancer une exception, parce que la superclasse le fait.

452

Chapitre 11

le pattern proxy

Enregistrer le ser vice dans le registre RMI


Cest termin pour le service. Il suffit maintenant de faire en sorte quil puisse recevoir des requtes. Tout dabord, nous devons lenregistrer dans le registre RMI, ce qui permettra aux clients de le localiser. Nous allons ajouter la classe de test un peu de code qui va sen charger pour nous:
public class TestDistributeur { public static void main(String[] args) { DistributeurDistant distributeur = null; int nombre; if (args.length < 2) { System.out.println(TestDistributeur <nom> <stock>); System.exit(1); Nous } try { nombre = Integer.parseInt(args[1]);

devons dabord ajouter un bloc try/ catch autour de linstanciation du dist ributeur, parce que notre constructeur peut mainte nant lancer des exceptions.

} }

Distributeur distributeur = new Distributeur(args[0], count); Naming.rebind(// + args[0] + /distributeur, distributeur); } catch (Exception e) { e.printStackTrace(); } Nous ajoutons galement lappel

de du souche la publie qui d(), g.rebin Namin r. buteu distri nom le sous r ibuteu Distr

Excutons le test...

Excutez ceci dabord.


Fichier dition Fentre Aide Comment?

Cette commande met en service le registre RMI.

% rmiregistry
Fichier dition Fentre Aide Comment?

% java Distributeur Marseille 100

Excutez ceci en second.

Cette commande met le Distributeur en fonction et lenregistre dans le registre RMI.


vous tes ici

453

le client contrleur du distributeur

Et maintenant, le client ControleurDistrib...


Vous souvenez-vous de ControleurDistrib? Nous voulions le rutiliser sans devoir tout rcrire pour quil fonctionne sur un rseau. Eh bien nous y sommes presque, mais au prix de quelques petites modifications.

que nous age RMI parce us ck pa le er rt ... po Nous devons im se RemoteException ci-desso utilisons le clas sur import java.rmi.*; us nous reposonsr la classe Maintenant, not te et non su linterface discoan public class ControleurDistrib { rte. Distributeur nc DistributeurDistant machine;
public ControleurDistrib(DistributeurDistant machine) { this.machine = machine; } public void rapport() { try { System.out.println(Distributeur: + machine.getEmplacement()); System.out.println(Stock Courant: + machine.getNombre() + bonbons); System.out.println(tat courant: + machine.getEtat()); } catch (RemoteException e) { e.printStackTrace(); } Nous devons galement intercepter toutes } les }

exceptions distantes qui pourraient survenir quand nous essayons dinvoquer des mt sexcutent finalement sur le rseau. hodes qui

Paul avait raison. Tout va trs bien marcher!

454

Chapitre 11

le pattern proxy

crire le test du contrleur


Nous avons maintenant tout ce quil faut. Il suffit dcrire un peu de code pour que le PDG puisse contrler quelques distributeurs:

Voici le test du contrleur. Cest le PDG qui va lexcuter!


import java.rmi.*; public class TestControleurDistrib {

placements Voici tous les em ntrler. que nous allons co Nous crons un tableau demplacements, un pour chaque machine.

public static void main(String[] args) { String[] emplacement = {rmi://rennes.distribon.com/distributeur, rmi://lille.distribon.com/distributeur, rmi://marseille.distribon.com/distributeur}; ControleurDistrib[] controleur = new ControleurDistrib[emplacement.length]; for (int i=0;i < emplacement.length; i++) { try { DistributeurDistant machine = (DistributeurDistant) Naming.lookup(emplacement[i]); controleur[i] = new ControleurDistrib(machine); System.out.println(controleur[i]); } catch (Exception e) { e.printStackTrace(); } } Il nous faut for(int i=0; i < controleur.length; i++) { controleur[i].rapport(); } } }

Nous crons aussi un tableau de contrleurs.

maintenant un proxy pour chaque machine distante.

ne Puis nous itrons sur chaque machi t. por rap un et nous affichons

vous tes ici

455

le proxy du distributeur

Code la loupe
Ce code retourne un proxy au Distributeur distant (ou lance une exception sil nen trouve pas).

try { DistributeurDistant machine = (DistributeurDistant) Naming.lookup(emplacement[i]); controleur[i] = new ControleurDistrib(machine); } catch (Exception e) { e.printStackTrace(); }

Souvenez-vous: Naming.lookup() est une mthode statique du package RMI qui prend un emplacement et un nom de service et recherche dans cet emplacement dans le registre RMI.

ons un proxy pour av us no e qu is fo ne U te, nous crons un lui la machine distan rDistrib et nous r. nouveau Controleu hine contrle transmettons la mac

Une autre dmo pour le PDG de Distribon


Bien. Il est temps dassembler tout cela et de prsenter une autre dmo. Vrifions dabord que quelques distributeurs excutent le nouveau code:

Sur chaque machine, excutez rmiregistry en arrire-plan ou dans une fentre de commandes spare....
Ficher dition Fentre Aide Comment?

...puis excuter le Distributeur, en lui donnant un emplacement et un nombre initial de bonbons.

% rmiregistry & % java TestDistributeur rennes.distribon.com 100


Ficher dition Fentre Aide Comment?

% rmiregistry & % java TestDistributeur lille.distribon.com 100


Ficher dition Fentre Aide Comment?

% rmiregistry & % java TestDistributeur marseille.distribon.com 250

distributeur trs populaire!


456
Chapitre 11

le pattern proxy

Et maintenant, mettons le contrleur entre les mains du PDG. Esprons que cette fois il sera content

Ficher dition Fentre Aide BonbonsCaramelsEsquimauxChocolats

% java TestControleurDistrib Distributeur: rennes.distribon.com Stock courant: 99 bonbons Etat courant: en attente de pice Distributeur: lille.distribon.com Stock courant: 44 bonbons Etat courant: en attente de poigne tourne Distributeur: marseille.distribon.com Stock courant: 187 bonbons Etat courant: en attente de pice %
Cest stupfiant! Ce programme va rvolutionner notre mtier et balayer la concurrence!

Le contrleur itre sur chaque machine distante et appelle ses mthodes getEmplacement(), getNombre() et getEtat().

Quand on invoque des mthodes sur le proxy, un appel distant est transmis sur le rseau. Il retourne une chane de caractres, un entier et un objet Etat. Comme nous utilisons un proxy, le ControleurDistrib ne sait pas, et na pas besoin de savoir, que les appels sont distants (mais il doit quand mme se soucier des exceptions).
vous tes ici

457

le proxy dans les coulisses

Ctait un succs! Mais je veux tre sr de comprendre exactement ce qui se passe...

Dans les coulisses

Le PDG excute le contrleur. Celui-ci accde dabord aux proxies des distributeurs distants, puis il appelle getEtat() sur chacun (ainsi que getNombre() et getEmplacement()).

D Ordinateur du P

Le type est Distributeu

rDistant

Distributeur distant av ec une JVM

tat() getE

trib

Co

is ntroleurD

h P ro xy/Souc

e pr ox

yr et ou rn

S qu e
2

lette Dist ributeur

1 lo o

kup (m ars eill e)

Registre RMI (sur le distributeur) marseille

Proxy/Souche

458

Chapitre 11

le pattern proxy

getEtat() est appele sur le proxy, qui transmet lappel au service distant. Le squelette reoit la requte, puis la fait suivre au distributeur.

getEtat()

tat() getE

Co

is ntroleurD

Le Distributeur retourne ltat au squelette, qui le srialise et le retransfre au proxy via le rseau. Le proxy le dsrialise et le retourne sous forme dobjet au contrleur.
Etat srialis Objet Etat Objet Etat

trib

Pro he xy/Souc

S qu e

lette Dist ributeur

Co

is ntroleurD

Pr e oxy /Souch

chang a pas du tout ut n ur le r nt co e L il sait quil pe du tout, sauf quexceptions distantes. rencontrer des ent linterface Il utilise galemistant au lieu dune DistributeurDn concrte. implmentatio

trib

S qu e

lette Dist ributeur

De mme, le Distributeur implmente une autre interface et peut lancer une exception distante dans son constructeur. Ceci mis part, le code na pas chang.

Il faut galement un petit fragment de code pour enregistrer et localiser les souches en utilisant le registre RMI. Mais, en tout tat de cause, si nous crivions quelque chose qui doive fonctionner sur lInternet, il nous faudrait une forme ou une autre de service de localisation.
vous tes ici

459

le pattern Proxy: dfinition

Le pattern Proxy: dfinition


Nous avons dj un certain nombre de pages derrire nous et vous avez pu constater que lexplication du Proxy distant tait assez complexe. Pourtant, vous allez voir que la dfinition et le diagramme de classes du pattern Proxy sont en fait relativement simples. Notez que le Proxy distant est une implmentation parmi dautres du pattern Proxy gnrique. Ce dernier prsente en ralit un assez grand nombre de variantes dont nous parlerons plus tard. Pour linstant, voyons les dtails de la forme canonique. Voici la dfinition du pattern Proxy:

Le pattern Proxy fournit un remplaant un autre objet, pour en contrler laccs. .

Bien. Nous avons vu comment le pattern Proxy fournissait un remplaant un autre objet. Nous avons galement dcrit un proxy comme un reprsentant dun autre objet. Mais quen estil du contrle daccs? Un peu trange, non? Ne vous inquitez pas. Dans le cas du distributeur de bonbons, pensez simplement que le proxy contrle laccs lobjet distant. Il doit contrler laccs parce que notre client, le contrleur, ne sait pas comment sadresser un objet distant. Donc, dans un certain sens, le proxy distant contrle laccs pour pouvoir grer notre place les dtails lis au rseau. Comme nous lavons mentionn, il existe de nombreuses variantes du pattern Proxy et ces variantes tournent gnralement autour de la faon dont le proxy contrle laccs. Nous y reviendrons plus tard. Pour linstant, voici un certain nombre de types de contrle daccs:

Utilisez le pattern Proxy pour crer un objet remplaant qui contrle laccs un autre objet qui peut tre distant, coteux crer ou qui doit tre scuris.

Comme nous lavons vu, un proxy distant contrle


laccs un objet distant.

Un proxy virtuel contrle laccs une ressource


dont la cration est coteuse.

Un proxy de protection contrle laccs une


ressource en fonction de droits daccs. Maintenant que vous avez lesprit du pattern, tudions le diagramme de classes...

460

Chapitre 11

le pattern proxy

<<interface>>

Sujet

requte()

jetRel Le Proxy et le Su us deux implmentent tot. Cela permet linterface Sujetraiter le proxy tout client de mme le SujetRel. exactement co

SujetRel
requte()

sujet

Proxy
requte()

Le SujetRel est Le Proxy instancie souvent gnralement lobjet qui ralise la plus grande partie le SujetRel ou en gre la cration. du vrai travail. Le Proxy en contrle laccs.

conserve une Comme le Proxy ujet, il peut rfrence au Se les requtes lui transmettr ce ssaire. lorsque cest n

Parcourons le diagramme... Tout dabord, nous avons un Sujet qui fournit une interface pour le SujetRel et le Proxy. Puisquils implmentent la mme interface, le Proxy peut toujours tre substitu au SujetRel. Le SujetRel est lobjet qui fait le vrai travail. Cest lui que le Proxy reprsente et auquel il contrle laccs. Le Proxy contient une rfrence au SujetRel. Dans certains cas, le Proxy peut tre responsable de la cration et de la destruction du SujetRel. Les clients interagissent avec le SujetRel via le Proxy. Comme le Proxy et le SujetRel implmentent la mme interface (Sujet), le Proxy peut tre substitue au sujet partout o ce dernier peut tre utilis. Le Proxy contrle galement laccs au SujetRel; ce contrle peut tre ncessaire si le sujet sexcute sur une machine distante, si le sujet est coteux crer ou si laccs au sujet doit tre protg pour une raison quelconque. Maintenant que vous comprenez le pattern gnrique, voyons un certain nombre dautres faons dutiliser un proxy en dehors du Proxy Distant...

vous tes ici

461

proxy virtuel

Prts pour le Proxy Virtuel?


Jusquici, vous avez vu la dfinition du pattern Proxy et vous avez eu un aperu dun exemple spcifique: le Proxy Distant. Nous allons maintenant considrer un autre type de proxy, le Proxy Virtuel. Comme vous allez le dcouvrir, le pattern Proxy peut se manifester sous de nombreuses formes, et pourtant toutes ces formes observent approximativement la mme structure. Mais pourquoi tant de formes? Parce que le pattern Proxy peut sappliquer quantit de situations diffrentes. Voyons le Proxy Virtuel et comparons-le au Proxy Distant:

Proxy Distant
Avec Proxy Distant, le proxy se comporte comme un reprsentant local dun objet qui rside dans une JVM diffrente. Quand on appelle une mthode sur le proxy, lappel est transfr sur le rseau, la mthode est invoque distance et le rsultat est retourn au proxy, puis au Client.
te() requ

te() requ

C li e n t

Proxy

Su

jetR el

Ce diagramme nous est maintenant familier.

Gros objet cote

ux crer.

Proxy Virtuel
Proxy Virtuel agit en tant que reprsentant dun objet dont la cration peut tre coteuse. Il diffre souvent la cration de lobjet jusquau moment o elle est ncessaire. Il sert galement de substitut lobjet avant sa cration. Aprs quoi, le proxy dlgue les requtes directement au SujetRel.
te() requ

Le proxy cre le SujetRel quand il est ncessaire.


Proxy

C li e n t

Le proxy peut grer les requtes, ou, si le SujetRel a t cr, lui dlguer les appels.

Su

jetR el

462

Chapitre 11

le pattern proxy

Afficher des pochettes de CD


Supposons que vous vouliez crire une application qui affiche les pochettes de vos CD favoris. Vous pouvez crer un menu des titres de CD puis rcuprer les images sur un service en ligne comme Amazon.com. Si vous utilisez Swing, vous pouvez crer une icne (avec la classe Icon) et lui demander de tlcharger limage sur le rseau. Un seul problme: selon la charge du rseau et le dbit de votre connexion, le chargement dune pochette de CD peut prendre un certain temps. Votre application doit donc afficher quelque chose pendant que vous attendez que limage apparaisse. De plus, vous ne voulez pas que toute lapplication se mettre ramer pendant quelle attend limage. Une fois limage charge, le message doit disparatre et vous devez voir limage. Pour ce faire, un moyen pratique consiste utiliser un proxy virtuel. Le proxy virtuel peut prendre la place de licne, grer le chargement en arrire-plan et, tant que limage na pas t entirement rcupre via le rseau, afficher Chargement en cours, patientez.... Une fois limage charge, le proxy dlguera laffichage licne.

Slectionnez ici la pochette dalbum de votre choix

Pendant que limage se charge le proxy affiche un message. ,

age est e, le Quand lim nt charg entiremef fiche. proxy la


vous tes ici

463

ProxyDImage contrle laccs

Concevoir le Proxy Virtuel pour les pochettes


Avant dcrire le code de laffichage des pochettes, regardons le diagramme de classes. Vous allez voir quil est similaire celui de notre Proxy Distant, sauf quici nous utilisons le proxy pour masquer un objet dont la cration est coteuse (parce que les donnes destines licne transitent sur le rseau) et non un objet qui rside rellement autre part sur le rseau.

Voici linterface Swing Icon qui sert afficher des images dans une interface utilisateur.

<<interface>>

Icon

getIconWidth() getIconHeight() paintIcon()

ImageIcon
getIconWidth() getIconHeight() paintIcon()

sujet

ProxyDImage
getIconWidth() getIconHeight() paintIcon()

Et voici javax.swing.ImageIcon classe qui affiche une image. , une

Voil notre proxy. Il commence par afficher un message. Quand limage est charge, il dlgue son affichage ImageIcon.

Comment ProxyDImage va fonctionner:


1

ProxyDImage cre dabord une ImageIcon et commence la charger partir dune URL donne. Pendant quil rcupre les octets qui composent limage, ProxyDImage affiche Chargement en cours, patientez.... Lorsque limage est entirement charge, ProxyDImage dlgue tous les appels de mthode ImageIcon, notamment paintIcon(), getWidth() et getHeight(). Si lutilisateur demande une nouvelle image, nous crons un nouveau proxy et nous rptons le processus.

464

Chapitre 11

le pattern proxy

crire ProxyDImage
class ProxyDImage implements Icon { ImageIcon image; URL urlImage; Thread threadChargement; boolean chargement = false;

ProxyDImage implmente linterface Icon

<<interface>>

Icon

getIconWidth() getIconHeight() paintIcon()

public ProxyDImage(URL url) { urlImage = url; } public int getIconWidth() { if (image != null) { return image.getIconWidth(); } else { return 800; } } public int getIconHeight() { if (image != null) { return image.getIconHeight(); } else { return 600; } }

image est licne RELLE, celle que nous voulons voir safficher quand elle aura t charge. Nous transmettons lURL de limage dans le constructeur. Cest limage que nous voulons tlcharger et afficher! Nous retournons une largeur et une hauteur par dfaut tant que limage nest pas charge. Ensuite, nous retournons les valeurs relles.

public void paintIcon(final Component c, Graphics g, int x, int y) { if (image != null) { image.paintIcon(c, g, x, y); } else { g.drawString(Chargement en cours. Patientez..., x+300, y+190); if (!chargement) { chargement = true; threadChargement = new Thread(new Runnable() { public void run() { try { image = new ImageIcon(urlImage, Pochette de CD); c.repaint(); } catch (Exception e) { e.printStackTrace(); } Cest l que cela devient intressant. } Cette portion de code peint }); limage lcran (en dlguant image). threadChargement.start(); Mais si nous navons pas dImageIcon } } compltement cre, nous en crons } une. Nous allons voir cela de plus prs

page suivante...

vous tes ici

465

ProxyDImage la loupe

Code la loupe
Cette mthode est appele quand il est temps dafficher limage lcran.

public void paintIcon(final Component c, Graphics g, int x, int y) { if (image != null) { image.paintIcon(c, g, x, y); } else {

Si nous avons dj une image, nous lui demandons de safficher.


Sinon, nous affichons un message de chargement.

g.drawString(Chargement en cours. Patientez..., x+300, y+190); if (!chargement) {

chargement = true; threadChargement = new Thread(new Runnable() { public void run() { try { image = new ImageIcon(urlImage, Pochette de CD); c.repaint(); } catch (Exception e) { e.printStackTrace(); } } }); threadChargement.start(); } } }

z ELLE. Note R e g a im l s n st nous chargeo c IconImage e ermine t Cest l quegement dune image aveg se e n on Ic e r a a m h I c d la ne le r u que constructe pas charge. Comme ce le : e n o r h rans c c syn limage nest de mettre jour les er de e u q t n a t s e d pa aucune chancessage, nous allons procC ode au nous laisse h m e r s n not da s r e il a t ic f d f s a le d s et nchrone. Tou ... manire asy page suivante e p o sc o r ic m

466

Chapitre 11

le pattern proxy

Code au microscope
de rcuprer limage... Si nous ne sommes pas dj en train faire (au cas o vous poseriez ...il est temps de commencer le paint(): nous pouvons donc la question, un seul thread appelle on des threads!). tre tranquilles sur lordre dexcuti (!chargement) { Comme nous ne voulons pas que se chargement = true; linterface utilisateur risque der un threadChargement = new Thread(new Runnable() { planter, nous allons utilise public void run() { autre thread pour extraire limage.

if

} });

try { image = new ImageIcon(urlImage, Pochette de CD); c.repaint(); Dans notre thread, } catch (Exception e) { n ous instancio e.printStackTrace(); lobjet Icon. ns } Son

threadChargement.start(); }

Quand nous avons limage, nous disons Swing quil faut la repeindre.

construct u r ne se terminera e p a que limage s tant pas charge ne sera .

Ainsi, la prochaine fois que laffichage changera aprs linstanciation dImageIcon, la mthode paintIcon() affichera limage, pas le message.

vous tes ici

467

problme de conception

Problme de conception

Il semblerait que la classe ProxyDImage possde deux tats qui sont contrls par des instructions conditionnelles. Connaissez-vous un autre pattern qui pourrait permettre darranger ce code? Comment pourriez-vous revoir la conception de ProxyDImage?

class ProxyDImage implements Icon { // variables dinstance et constructeur public int getIconWidth() { if (image != null) { return image.getIconWidth(); } else { return 800; } } public int getIconHeight() { if (image != null) { return image.getIconHeight(); } else { return 600; } }

Deux tats

Deux tats

public void paintIcon(final Component c, Graphics g, int x, int y) { if (image != null) { image.paintIcon(c, g, x, y); } else { g.drawString(Chargement en cours. Patientez..., x+300, y+190); // reste du code } } }

Deux tats

468

Chapitre 11

le pattern proxy

Tester le programme Code prt lemploi


Bien. Il est temps de tester ce nouveau proxy virtuel dernier cri. Dans les coulisses, nous avons concoct un nouveau programme, TestProxyDImage, qui cre une fentre, puis un cadre, puis les menus et enfin notre proxy. Nous nallons pas entrer dans tous les dtails ici, mais vous pouvez toujours tlcharger le code source et y jeter un coup dil ou vous rendre la fin de ce chapitre o vous trouverez lintgralit du code du Proxy Virtuel. Voici une vue partielle du code de test: public class TestProxyDImage { ComposantDImage composant; public static void main (String[] args) throws Exception { TestProxyDImage test = new TestProxyDImage(); } public TestProxyDImage() throws Exception{ // installer le cadre et les menus Icon icone = new ProxyDImage(urlInitiale); composant = new ComposantDImage(icone); cadre.getContentPane().add(composant); } }

Ici, nous crons un proxy pour limage et nous lui affectons une URL initiale. Chaque fois que vous effectuez une slection dans le menu, vous obtenez un nouveau proxy.

Enfin, nous ajoutons le proxy au cadre pour pouvoir lafficher.


Et maintenant, excutons le test:
Ficher dition Fentre Aide Musique!

Puis nous enveloppons notre proxy dans un composant pour pouvoir lajouter au cadre. Le composant se chargera de la taille du proxy et autres dtails similaires.

% java TestProxyDImage

Lexcution de TestProxyDImage doit afficher cette fentre.


Essayez ceci...
1

Utilisez le menu pour charger diffrentes pochettes de CD; regardez le proxy afficher le message jusqu larrive de limage. Redimensionnez la fentre pendant laffichage du message. Remarquez que le proxy gre le chargement sans planter la fentre Swing. Ajoutez vos propres CD favoris TestProxyDImage.

vous tes ici

469

dans les coulisses avec le proxy

Quavons-nous fait?
1

Nous avons cr un ProxyDImage pour laffichage. La mthode paintIcon() est appele et ProxyDImage initialise un thread pour extraire limage et crer lobjet ImageIcon.

Dans les coulisses

paintIcon()

e un ProxyDImage cr ancier thread pour inist mmence lImageIcon qu co extraire limage.

obtenir limage

Un serveur dimages quelconque sur lInternet

e Pro xyDImag
affiche le message de chargement

Im ag

eIcon

image obtenue

un moment donn, limage est retourne et lImageIcon est compltement instancie.

Im ag

eIcon

Quand lImageIcon est cre, la prochaine fois que paintIcon() est appele, le proxy dlgue lImageIcon.

paintIcon()

paintIcon()

e Pro xyDImag
affiche limage relle

Im ag

eIcon

470

Chapitre 11

le pattern proxy

questions stupides

il ny a pas de

Q: R:

Le Proxy Distant et le Proxy Virtuel me semblent vraiment trs diffrents; sagit-il vraiment du MME pattern?

Vous trouverez beaucoup de variantes du pattern Proxy dans le monde rel. Ils prsentent tous un point commun: ils interceptent une invocation de mthode que le client effectue sur le sujet. Ce niveau dindirection nous permet de faire beaucoup de choses, notamment transmettre des requtes un sujet distant, procurer un reprsentant un objet dont la cration est coteuse, ou, comme nous allons le voir, fournir un certain niveau de protection capable de dterminer quels clients peuvent appeler certaines mthodes. Et ce nest quun dbut: le pattern Proxy a un grand nombre dapplications diffrentes et nous en aborderons quelques unes la fin de ce chapitre.

Cest vrai en un sens, mais ce nest pas le plus important. Le plus important, cest que ProxyDImage contrle laccs une ImageIcon. Et comment contrlet-il laccs? Vous pouvez penser en ces termes: le proxy dcouple le client de lImageIcon. Sils taient coupls, le client devrait attendre que limage soit charge avant dafficher toute son interface. Le proxy contrle laccs lImageIcon en affichant une autre reprsentation lcran tant quelle nest pas entirement cre. Une fois lImageIcon cre, le proxy autorise laccs.

Vous parlez l dune forme spcialise de Proxy virtuel nomme Proxy de mise en cache. Un tel proxy mmorise tous les objets crs antrieurement et, si cest possible, retourne lobjet en cache quand une requte est mise. Nous verrons cette variante du pattern Proxy ainsi que quelques autres la fin du chapitre.

R:

Q: R:

Je vois bien le rapport entre Dcorateur et Proxy, mais quen est-il dAdaptateur? Un adaptateur semble galement trs similaire. Proxy et Adaptateur sinterposent tous deux entre le client et un objet et lui transmettent les requtes. Mais noubliez pas quun adaptateur modifie linterface de lobjet quil adapte alors quun proxy implmente la mme interface. Il existe aussi une autre similarit en rapport avec le Proxy de Protection. Un proxy de protection peut autoriser ou interdire un client daccder certaines mthodes dun objet, en fonction du rle de ce client. Il peut donc se contenter doffrir une interface partielle un client, de faon assez similaire celle de certains adaptateurs. Nous allons jeter un coup dil un proxy de protection dans quelques pages.

Q: R:

Et comment faire pour que le client utilise le Proxy et non le SujetRel?

Q:

Pour moi, ProxyDImage ressemble tout fait un Dcorateur. Je veux dire quen substance nous enveloppons un objet dans un autre, puis que nous dlguons les appels lImageIcon. Est-ce que jai loup quelque chose?

Bonne question. Une technique courante consiste fournir un objet fabrique qui instancie et retourne le sujet. Comme il sagit dune mthode de fabrique, nous pouvons envelopper lobjet dans un proxy avant de le retourner. Le client ne sait jamais quil utilise un proxy la place du sujet rel, et il ne sen soucie mme pas.

Q:

Il arrive que Proxy et Dcorateur aient lair trs similaires, mais leurs objectifs sont diffrents. Un dcorateur ajoute un comportement une classe, tandis quun proxy en contrle laccs. Vous pourriez rtorquer que le message de chargement ajoute un comportement.

R:

Dans lexemple de ProxyDImage, jai remarqu quon crait toujours un nouvel objet ImageIcon pour obtenir limage, mme si limage avait dj t charge. Pourrait-on implmenter quelque chose de similaire ProxyDImage pour rcuprer les chargements antrieurs?

vous tes ici

471

face face: proxy et dcorateur

Face face :

Le face face de ce soir: Proxy et Dcorateur senvoient des fleurs.

Proxy
Bonjour, Dcorateur. Je prsume que vous tes l parce quon nous confond parfois?

Dcorateur
Eh bien je crois que les gens nous confondent parce que vous vous baladez en prtendant tre un pattern totalement diffrent alors que vous ntes en fait quun Dcorateur dguis. Et je pense que vous devriez arrter de copier toutes mes ides.

Moi? Copier vos ides? Sil vous plat! Je contrle laccs aux objets. Vous, vous vous contentez de les dcorer. Mon rle est tellement plus important que le vtre que ce nest mme pas drle.

Daccord, vous ntes peut-tre pas entirement frivole... Mais je ne comprends toujours pas pourquoi vous pensez que je copie toutes vos ides. Je sers uniquement reprsenter mes sujets, pas les dcorer.

Me contenter de dcorer? Vous croyez que la dcoration est une tche frivole et sans importance? Laissez-moi vous dire quelque chose, mon pote: jajoute des comportements. Cest la chose la plus importante pour un objet ce quil fait!

Jai limpression que vous navez pas trs bien saisi, Dcorateur. Je ne me contente pas dajouter des comportements: je remplace mes sujets. Les clients mutilisent comme un substitut du sujet rel, parce que je les protge contre les accs indsirables ou parce que jempche leur IHM de ramer pendant quils attendent le chargement dun gros objet, ou encore parce que je masque le fait que leurs sujets sexcutent sur des machines distantes. Je dirais que cest l un objectif trs diffrent du vtre!

Vous avez beau parler de reprsentation. Mais si a ressemble un canard et que a marche comme un canard... Regardez donc votre Proxy Virtuel: cest juste une autre faon dajouter un comportement pour faire quelque chose pendant quon charge un gros objet coteux, et votre Proxy Distant est un moyen de communiquer avec des objets distants pour que vos clients naient pas besoin de sen occuper eux-mmes. Comme je viens de le dire, il ny a l que des comportements.

Dites ce que vous voudrez. Jimplmente la mme interface que les objets que jenveloppe, et vous aussi.

472

Chapitre 11

le pattern proxy

Proxy
Daccord. Je retire ce que jai dit. Vous enveloppez un objet. Mais si je dis parfois de faon informelle quun proxy enveloppe son sujet, ce nest pas vraiment le terme exact. Pensez un proxy distant... Quel est lobjet que jenveloppe? Lobjet que je reprsente et dont je contrle laccs rside sur une autre machine! Quest-ce que vous en dites?

Dcorateur

Ah oui? Pourquoi?

Daccord, mais on sait bien que les proxies distants sont un peu bizarres. Vous avez un autre exemple? Cela mtonnerait.

Bien sr. Prenez les proxies virtuels... Pensez lexemple du programme qui affiche des pochettes de CD. Quand le client mutilise comme proxy, le sujet nexiste mme pas! Quest-ce que je pourrais bien envelopper? Je ne savais pas que les dcorateurs taient aussi stupides! Bien sr quil marrive de crer des objets. Comment croyez-vous quun proxy virtuel obtient son sujet? Et puis vous venez de souligner une autre grosse diffrence entre nous: nous savons tous les deux que les dcorateurs se contentent de faire de lhabillage et quils ninstancient jamais rien.

Ah ah! Et vous allez bientt me dire quen fait vous crez des objets.

Ah oui? Instanciez-moi donc a! Eh, aprs cette conversation je suis convaincu que vous ntes quun proxy passif !

Un proxy passif!? Jaimerais bien vous voir envelopper rcursivement un objet avec dix dcorateurs et garder la tte droite en mme temps.

Cest bien rare quon voit un proxy saventurer envelopper un objet plusieurs fois. En fait, si vous enveloppez quelque chose dix fois, vous feriez peuttre bien de rexaminer votre conception. Et que dire dun proxy qui fait semblant dtre rel alors quil ne fait que servir dintermdiaire avec les objets qui font le vrai travail? Tenez, vous me faites de la peine.
vous tes ici

473

proxy de protection

Utiliser la classe Proxy de lAPI Java pour crer un proxy de protection


Java possde sa propre implmentation de Proxy dans le package java.lang.reflect. Grce ce package, Java vous permet de crer la vole une classe proxy qui implmente une ou plusieurs interfaces et transmet les invocations de mthode une classe que vous spcifiez. Comme la vraie classe proxy est cre lors de lexcution, nous qualifions cette technologie Java de proxy dynamique. Nous allons utiliser le proxy dynamique de Java pour crer notre prochaine implmentation de Proxy (un proxy de protection). Mais auparavant, jetons un rapide coup dil au diagramme de classes qui reprsente la faon dont les proxies dynamiques sarticulent. Comme la plupart des entits du monde rel, ce Proxy diffre lgrement de la dfinition officielle du pattern:
<<interface>> Sujet
requte()
<<interface>> InvocationHandler
invoke()

ES IR TA ILI UT JAVA

aintenant Le Proxy est m x classes. constitu de deu


SujetRel requte()
requte() Proxy
InvocationHandler requte()

Le Proxy est gnr par Java et implmente toute linterface Sujet.

ler, auquel sont Vous fournissez lInvocationHand e qui sont hod transmis tous les appels de mt ionH ler contrle cat invoqus sur le Proxy. LInvo Sujand el. etR du laccs toutes les mthodes

Comme Java cre votre place la classe Proxy, il vous faut un moyen dindiquer celle-ci ce quelle doit faire. Vous ne pouvez pas placer ce code dans la classe Proxy comme nous lavons fait tout lheure, parce que nous ne limplmentons pas directement. Alors, si vous ne pouvez pas insrer ce code dans la classe Proxy, o allez-vous le placer? Dans un InvocationHandler. La tche de lInvocationHandler consiste rpondre tous les appels de mthode envoys au proxy. Reprsentez-vous lInvocationHandler comme lobjet auquel le Proxy demande de faire tout le vrai travail aprs quil a reu les appels de mthode. Voyons maintenant quelles sont les tapes ncessaires pour utiliser le proxy dynamique...

474

Chapitre 11

le pattern proxy

Rencontres Object ville


SEXY
Toute ville digne de ce nom a besoin dun service de rencontres, non? Vous vous tes attel la tche et vous en avez programm un pour Objectville. Vous avez galement essay dinnover en incluant une fonctionnalit Sexy ou non qui offre aux participants la possibilit de se noter mutuellement. Vous pensez que cela aura pour effet de fidliser vos clients. Et puis, cela rend les choses beaucoup plus amusantes. Votre service sappuie sur un bean Personne qui vous permet daccder aux informations concernant un individu et de les modifier:

NON

face. Nous Voici linterlimplmentation allons voir conde... dans une se

public interface BeanPersonne { String getNom(); String getSexe(); String getInterets(); int getSexyOuNon(); void void void void } setNom(String nom); setSexe(String sexe); setInterets(String interets); setSexyOuNon(int note);

Voil o nous sur le nom de obtenons les information ses centres d la personne, son sexe, s SexyOuNon (dintrt et son niveau e 1 10).

Nous pouvons galement modifier les mmes informations au moyen de leurs appels de mthode respectifs.
Regardons maintenant limplmentation

accepte un setSexyOuNon() e la moyenne entier et lajout rsonne. actuelle de la pe

vous tes ici

475

BeanPersonne a besoin de protection

Limplmentation de BeanPersonne
BeanPersonneImpl implmente linterface BeanPersonne
public class BeanPersonneImpl implements BeanPersonne { String nom; String sexe; Les variables dinstance. String interets; int note; int nombreDeNotes = 0; public String getNom() { return nom; } public String getSexe() { return sexe; } public String getInterets() { return interets; } public int getSexyOuNon() { if (nombreDeNotes == 0) return 0; return (note/nombreDeNotes); } public void setNom(String nom) { this.nom = nom; } public void setSexe(String sexe) { this.sexe = sexe; } public void setInterets(String interets) { this.interets = interets; } public void setSexyOuNon(int note) { this.note += note; nombreDeNotes++; } }

Toutes les mthodes get. Chacune delles retourne le contenu de la variable dinstance approprie...

...sauf getSexyOuNon() qui calcule la moyenne en divisant les notes par le nombreDeNotes.

Et voici toutes les mthodes set qui modifient les variables dinstance correspondantes.

Enfin, la mthode setSexyOuNon() in nombreDeNotes etcrmente note au total cour ajoute la ant.

476

Chapitre 11

le pattern proxy

Je narrivais pas obtenir des rendezvous. Et puis je me suis rendu compte que quelquun avait modifi mes centres dintrt. Jai aussi remarqu que beaucoup de gens gonflaient leurs scores SexyOuNon en sattribuant des notes trs leves. On ne devrait pas pouvoir modifier les centres dintrt de quelquun dautre ou se donner soi-mme des notes!

Mme si nous souponnons que dautres facteurs pourraient bien expliquer les checs dric, il a nanmoins raison: on ne devrait pas pouvoir voter pour soi ni changer les donnes dun autre client. De la faon dont notre BeanPersonne est dfini, nimporte quel client peut appeler nimporte quelle mthode. Voil un exemple parfait de situation dans laquelle nous pourrions utiliser un Proxy de Protection. Quest-ce quun Proxy de Protection? Cest un proxy qui contrle laccs un objet en fonction de droits daccs. Si par exemple nous avions un objet Employ, un proxy de protection pourrait permettre lemploy dappeler certaines mthodes tandis quun manager pourrait appeler des mthodes supplmentaires (comme setSalaire()) et quun agent des ressources humaines pourrait appeler toutes les mthodes de lobjet. Dans notre service de rencontres, nous voulons tre certains quun client peut modifier les informations qui le concernent tout en empchant les autres den faire autant. Et nous voulons exactement le contraire avec les notes SexyOuNon: les autres clients doivent pouvoir attribuer la note, mais pas ce client particulier. Le BeanPersonne possde galement un certain nombre de mthodes get, et, comme aucune dentre elles ne retourne dinformations confidentielles, tout client doit pouvoir les appeler.

ric

vous tes ici

477

comdie express

Comdie express: des sujets sous protection


La bulle Internet ne semble plus quun lointain souvenir. Ctait lpoque o il suffisait de traverser la rue pour trouver un poste plus intressant et mieux pay. Les dveloppeurs de logiciels avaient mme des agents...

Jaimerais faire une proposition. Puis-je lui parler?

Elle est prise... euh ... par une runion en ce moment. quoi pensiez-vous?

Jacques Poincom

Agent

roxy de Comme un p, lagent protection ccs son sujet protge la passer que et ne laisse pels... certains ap
Allons, allons! Vous perdez votre temps! Aucune chance! Rappelez-nous quand vous aurez une meilleure offre.

Nous pensons pouvoir lui offrir 15% de plus que son salaire actuel.

478

Chapitre 11

le pattern proxy

Vue densemble: crer un Proxy dynamique pour le BeanPersonne


Nous avons deux problmes rsoudre: les clients ne doivent pas pouvoir changer leur note SexyOuNon, et ils ne doivent pas pouvoir changer non plus les informations personnelles des autres clients. Pour ce faire, nous allons crer deux proxies: lun pour accder votre propre objet BeanPersonne et lautre pour accder lobjet BeanPersonne dun autre client. Ainsi, les proxies pourront contrler les requtes qui sont mises dans chaque circonstance.

Pour crer ces proxies, nous allons utiliser le proxy dynamique de lAPI Java que vous avez vu il y a quelques pages. Java va crer deux proxies pour nous et il nous suffira de fournir des gestionnaires qui sachent quoi faire quand une mthode est appele sur un proxy.

Souvenez-vous de ce diagramme. Nous lavons vu il y a quelques pages...


<<interface>> InvocationHandler

<<interface>> Sujet

requte()

invoke()

tape 1:
Crer deux InvocationHandler, autrement dit deux gestionnaires dinvocations. Les InvocationHandler implmentent le comportement du proxy. Comme vous allez le voir, Java se chargera de crer la classe et lobjet proxy, et il nous faudra seulement fournir le gestionnaire qui sait ce quil faut faire lors dun appel de mthode.

RealSubject requte()

Proxy requte()

InvocationHandler invoke()

tape2:
crire le code qui cre les proxies dynamiques. Nous devons crire un peu de code pour gnrer la classe proxy et linstancier. Nous tudierons ce code dans quelques instants.
Proxy

Nous crons le proxy au moment de lexcution.

Il nous faut deux gestionnaires dinvocations comme celuici.

InvocationHandlerProprietaire
invoke()

tape3:
Envelopper chaque objet BeanPersonne dans le proxy qui convient. Quand nous avons besoin dutiliser un objet BeanPersonne, soit il sagit de lobjet du client lui-mme (auquel cas nous lappelons le propritaire), soit il sagit de celui dun autre client du service (et nous lappellerons nonpropritaire). Dans les deux cas, nous crons le proxy appropri pour le BeanPersonne.

requte()

Quand un client voit son propre bea

n
un dautre.

Quand un client voit le bean de quelqu


Proxy
requte() invoke()

InvocationHandlerNonProprietaire

vous tes ici

479

crer un gestionnaire dinvocations

tape 1: crer les gestionnaires dinvocations


Nous avons que nous devons crire deux gestionnaires dinvocations, lun pour le propritaire et lautre pour le non-propritaire. Mais quest-ce quun gestionnaire dinvocations? Voici une faon de se le reprsenter: quand une mthode est appele sur le proxy, le proxy transmet lappel votre gestionnaire dinvocations mais il nappelle pas la mthode correspondante de ce dernier. Alors quappelle-t-il? Jetons un coup dil linterface InvocationHandler:
<<interface>> InvocationHandlerProprietaire

invoke()

Cette interface ne contient quune seule mthode, invoke(), et, quelle que soit la mthode appele sur le proxy, cest cette mthode invoke() qui est appele sur le gestionnaire. Voyons comment cela fonctionne:

1 Supposons que la mthode setSexyOuNon() est appele sur le proxy.

proxy.setSexyOuNon(9);

Le proxy se retourne et appelle invoke() sur le gestionnaire.

invoke(Object proxy, Method method, Object[] args)

3 Le gestionnaire dtermine ce quil doit faire de la requte et la transmet ventuellement au SujetRel. Comment le gestionnaire dcide-t-il? Nous allons bientt le savoir.

Ici, nous invoquons la mthode sur le SujetRel

La classe Method, qui fait partie du package java.lang. e reflect, nous dit quelle mthod a t appele sur le proxy via sa mthode getName().

return method.invoke(personne, args);

arguments Ici, nous invoquons la Mais maintenant avec les mthode initiale qui a t nous lappelons sur dorigine. appele sur le proxy. Cet le SujetRel... objet nous a t transmis dans lappel de invoke().

480

Chapitre 11

le pattern proxy

Crer des gestionnaires dinvocations (suite)...


Quand invoke() est appele par le proxy, comment savez-vous ce quil faut faire de lappel? En gnral, vous examinez la mthode appele et vous prenez des dcisions en fonction de son nom et ventuellement de ses arguments. Implmentons InvocationHandlerProprietaire pour voir comment il fonctionne:

tie du package java. Comme InvocationHandler fait par lang.reflect, nous devons limporter.
import java.lang.reflect.*;

Tous les gestionnaires dinvocations Le et rel nous implmentent linterface est suj transmis dans le InvocationHandler. constructeur et nous mmorisons une rfrence celui-ci.

public class InvocationHandlerProprietaire implements InvocationHandler { BeanPersonne personne; public InvocationHandlerProprietaire(BeanPersonne personne) { this.personne = personne; } public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException { try { if (method.getName().startsWith(get)) { return method.invoke(personne, args); } else if (method.getName().equals(setSexyOuNon)) { throw new IllegalAccessException(); } else if (method.getName().startsWith(set)) { return method.invoke(personne, args); } } catch (InvocationTargetException e) { e.printStackTrace(); } return null; Comme no

Voici la mthode invoke() qui est appele chaque fois quune mthode est invoque sur le proxy. Si cest une mthode get, nous continuons et nous lappelons sur le sujet rel. Sinon, si cest la mthode setSexyOuNon(), nous empchons de lappeler en lanant une IllegalAccessException.

} }

Si une autre mthode qu elconque est appele, nous prf r on retourner null au lieu de s prendre un risque.

Voici ce qui arrive si le sujet rel lance une exception.

us sommes le propritaire, nous avons le droit dappeler nimporte quelle mthode set. Nous continuons et nous lappelons sur le sujet rel.

vous tes ici

481

crez votre propre gestionnaire dinvocations

Exercice

InvocationHandlerNonProprietaire fonctionne exactement comme InvocationHandlerProprietaire, except quil autorise les appels de setSexyOuNon() et quil interdit ceux de toutes les autres mthodes set. Allez-y! crivez ce gestionnaire.

482

Chapitre 11

le pattern proxy

tape2: crer la classe Proxy et instancier lobjet Proxy


Maintenant, il ne nous reste plus qu crer dynamiquement la classe proxy et instancier lobjet proxy. Commenons par crire une mthode qui accepte un BeanPersonne et qui cre un proxy propritaire pour lui. Autrement dit, crons le type de proxy qui transmet ses appels de mthode InvocationHandlerProprietaire. Voici le code:

Cette mthode accepte un objet personne le (le sujet rel) et retourne un proxy qui va e remplacer. Comme le proxy a la mme interfac que le sujet, nous retournons un BeanPersonne.

Cette portion de code cre le proxy. Comme elle est un peu complique, nous allons ltudier soigneusement. Pour crer un proxy, nous utilisons la mthode statique newProxyInstance() de la classe Proxy... Nous lui transmettons le chargeur de classe pour notre sujet...

BeanPersonne getProxyProprietaire(BeanPersonne personne) { return (BeanPersonne) Proxy.newProxyInstance( personne.getClass().getClassLoader(), personne.getClass().getInterfaces(), new InvocationHandlerProprietaire(personne)); }

...et lensemble dinterfaces que le proxy doit implmenter...

Nous transmettons le sujet rel dans le constructeur du gestionnaire dinvocations. Si vous vous reportez deux pages en arrire, vous verrez que cest ainsi que le gestionnaire accde au sujet rel.

...et un gestionnaire dinvocations,.en loccurrence notre InvocationHandlerProprietaire.

vos crayons Sharpen your pencil

Mme si elle est un peu complique, la cration dun proxy dynamique nest pas bien difficile. Pourquoi ncririez-vous pas getProxyNonProprietaire(), qui retourne un proxy pour InvocationHandlerNonProprietaire?

Encore plus fort: pouvez-vous crire une mthode getProxy() qui accepte en argument un gestionnaire et une personne et qui retourne un proxy qui utilise ce gestionnaire?

vous tes ici

483

trouvez lme soeur

Tester le ser vice de rencontres


Testons le service de rencontres et voyons comment il contrle laccs aux mthodes set selon le proxy utilis.

public class TestRencontres { // variables dinstance public static void main(String[] args) { TestRencontres test = new TestRencontres(); test.tester(); } public TestRencontres() { initialiserBD(); }

La mthode main() cre simplement le test et appelle sa mthode tester(). Le constructeur initialise la base de donnes des personnes abonnes notre service de rencontres. Extrayons une personne de la base de donnes

public void tester() { BeanPersonne luc = getPersonneDepuisBD(Luc Javabine); ...et crons un proxy BeanPersonne proxyProprietaire = getProxyProprietaire(luc); System.out.println(Nom = + proxyProprietaire.getNom()); propritaire. proxyProprietaire.setInterets(tennis, jeu de go); Appelons une mthode get System.out.println(Intrts fixs par le proxy propritaire); puis une mthode set try { puis essayons de proxyProprietaire.setSexyOuNon(10); modifier la note } catch (Exception e) { System.out.println(Le proxy propritaire ne peut pas modifier la note); } ceci ne doit pas marcher! System.out.println(Note = + proxyProprietaire.getSexyOuNon());

BeanPersonne proxyNonProprietaire = getProxyNonProprietaire(luc); System.out.println(Nom = + proxyNonProprietaire.getNom()); try { ...et appelons une mthode get proxyNonProprietaire.setInterets(tennis, jeu de go); } catch (Exception e) { suivie dune mthode set System.out.println(Le proxy non propritaire ne peut pas modifier les intrts); } Ceci ne doit pas marcher! proxyNonProprietaire.setSexyOuNon(3); System.out.println(Note modifie par le proxy non propritaire); System.out.println(Note = + proxyNonProprietaire.getSexyOuNon()); Puis essayons de

Crons maintenant un proxy non-propritaire

changer la note

// autres mthodes comme getProxyProprietaire et getProxyNonProprietaire }

Ceci doit marcher!

484

Chapitre 11

le pattern proxy

Excuter le code...
Ficher dition Fentre Aide Dynamiser

% java TestRencontres Nom = Luc Javabine Intrts fixs par le proxy propritaire

Le proxy propritaire ne peut pas modifier la note Note = 7

Notre proxy propritaire autorise les accs et les modifications, sauf pour la note SexyOuNon.
Notre proxy nonpropritaire nautorise que les accs, mais il permet galement de modifier la note SexyOuNon.

Nom = Luc Javabine Note modifie par le proxy non propritaire Note = 5 %

Le proxy non propritaire ne peut pas modifier les intrts

vous tes ici

485

questions et rponses sur le pattern Proxy

questions stupides

il ny a pas de

Q:

Que signifie donc le mot dynamique dans lexpression proxy dynamique? Est-ce li au fait que jinstancie le proxy et que je laffecte un gestionnaire au moment de lexcution?

Non, le proxy est dynamique parce que sa classe est cre au moment de lexcution. Rflchissez: avant que votre code ne sexcute, il ny a pas de classe proxy. Elle est cre la demande partir de lensemble dinterfaces que vous lui transmettez.

R:

Oui. La classe Proxy a une mthode statique nomme isProxyClass(). Lappel de cette mthode avec une classe retourne true si la classe est un proxy dynamique. part cela, la classe proxy se comportera comme toute autre classe qui implmente un ensemble particulier dinterfaces.

R:

Q: R:

Y a-t-il des restrictions sur le type dinterfaces que je peux transmettre dans newProxyInstance() ?

Vous avez raison, nous navons pas rellement besoin de gnrer des squelettes. partir de Java 1.2, RMI peut transmettre directement les appels des clients au service distant en utilisant lintrospection au moment de lexcution. Mais nous prfrons montrer les squelettes, parce que, du point de vue conceptuel, cela aide comprendre quil y a quelque chose dans les coulisses qui permet la souche du client et au service distant de communiquer.

R:

Q: R:

Mon InvocationHandler ma lair dun proxy bien trange: il nimplmente aucune des mthodes de la classe laquelle il se substitue.

Cest parce que lInvocationHandler nest pas un proxy: cest une classe laquelle le proxy transmet les appels de mthode pour quelle les gre. Le proxy luimme est cr dynamiquement lors de lexcution par la mthode statique Proxy.newProxyInstance( ).

Q:

Existe-t-il un moyen quelconque de savoir si une classe est une classe Proxy?

Oui, il y en a quelques unes. Tout dabord, il est intressant de souligner que nous passons toujours newProxyInstance() un tableau dinterfaces seules les interfaces sont autorises, pas les classes. La restriction la plus importante est que toutes les interfaces non publiques doivent appartenir au mme package. De plus, vous ne pouvez pas avoir des interfaces dont les noms de mthode soient conflictuels (autrement dit, deux interfaces ayant des mthodes dont la signature est la mme). Il y a encore quelques nuances mineures et vous devrez donc un moment donn lire ce qui est crit en petits caractres dans la javadoc.

Q: R:

Jai entendu dire quen Java5, je navais mme plus besoin de gnrer des souches. Est-ce que cest vrai?

Q:

Pourquoi utilisez-vous des squelettes? Je croyais quon sen tait dbarrass depuis Java1.2.

Certainement. En Java 5, RMI et le Proxy dynamique sont fusionns, et les souches sont maintenant gnres dynamiquement en utilisant le Proxy dynamique. La souche de lobjet distant est une instance de java.lang. reflect.Proxy (avec un gestionnaire dinvocations) qui est gnre automatiquement et qui gre tous les dtails de la transmission des appels de mthodes locaux du client lobjet distant. En consquence, vous navez plus du tout besoin dexcuter rmic: tout ce quil faut pour que le client converse avec lobjet distant est gr de manire transparente votre place.

486

Chapitre 11

le pattern proxy

Qui fait quoi ?

Faites correspondre chaque pattern avec sa description:

Pattern Dcorateur

Description Enveloppe un autre objet et lui fournit une interface diffrente Enveloppe un autre objet et lui fournit un comportement supplmentaire Enveloppe un autre objet pour en contrler laccs Enveloppe un groupe dobjets pour simplifier leur interface

Faade

Proxy

Adaptateur

vous tes ici

487

le zoo des proxies

Le zoo des proxies


Bienvenue au zoo dObjectville! Vous connaissez maintenant les proxies distants, virtuels et de protection, mais vous rencontrerez aussi dans la nature toutes sortes de mutations de ce pattern. Ici, dans le coin Proxy de notre zoo, nous conservons une jolie collection de proxies sauvages que nous avons capturs pour que vous puissiez les tudier. Notre tche nest pas termine: nous sommes convaincus que vous verrez dautres variantes de ce pattern dans le monde rel et vous pouvez nous aider ajouter dautres proxies au catalogue. Mais jetons un coup dil la collection existante:

Proxy pare-feu contrle laccs un ensemble de ressources rseau et protge le sujet des clients malveillants.

Habitat: souvent observ aux environ pare-feu dentreprise.

s de systmes

Aidez-le trouver un habitat


Rfrence intelligente ralise des oprations supplmentaires lorsquun objet est rfrenc, telles que le comptage du nombre de rfrences.

Proxy de mise en cache permet de stocker temporairement les rsultats doprations coteuses. Il peut galement permettre plusieurs clients de partager les rsultats afin de rduire les temps de calcul ou de remdier la latence du rseau. 488
Chapitre 11

Habitat: souvent rencontr dans les serveurs web ainsi que dans les systmes de publication et de gestion de contenus.

le pattern proxy

Proxy de synchronisation permet plusieurs threads daccder de faon scurise un sujet.

Vu en train de traner dans des JavaSpaces, o il cont r le synchronis un ensemb laccs dobjets dans un environle sous-jacent nement distribu.

Aidez-le trouver un habitat

Proxy Copy-On-Write (copie-calque) contrle la copie dun objet en la diffrant jusqu ce quun client en ait besoin. Cest une variante du Proxy Virtuel.

Proxy de masquage de la complexit. Comme son nom lindique, masque la complexit dun ensemble de classes et en contrle laccs. Parfois appel Proxy Faade pour des raisons videntes. Il diffre du pattern Faade en ce que le proxy contrle laccs alors que la Faade se contente de fournir une autre interface.

Habitat: observ au voisinage du CopyOnWriteArrayList de Java5.

Notes de terrain: ajoutez ici vos observations dautres proxies ltat sauvage:

vous tes ici

489

mots-croiss

Ctait un trs LONG chapitre. Pourquoi ne pas vous dtendre en faisant un mots-croiss avant de le terminer?

1 3 6 7 10 4 8

2 5 9

11

12 13 14

15 18 19

16

17

20

21

Horizontalement 3. La classe du proxy _________ est cre au moment de lexcution. 6. Vous pouvez en afficher les pochettes. 8. Sans _______, pas de services. 10. Ce que contrle un proxy. 11. Il faut lexcuter pour enregistrer un service. 16. Toute _________ est transmise au gestionnaire dinvocations. 19. Pas vraiment rel. 20. Fournis par un serveur. 21. Cest ainsi que RMI nomme le proxy. 22. Certains constructeurs nen ont pas. 23. Manque de rendez-vous.

Verticalement 1. Dans RMI, auxiliaire du serveur. 2. Pas C++, pas C#, _______. 3. Distributeur de distributeurs. 4. Entres et sorties familires. 5. Substitut. 7. Similaire Proxy, son intention est diffrente. 9. Rencontres. 12. Remote Method __________. 13. Nous en avons fait un pour dcouvrir RMI. 14. Protge les mthodes des appels non autoriss. 15. Appeler. 17. On en voit un page 469. 18. On y rencontre des foules de proxies.

490

Chapitre 11

le pattern proxy

Votre bote outils de concepteur


Votre bote outils est presque pleine. Vous tes maintenant prt rsoudre presque tous les problmes de conception qui pourraient se prsenter.

POINTS DIMPACT

Le pattern Proxy fournit un


reprsentant un autre objet pour contrler les accs du client. Il existe de nombreuses faons de grer ces accs.

ce Encapsulez lation lhritage. Encapsu n io t la su ap nc Prfrez le phisme es, non des Polymor ac f er t in es d Programmez ions. Hritage implmentat ler vous de coup ui q Efforcezs t je ob s le faiblement nt. interagisse ouvertes doivent tre Les classeson mais fermes la lextensi ion. modificat ctions. Ne tes. des abstra s concr Dpendez pa as cl s des se dpendez . u vos amis Ne parlez q us vous pelez pas, no Ne nous aps. appelleron e seule t avoir quun oi d ne se as Une cl changer. raison de

lOO e d s e s a B OO Principesvari Abstraction qui e.

Un Proxy Distant gre les


interactions entre un client et un objet distant.

Un Proxy Virtuel contrle


laccs un objet qui est coteux instancier.

Un Proxy de protection
contrle laccs aux mthodes dun objet en fonction de lappelant.

ce cipes dans re in r p x u a e v v Pas de nou si vous fermiez le li ? ; s u e o r t chapit leriez-vous de vous rappe

Il existe de nombreuses autres


variantes du pattern Proxy, notamment des proxies de mise en cache, des proxies de synchronisation, des proxies pare-feu, des proxies copyon-write, etc.

Du point de vue structurel,


Notre nouveau pattern. Un proxy joue le rle de remplaant dun autre objet.
Proxy est similaire Dcorateur, mais leurs objectifs diffrent.

O Patterns O lation ille une ree fa it ef inm init-und esurs,

f e dsi de n uc unr pulu farcf c -e re an ee h t ie hu tn ct a a v in tg t lep r ab e e su n ae s g s a Str y c a it t s. O e ace he ir in t cn le ep a r b in d t s,tes u f t a e e n e D je g m e b n u h a a o m je it h r b n it r l n c o n o u in r p io e c e e lg p t e t f r . is u a u a t eF sd D d enD t e sq urec ic m nm dil sanlg r d r ,n e,a n iq l lo t eu n b e d rq m n sse na , in n a it m je io e e e a sna hn b p t o le b it is a y r a a n d s d u r e o e b ux ia o nd p iuix ue je ft uo sla sa io u u b qf ded qt F t jo ere ot t q sit x eic a n u r m u n e c e o r is e t d c h e d li n m c la p s n c a tmme u r t t r io s le ie o a e u n e e g t d o s j d g ie poeinco p e a , b t n r t ix n o c n a n u io la et heuor t cso n tss c la mn Str re t t ut n sso esqu r da e le a ue le un oulu rtso i oe d seca un g s rm so o psa e n if eit f in p ss t e x ul u S ot t p la n ps am n c u d io t t e in t t la se c en n a rface n m e e ic ie a r ie so t c r is m e n b st e in p e x r la a a l vare u u in e d nIl n . a F st n p t iq io it ge . a t nse t r r. Far u t t niv e ra .ef a rin era m ie a m t sa ic c n t r ve e m r nle t is m n . io p u e a b n o s o t la t c c iq u n C st e le ia t