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.
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
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
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
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
viii
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 }
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
Mandarin
CanardEnPlas
tique
Leurre
{ afficher() // aspect dun leurre }
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
ller ontro
Observateurs
te Requ
Vue
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
ard
Obj
et Ca
Obj
ris
a et Ch
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
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()
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
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
Vous tes la JVM Grer le multithread Singleton: questions et rponses Votre bote outils de concepteur Solutions des exercices
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
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
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
()
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
r fai
eM
ar
ch
er
()
faireHamburger(), faireMilkshake()
su
lta
7
Prise murale europenne
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
()
Adaptateur
interfa ce adapt
inte
e rfac
cible
xv
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
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
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
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
3 4
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
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
xvi
9
Tous les menus
MenuC
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
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
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()
Proxy requte()
InvocationHandler invoke()
xix
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.
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
Contrleur
Vous voyez la barre pulser deux fois par seconde
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.
setBPM()
getBPM
arrt() ()
La vue est informe que les BPM ont chang. Elle appelle .. getBPM() sur ltat du modle
xx
13
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...
John Vlissides
xxi
14
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
te() getSta
Menu
Visitor
Plat
e() tat tS ge
Client / vigateur
Na-
Ingredient
Ingredient
i
xxii
Index
631
Intro
Je narrive pas croire quils aient mis a dans un livre sur les design patterns!
: 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
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?
[note du marketing: ce livre est des personne possdant une carte de cr tin toute dit.]
xxiv
intro
lintro
xxv
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
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.
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
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.
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
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
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.
xxxi
diteurs techniques
Jef Cumps
Valentin Crettaz
Barney Marispini
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
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
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.
SuperCanard
Canard
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 }
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
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
voler()
// AUTRES mthodes propres un canard...
Colvert
afficher() { // aspect dun colvert }
Mandarin
afficher() { // aspect dun mandarin }
Autres typ
es de canar
d...
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
nards en Puisque les ca lent pas, vo plastique ne t redfinie cancaner() es pour couiner.
Chapitre 1
CanardEnPlastique cancaner() { // couiner } afficher () { .// canard en plastique } voler() { // redfinir pour ne rien faire }
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
Canard
Cancaneur cancaner()
Volant voler()
Leurre
Chapitre 1
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 ?!
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.
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
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 !
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
Comportements de canard
10
Chapitre 1
Co mpo
vo l
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() {
11
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 ?.
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
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 }
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.
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
13
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:
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.
14
Chapitre 1
Voyez-vous une classe qui pourrait utiliser le comportement de Cancan et qui nest pas un canard ?
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.
vo l
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
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
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
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.)
17
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!); }
public class VolerAvecDesAiles implements ComportementVol { public void voler() { System.out.println(Je vole !!); } }
public class NePasVoler implements ComportementVol { public void voler() { System.out.println(Je ne sais pas voler) } }
18
Chapitre 1
(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); } }
Excutez le code !
Fichier dition Fentre Aide Yadayadayada
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
19
Nous pouvons appeler ces mthodes chaque fois que nous voulons modifier le comportement dun canard la vole.
note de lditeur : jeu de mots gratuit
public class PropulsionAReaction implements ComportementVol { public void voler() { System.out.println(Je vole avec un racteur !); } }
20
Chapitre 1
avant
public class MiniSimulateur { public static void main(String[] args) { Canard colvert = new Colvert(); colvert.effectuerCancan(); colvert.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
Client
nager() afficher() effectuerCancan() effectuerVol()
VolerAvecDesAiles
voler() { // implmente le vol du canard }
ComportementCancan comportementCancan
chaque Pensez le de ensemb tements compor une famille comme ithmes. dalgor
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 ! }
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.
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 ?
23
le pattern Stratgie
er
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
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.
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; }
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
Paul
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 ?
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...
27
un vocabulaire commun
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
de vol
encapsu
NePasVoler
sAiles VolerAvecDe
voler() { du canard ente le vol // implm }
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
Coincoin
lastique
Cancan
{ cancaner() ement ente le cancan // implm }
CanardEnP
Mandarin
t CanardMue
Leurre
{ afficher() } dun leurre // 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
Con
trolle
te Requ
Vue
Q:
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:
29
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
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.
31
POINTS DIMPACT
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
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
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.
Tout au long de ce livre, rflchissez la faon dont les patterns sappuient sur les concepts de base et les principes OO.
On essaie souvent
dextraire ce qui varie dun systme et de lencapsuler.
vront !
32
Chapitre 1
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.
33
classe abstraite
Personnage
ComportementArme arme ; combattre(); setArme(ComportementArme a) { this.arme = a; }
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
ComportementPoignard
ComportementHache
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
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
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 !
nouveau chapitre
37
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
le pattern observateur
uelles est lun Conditions act hages possibles. des trois affic ut galement Lutilisateur pestatistiques et demander des des prvisions.
Capteur dhumidit
affiche
Conditions actuelles
Temp: 22 Hygro: 60 Pression:
Capteur de temprature
Station mto
Capteur de pression
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.
39
la classe DonneesMeteo
DonneesMeteo
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 ?
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.
Affichage Deux
Affichage Un
Prvisions
TT T
Affichage Trois
?
Affichages futurs
41
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.
42
Chapitre 2
le pattern observateur
public void actualiserMesures() { float temp = getTemperature(); float humidite = getHumidite(); float pression = getPression();
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.
43
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.
44
Chapitre 2
le pattern observateur
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
45
Ob
je
jet Chat
rd Ob jet Cana
Ob
is jet Sour
Observateurs
bj et Sujet
Ob
int
Ob
n jet Chie
rd jet Cana
Ob
jet Chat
Ob
is jet Sour
Observateurs
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
bj et Sujet
int
Ob
n jet Chie
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
14
14 14 14
Ob Ob
n jet Chie
bj et Sujet
int
rd jet Cana
Ob
jet Chat
Ob
is jet Sour
Observateurs
47
comdie express
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
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 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.
Ob
jet Chat
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
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
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.
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.
Q: R:
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
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
54
Chapitre 2
le pattern observateur
Ann
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
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
AffichageConditions
suje t
AffichageTiers
actualiser() afficher() { // afficher autre chose en fonction des mesures }
AffichageStats
actualiser() afficher() { // afficher moyenne, minimum et maximum }
AffichagePrevisions
actualiser() afficher() { // afficher la prvision }
Les dveloppeurs peuvent implmenter les interfaces Sujet et Observateur pour crer leur propre affichage.
le pattern observateur
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
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.
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
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); } }
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(); }
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
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.
AffichageConditions implements Observateur, Affichage { float temperature; float humidite; Sujet donneesMeteo; ttons
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
fiche simplement les mesures La mthode afficher() af les plus rcentes. de temprature et dhumidit
Q: R:
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
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();
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);
%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
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
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 :
%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 %
61
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.
63
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
soit notifyObservers()
soit
notifyObservers(Object arg)
version, Dans cette n ne un objet doest transmis arbitraire servateur chaque ob otification. lors de la n
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
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 :
) La mthode setChanged( positionne un drapeau changed true. tifie les notifyObservers() ne no r du va observateurs que si la leu drapeau est TRUE.
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
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.
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
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.
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) ;
(); 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;
69
excuter le test
%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 %
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 ?
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.
71
observateur et swing
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.
rface.
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 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.
73
POINTS DIMPACT
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
Principes OO
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).
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.
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 !
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.
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
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
Le frigo
servable; import java.util.Ob server; import java.util.Ob
observable.addO bserver(this);
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
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.
Colombia
cout()
cout()
Sumatra
cout()
Deca
cout()
Espresso
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()
ColombiaPlusLaitEtChocolatEtCaramel
cout()
cout()
SumatraPlusCaramel EtChocolat
EspressoPlusLait EtCaramel
cout()
EspressoPlusLait
cout()
SumatraPlusChantilly
DecaPlusChantilly
EspressoPlusLait
cout()
cout()
cout() SumatraPlusCaramel
DecaPlusChantillyEtChocolat
EspressoWithMocha
EspressoWithSteamedMilk DecaPlusChantillyEtChocolat andSoy
cout()
SumatraPlusLaitEtChantilly
cout()
cout() DecafWithMocha
cout()
cout()
ColombiaPlusChantillyEtCaramel cout()
cost() ColombiaPlusCaramel
cout()
DecaPlusLait
cout()
cost() EspressoPlusLaitEtVanille
DarkRoastWithSoy SumatraPlusLait
cout()
cost() DecaPlusCaramelEtChocolat
DarkRoastWithSoy
cost()
cout()
cout()
cost() EspressoPlusLaitEtChocolat
cout()
SumatraPlusChantillyEtChocolat
cout()
DecafWithWhip cout()
EspressoChantillyEspressoChantilly
SumatraPlusLait
SumatraPlusChocolat cost()
cout()
DecaPlusCaramelEtChocolat
EspressoPlusChantillyEtCaramel
cout()
cost()
DecaPlusLaitEtChantilly cost()
cout()
() calcule le cot Chaque mthode cout autres ingrdients du caf plus celui des de la commande.
81
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.
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
crivez les mthodes cout() pour les classes suivantes (du pseudo Java suffit) :
public class Sumatra extends Boisson { public Sumatra() { description = Dlicieux et cors;
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.
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.
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.
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
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
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
elons Tout dabord, nous app r le plus cout() sur le dcorateu . illy ant Ch externe,
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 .
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
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
Le ComposantConcret est lobjet auquel nous allons ajouter dynamiquement un nouveau comportement. Il drive de Composant.
methodeA() methodeB()
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
une Le DecorateurConcret a lobjet variable dinstance pour nt que le quil dcore (le Composa Dcorateur enveloppe).
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
Boisson
description getDescription() cout() // autres mthodes
composant
Colombia
cout()
cout()
Sumatra
DecorateurIngredient
getDescription()
Espresso
cout()
Deca
cout()
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...
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
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
OK, Jai besoin de vous pour faire un double chocolat, caramel et chantilly.
1,29 0,10
cout()
cout()
0,20
0,99 Sumatra
cout()
Chantilly
6 Chantilly ajoute son total, 10 centimes, au rsultat de Chocolat et retourne le rsultat final 1,29 .
Chocolat
5 Chocolat ajoute son cot, 20 centimes, au rsultat de Sumatra et retour ne le nouveau total, 1,19 .
Starbuzz Coffee
0,89 0,99 1,05 1,99 0,10 0,20 0,15 0,10
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
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...
95
public class Espresso extends Boisson { public Espresso() { description = Espresso; } public double cout() { return 1.99; } }
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
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.
97
public class StarbuzzCoffee { public static void main(String args[]) { Boisson boisson = new Espresso(); System.out.println(boisson.getDescription() + + boisson.cout());
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());
} }
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:
99
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
mposant abst
rait
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.
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.
101
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 !
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
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
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
POINTS DIMPACT
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
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.
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
105
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
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.
107
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();
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
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
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?
111
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.
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; }
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
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.
113
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
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; } }
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
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
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).
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
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
ab r
iqueDeP
Piz
qu
eD ePizzas
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.
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.
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.
attendez dun Ce nest pas ce que vous ulez PAS bon franchis. Vous ne vo pizzas. savoir ce quil met sur ses
119
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...
120
Chapitre 4
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) { 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(); }
121
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
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; } }
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?
123
mthode de fabrique
124
Chapitre 4
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.
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
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
Michel doit commander sa pizza une PizzeriaStrasbourg. La mthode de commande est la mme, mais la pizza sera diffrente!
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
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
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);
127
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
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);
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 redfinit galement la mthode couper() afin de dcouper des parts carres.
129
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); }
% 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
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 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
131
crateurs et produits
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.
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
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
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!
133
fabrication: dfinition
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
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
Q: R:
questions stupides
Il ny a pas de
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:
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
nombre
seille
137
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
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.
139
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
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
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...
141
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
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.
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
Pte
Poivrons
Fromage
Lgumes
Sauce
Nous avons les mmes familles de produits (pte, sauce, fromage, lgumes, etc) mais des implmentations diffrentes selon la rgion.
Pizza vgtarienne
Pizza poivrons
144
Chapitre 4
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
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
Si nous avions un mcanisme commun implmenter dans chaque ions instance de fabrication, nous aur pu crer la place une classe abstraite...
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
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(); } }
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
147
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
public abstract class Pizza { String nom; Pate pate; Sauce sauce; Legume legume[]; Fromage fromage; Poivrons poivrons; Moules moules; abstract void preparer();
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 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
149
public }
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(); }
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
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.
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(); }
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
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
Comparez cette version de la mthode creerPizza() avec celle de limplmentation de Fabrication que nous avons vue plus haut dans ce chapitre.
152
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.
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
153
La premire partie du processus de commande na pas chang dun iota. Revoyons la commande de Luc:
Maintenant que nous avons une boutique, nous pouvons prendre une commande:
pizzeriaBrest.commanderPizza(fromage);
zz eriaBres
154
Chapitre 4
pi
partir de l, les choses changent, parce que nous utilisons une fabrique dingrdients
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()
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
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
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 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
157
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?
Interview
Linterview de cette semaine:
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!
159
Pizzeria
creerPizza()
PizzeriaBrest
creerPizza()
PizzeriaStrasbourg
creerPizza()
La Fabrication
Boutique de Strasbourg
Voici le produit de la Pizzeria. Les clients ne sappuient que sur ce type abstrait.
Pizza
PizzaFromageStyleBrest PizzaPoivronsStyleBrest
PizzaFruitsDeMerStyleBrest
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
<<interface>> FabriqueIngredientsPizza
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...
<<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
POINTS DIMPACT
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
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.
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.
162
Chapitre 4
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.
163
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; } }
164
Chapitre 4
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
} 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; } }
nombre
12
avec Marseille
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
167
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
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
new MonObjet();
Et si un autre objet voulait crer un MonObjet? Pourrait-il de nouveau appeler new sur MonObjet?
Tant que nous avons une classe, pouvons-nous toujours linstancier une ou plusieurs fois?
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.
Et cela signifie?
Je suppose que cest une classe qui ne peut pas tre instancie parce quelle a un constructeur priv.
Euh, je pense que le code de MaClasse est le seul qui pourrait lappeler. Mais cela na pas beaucoup de sens.
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();
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(); } }
MyClass.getInstance();
Pouvez vous terminer le code de sorte quUNE SEULE instance de MaClasse soit jamais cre? 172
Chapitre 5
le pattern singleton
.
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
Si uniqueInstance ntait pas null, cest quelle avait t cre prcdemment. Nous allons directement linstruction return.
vous tes ici
173
Interview
Linterview de cette semaine :
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; }
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.
175
un bouilleur singleton
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?
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 garantit quune classe na quune seule instance et fournit un point daccs global cette instance.
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.
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...
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
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
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();
}
Thread Un
Thread Deux
Valeur de uniqueInstance
return uniqueInstance;
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
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.
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.
181
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
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 !
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
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 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.
185
Principes OO
Bases de lOO
Abstraction n Encapsulatio
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
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
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.
187
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
null
<object1>
<object1>
<object2>
Deux objets diffrents sont retourns! Nous avons deux instances de BouilleurChocolat!!!
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
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.
189
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
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.
193
ControleAppareil
marche() arret()
Stereo
marche()
Plafonnier
marche() arret() attenuer()
TV
marche() arret() selectionnerCanal() setVolume()
LumiereExterieur
marche() arret()
ControleRobinet
valeurOuvert() valeurFerme()
Ventilateur
rapide()
Jacuzzi
bouillonner()
LumiereJardin
setNuit() setJour() modeManuel() modeAuto()
PorteGarage
ouvrir() fermer() stop() lampeAllumee() lampeEteinte()
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
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!
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
Caftria dObjectville
s visite Rendez-nou
2 La Serveuse 1
votre repas.
vous tes ici
197
la caftria
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
pas
ser
Co
mm
and
e()
rt pa D
p re n
d re C
omm
ande
()
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
re fai
faireHamburger(), faireMilkshake()
su
lta
le pattern Commande
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.
La serveuse et moi, on est dcoupls, vous pouvez le dire. Elle nest mme pas mon type!
199
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...
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
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
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
fa
ire
faireHa mburg
ar
ch
er
()
200
Chapitre 6
rsu
lta
le pattern Commande
fournit Lobjet Commandeuter(), une mthode, execactions qui encapsule des ur les et quon appelle po epteur. invoquer sur le Rc
c re a
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() Re
cepteur
201
Reliez les objets et les mthodes de la Caftria leurs homologues dans le pattern Commande.
202
Chapitre 6
le pattern Commande
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
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( ).
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();
} }
204
Chapitre 6
le pattern Commande
PorteGarage
ouvrir() fermer() stop() lampeAllumee() lampeEteinte()
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
205
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
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()
Receiver action()
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.
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
Comment la conception du pattern Commande permet-elle de dcoupler linvocateur de la requte de son rcepteur?
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
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
P rir Ouv
ma Com
ge or teGara
executer()
executer()
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
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
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.
211
tester la tlcommande
212
Chapitre 6
le pattern Commande
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.
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
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.
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
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
215
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(); }
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(); } }
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(); } }
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
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.
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 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
219
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
220
Chapitre 6
le pattern Commande
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.
Il nous reste trois commandes crire: lent, moyen et arrt. Voyez-vous comment elles sont implmentes?
221
tester le ventilateur
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!
------- 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
------ 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
223
macro-commandes
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
d la tlcommande excute la macro, ces commandes sont toutes excutes une une.
224
Chapitre 6
le pattern Commande
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.
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);
Affecter la macrocommande un bouton comme nous le ferions pour tout autre commande.
225
Voici le rsultat
% 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
--- 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 %
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
Commandes
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()
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
T h re a d
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
Commande
<<interface
>>
store
re sto
sto re
om m
ande
un e
2. executer()
In vocateu
de
3. ex ec ute r()
Co mmande
executer() stocker() charger()
ux
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
r rge cha
om m
C
C
charger
executer() stocker() charger()
1. ex ec ute r()
2. executer()
In vocateu
229
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
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.
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
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.
231
Reliez les objets et les mthodes de la Caftria leurs homologues dans le pattern Commande.
232
Chapitre 6
le pattern Commande
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(); } } }
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
233
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?
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
ose une enne exp ant. p o r u e murale u cour La prise e pour obtenir d interfac
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
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 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
Nouveau 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:
nos canards Cette fois, t une interface implmenten leur permet de Canard qui cancaner. voler et de
Les d
public interface Dindon { public void glouglouter(); public void voler(); }
ncanent pa indons ne ca
s, ils glouglou
tent.
238
Chapitre 7
le pattern Adaptateur
tion concrte Voici une implmentale canard, ce de Dindon; comme dafficher ses dindon se contente actions.
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);
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().
%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
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
Adapt Client
requ ete()
req
uet
eTr
adu
) ite(
Adaptateur
interf ac adapt e
r inte
face
cibl
e
LAdaptateur implmente linterface cible et contient une instance 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.
241
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:
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 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()
Adapt
requeteSpecifique()
243
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
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.
245
solution de lexercice
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()
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()
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 :
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
247
Enumera
<<interface>> Enumeration
hasMoreElements() nextElement()
e interf tion a un
ace simple
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
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.
<<interface>>
Enumeration hasMoreElements() nextElement()
249
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).
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?
251
Face face :
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.
253
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()
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...
255
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:
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!
ecran.baisser(); projecteur.marche(); projecteur.setEntree(dvd); projecteur.modeGrandEcran(); amp.marche(); amp.setDvd(dvd); amp.setSonSurround(); amp.setVolume(5); dvd.marche(); dvd.jouer(film);
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...
257
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().
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()
LecteurDvd
amplificateur marche() arret() ejecter() pause() jouer() jouer() setAudioSurround()
jouer
()
LecteurCd
amplificateur
SetAudioStereo() stop()
Ecran
monter()
baisser()
Projecteur
lecteurDvd
MachineAPopcorn
marche() arret() eclater()
Lumieres
marche() arret() attenuer()
marche
()
258
Chapitre 7
le pattern Adaptateur
regard er
Film()
REG ARD ERF ILM ARR [] ETE RFI LM []
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.
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.
259
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.
Pas ncessairement. Le pattern permet sans quivoque de crer un nombre quelconque de faades pour un soussystme donn.
Chapitre 7
le pattern Adaptateur
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.
un instant...
261
implmenter la faade
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
Chapitre 7
le pattern Adaptateur
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?
%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
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
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
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.
265
principe de conception
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
Ici, nous obtenons lobjet thermomtre de la station, puis nous appelons la mthode getTemperature() nous-mmes.
Avec le Principe
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
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 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:
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:
267
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(); } }
A
268
Connaissez-vous un emploi courant de Java qui viole le principe Ne parlez pas aux inconnus? Est-ce important?
Chapitre 7
le pattern Adaptateur
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()
LecteurDvd
amplificateur marche() arret() ejecter() pause() jouer() jouer() setAudioSurround()
LecteurCd
amplificateur
SetAudioStereo() stop()
Ecran
monter()
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()
Lumieres
marche() arret() attenuer()
269
POINTS DIMPACT
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
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.
271
Imaginez que nous ayons galement besoin dun Adaptateur qui convertit un Canard en Dindon. Appelons-le AdaptateurCanard. crivez cette classe:
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(); } } }
un objet alatoire;regardez la mthode voler() pour voir comment elle est utilise.
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
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(); } }
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
Motivation Convertit une interface en une autrer Ne modifie pas linterface mais ajoute une responsabilit Simplifie une interface
273
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
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
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
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
276
Chapitre 8
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.
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 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.
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
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.
279
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
Cafe
suivreRecette() filtrerCafe() ajouterLaitEtSucre()
The
suivreRecette() tremperSachet() ajouterCitron()
A
280
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
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
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
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:
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
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); } }
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.
283
Tracez le nouveau diagramme de classes, maintenant que nous avons transfr limplmentation de suivreRecette() dans la classe BoissonCafeinee.
284
Chapitre 8
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
il li r de l e au
c af s u ne t a s se
Ve rser
c af dan
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
3 4
se The Sous-clas
Sous-cla
sse Cafe
2 4
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
285
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();
} 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
Bien. Dabord, il nous faut un objet The... The monThe = new The();
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
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();
287
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 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 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.
289
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
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.
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.
291
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
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();
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 %
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:
Q: R:
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.
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.
Un composant de bas niv eau nappelle jamais un composa de haut niveau directeme nt nt.
296
Chapitre 8
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.
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 .
pellent The et Cafe nap straite jamais la classe ab avoir directement sans d. t appeles dabor
297
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?
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.
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
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.
299
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) {
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
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().
301
implmenter Comparable
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; }
Restons simples: nos canards se contente dafficher leur nom et leur poids! Voil ce dont sort() a besoin...
nt
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; } } }
302
Chapitre 8
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) };
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]); } } }
%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 %
303
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++){
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
Si les Canards ne sont pas dans lordre, il sont permuts grce la mthode concrte swap() de la classe Arrays: swap()
sort() swap()
La mthode sort() continue comparer et permuter les Canards jusqu ce que le tableau soit dans lordre correct!
Chapitre 8
304
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:
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
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?
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.
305
ladaptateur paint()
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
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
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
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
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
Principes OO
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.
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
311
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
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
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
313
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...
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
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.
317
deux menus
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];
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
et
320
Chapitre 9
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();
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.
321
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
Pour parcourir les plats du brunch, nous appelons les mthodes size() et get() sur lArrayList :
ArrayList
Plat
Plat
Plat
Plat
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 ]
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
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
<<interface>> Iterateur
encore() suivant()
La mthode encore() nous indique sil reste des lments parcourir dans lagrgat.
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...
325
crer un itrateur
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:
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.
{ 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.
327
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.
328
Chapitre 9
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.
329
Gnial! Rien modifier dans le code sauf lajout dune mthode creerIterateur().
Crpe vgtarienne
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
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
331
amliorer litrateur
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
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
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; } } }
333
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:
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 }
334
Chapitre 9
Voici notre nouvelle interface Menu. Elle spcifie la nouvelle mthode, creerIterateur().
<<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
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
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()
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.
337
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.
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
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.
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.
339
responsabilits multiples
A
feu()
Personne
Jeu
connecter() quitter() bouger() pause()
Telephone
numeroter() raccrocher() parler() emettreDonnees() clignoter()
getEtat() getPosition()
Iterateur PaquetDeCartes
hasNext() next() remove() ajouterCarte() supprimerCarte() battre()
PanierDAchat
ajouter() supprimer() payer() ajouterASelection()
A
quitter() feu() pause()
connecter() bouger()
Joueur
getScores() getNom()
getScores() getNom()
340
Chapitre 9
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.
341
un nouveau menu
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.
1. 2. 3.
342
Chapitre 9
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).
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
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
public class TestMenu { public static void main(String args[]) { MenuCreperie menuCreperie = new MenuCreperie(); MenuCafeteria menuCafeteria = new MenuCafeteria(); MenuBrasserie menuBrasserie = new MenuBrasserie();
menus.
% 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
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 %
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
Plat
Plat
Plat
Plat
next()
... 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
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.
LinkedList Vector
Me nuItem
Me nuItem
Me nuItem
Men
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
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:
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 :
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.
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.
350
Chapitre 9
}
}
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.
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?
351
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.
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()); } } }
Lide semble bonne. Bien sr, nous avons perdu les noms des menus, mais rien ne nous empche den ajouter.
352
Chapitre 9
reperie
Di ner nu Me
en
uBrasseri
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.
On ne peut pas affecter un menu de desserts un tableau de plats. Il faut modifier quelque chose!
vous tes ici
353
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
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
ia
en u Cafeter
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
nu C
en u Cafeter
en
e M
u D s se r e
P l at
P l at
355
ce.
Feuille
Feuille
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.
Menu
Plat
Pla t
Plat
s et Les menus sont des nud . les uil fe s de les plats sont
356
Chapitre 9
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
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
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)
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:
358
Chapitre 9
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 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.
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
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()); } }
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.
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.
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.
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.
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:
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
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:
Composite
To
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
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(); } }
365
Excuter le test...
Fichier dition Fentre Aide OmeletteAuxFinesHerbes
% 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.
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.
367
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
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(); } }
Et maintenant, le Plat...
368
Chapitre 9
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(); } }
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.
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
A
}
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() ; }
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.
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.
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.
372
Chapitre 9
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.
Nous avons implment estVegetarien() pour que les Menus lancent toujours une exception. Si cela se produit, nous interceptons l exception mais nous poursuivons litration.
373
% 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.
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.
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.
377
mots-croiss
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
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
379
POINTS DIMPACT
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
Il convient de sefforcer
de naffecter quune seule responsabilit chaque classe.
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
380
1. implmenter linterface Menu 2. nous dbarrasser de getPlats() 3. ajouter creerIterateur() et retourner un itrateur capable de parcourir les valeurs de la table.
381
}
public boolean hasNext() {
null) { if (position >= plats.length || plats[position] == return false; } else { return true; }
}
public Object next() {
}
public void remove() {
382
Chapitre 9
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
383
solution de 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
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
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
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
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...
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...
387
Pas de pice
A une pice
Puis crons une variable dinstance pour contenir ltat courant et dfinissons des valeurs pour chaque tat:
Nous recherchons maintenant toutes les actions qui peuvent se produire dans le systme.
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) {
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); } }
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.
389
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