Académique Documents
Professionnel Documents
Culture Documents
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 (etat == A_PIECE) { System.out.println(Vous ne pouvez plus insrer de pices); client ; } else if (etat == SANS_PIECE) { sinon, nous acceptons la pice etat = A_PIECE; et dclenchons une transition System.out.println(Vous avez insr une pice); vers ltat A_PIECE. } else if (etat == EPUISE) {
System.out.println(Vous ne pouvez pas insrer de pices, nous sommes en rupture de stock); } else if (etat == VENDU) { System.out.println(Veuillez patienter, le bonbon va tomber); } } } Si le client vient dacheter un bonbon, et si la machine est en rupture
enons Maintenant, nous comm ns sous tio ac implmenter les forme de mthodes....
il doit attendre que la transaction soit termine avant dinsrer une autre pice.
390
Chapitre 10
le pattern tat public void ejecterPiece() { ... Maintenant, si le client essaie de rcuprer sa pice if (etat == A_PIECE) { pi Sil y a une ce, nous la lui System.out.println(Pice retourne); rendons et nous retournons etat = SANS_PIECE; ltat SANS_PIECE. } else if (etat == SANS_PIECE) { System.out.println(Vous navez pas insr de pice); Sinon, sil ny en a pas, nous } else if (etat == VENDU) { pouvons pas la lui rendre. System.out.println(Vous avez dj tourn la poigne); } else if (etat == EPUISE) { System.out.println(jection impossible, vous navez pas insr de pice); } }
ne
Vous ne pouvez pas jecter: puisquil ny a pas de bonbons, la machine naccepte pas de pices!
public void tournerPoignee() { Quelquun essaie de tricher if (etat == VENDU) { System.out.println(Inutile de tourner deux fois!); Il faut dabord } else if (etat == SANS_PIECE) { System.out.println(Vous avez tourn mais il ny a pas de pice); une pice } else if (etat == EPUISE) { System.out.println(Vous avez tourn, mais il ny a pas de bonbons); Nous ne pouvons pas } else if (etat == A_PIECE) { dlivrer de bonbons, System.out.println(Vous avez tourn...); etat = VENDU; il ny en a pas. delivrer(); Victoire! Le client va avoir un } bonbon. On passe ltat VENDU et } dlivrer un bonbon
Appele pour
public void delivrer() { if (etat == VENDU) { System.out.println(Un bonbon va sortir); nombre = nombre - 1; if (nombre == 0) { System.out.println(Ae, plus de bonbons!); etat = EPUISE; } else { etat = SANS_PIECE; } } else if (etat == SANS_PIECE) { System.out.println(Il faut payer dabord); } else if (etat == EPUISE) { System.out.println(Pas de bonbon dlivr); } else if (etat == A_PIECE) { System.out.println(Pas de bonbon dlivr); } } // autres mthodes comme toString() et remplir() }
on appelle la mthode delivrer(). s Nous sommes dan on ltat VENDU: on! lui donne le bonb ition raitons la cond Voici o nous t ons. Si ctait le plus de bonb sitionnons ltat de dernier, nous po UISE; sinon nous la machine EP SANS_PIECE. revenons ltat
Rien de ceci ne devrait arriver. Mais si cest le cas, nous retournons une erreur, non un bonbon.
391
tester le distributeur
Test maison
Ne dirait-on pas une bonne petite conception bien solide sappuyant sur une mthodologie bien pense? Faisons-lui subir un petit test maison avant de la livrer Distribon pour quils chargent le code dans leurs distributeurs. Voici le code:
public class TestDistributeur { public static void main(String[] args) { Distributeur distributeur = new Distributeur(5); System.out.println(distributeur); distributeur.insererPiece(); distributeur.tournerPoignee(); System.out.println(distributeur); distributeur.insererPiece(); distributeur.ejecterPiece(); distributeur.tournerPoignee(); System.out.println(distributeur); distributeur.insererPiece(); distributeur.tournerPoignee(); distributeur.insererPiece(); distributeur.tournerPoignee(); distributeur.ejecterPiece(); System.out.println(distributeur); distributeur.insererPiece(); distributeur.insererPiece(); distributeur.tournerPoignee(); distributeur.insererPiece(); distributeur.tournerPoignee(); distributeur.insererPiece(); distributeur.tournerPoignee(); System.out.println(distributeur); } }
Afficher ltat de la machine. Insrer une pice... Tourner la poigne; nous devrions recevoir notre bonbon.
Afficher de nouveau ltat de la machine Insrer une pice... Demander la rcuprer.. Tourner la poigne. Nous ne devons pas avoir de bonbon.. Afficher de nouveau ltat de la machine.
Insrer une pice.... Tourner la poigne; nous devrions recevoir notre bonbonl Insrer une pice... Tourner la poigne; nous devrions recevoir notre bonbon Demander la pice que nous navons pas insre.
Afficher de nouveau ltat de la machine. Insrer deux pices... Tourner la poigne; nous devrions recevoir notre bonbon.
Maintenant, le test de charge... Afficher encore une fois ltat de la machine.
392
Chapitre 10
le pattern tat
%java TestDistributeur Distribon, SARL. Distributeur compatible Java, modle 2004 Stock: 5 bonbons Lappareil attend une pice Vous avez insr une pice Vous avez tourn... Un bonbon va sortir Distribon, SARL. Distributeur compatible Java, modle 2004 Stock: 4 bonbons Lappareil attend une pice Vous avez insr une pice Pice retourne Vous avez tourn mais il ny a pas de pice Distribon, SARL. Distributeur compatible Java, modle 2004 Stock: 4 bonbons Lappareil attend une pice Vous avez insr une pice Vous avez tourn... Un bonbon va sortir Vous avez insr une pice Vous avez tourn... Un bonbon va sortir Vous navez pas insr de pice Distribon, SARL. Distributeur compatible Java, modle 2004 Stock: 2 bonbons Lappareil attend une pice Vous avez insr une pice Vous ne pouvez plus insrer de pices Vous avez tourn... Un bonbon va sortir Vous avez insr une pice Vous avez tourn... Un bonbon va sortir Ae, plus de bonbons ! Vous ne pouvez pas insrer de pice, nous sommes en rupture de stock Vous avez tourn, mais il ny a pas de bonbons Distribon, SARL. Distributeur compatible Java, modle 2004 Stock: 0 bonbons Lappareil est en rupture de stock
393
ajoutons un jeu
Nous pensons que transformer lachat de bonbons en jeu dbouchera sur une augmentation significative des ventes. Nous allons placer lun de ces autocollants sur chaque distributeur. Heureusement que nous avons adopt Java. Cela va tre facile, non?
, quand 10% du temps la le client tourneit deux poigne, il reo dun. bonbons au lieu Bonbons
le pattern tat
Problme de conception
Tracez un diagramme dtats du Distributeur qui gre le concours. Dans ce concours, 10% du temps, ltat Vendu conduit librer deux bonbons au lieu dun. Comparez votre rponse la ntre (en fin de chapitre) pour tre sr que nous sommes daccord avant de poursuivre...
Distribon, SARL.
Le monde dans lequel les distributeurs ne sont jamais vides
395
tat GAGNANT.
public void insererPiece() { // code pour insrer une pice } public void ejecterPiece() { // code pour jecter la pice } public void tournerPoignee() { // code pour tourner la poigne } public void delivrer() { // code pour librer le bonbon }
uctions .. mais alors, il faudrait ajouter de nouvelles instr t lta r gre pour ode mth conditionnelles dans chaque GAGNANT. Cela fait beaucoup de code modifier.
tournerPoignee() va tre particulirement dlicate, car vous allez devoir ajouter du code pour tester si vous avez un GAGNANT, puis passer soit ltat GAGNANT soit ltat VENDU.
E. Nous navons pas encapsul ce qui varie. F. Les ajouts ultrieurs sont susceptibles de
provoquer des bogues dans le code.
396
Chapitre 10
le pattern tat
Bon, cela ne va pas. Je pense que notre premire version tait bonne, mais quelle ne va pas tenir la route si Distribon continue demander de nouveaux comportements. Le taux de bogues va endommager notre image, sans parler du fait que le PDG va nous rendre fous.
Guy : Tout fait daccord ! Il faut remanier ce code pour quil soit facile maintenir et modifier. Line: Il faut vraiment essayer disoler le comportement de chaque tat, de sorte que si nous en modifions un, nous ne risquions pas de mettre la pagaille dans le reste du code. Guy : Bien, autrement dit appliquer le bon vieux principe encapsuler ce qui varie. Line: Exactement. Guy : Si nous plaons le comportement de chaque tat dans sa propre classe, chaque tat nimplmentera que ses propres actions. Line: Tout fait. Et peut-tre que le Distributeur peut simplement dlguer lobjet tat qui reprsente ltat courrant. Guy : Ah, bravo: prfrer la composition... encore un bon principe luvre. Line: Oui. Eh bien, je ne suis pas sre 100% de la faon dont cela va fonctionner, mais je crois quon est sur la bonne piste. Guy : Je me demande si cela facilitera lajout de nouveaux tats. Line: Je pense que oui... Nous devrons toujours modifier le code, mais la porte des changements sera beaucoup plus restreinte, parce que lajout dun nouvel tat se limitera linsertion dune nouvelle classe et peut-tre la modification de quelques transitions ici et l. Guy : Voil qui me plat bien. Attaquons-nous cette nouvelle conception!
397
La nouvelle conception
On dirait que nous avons un nouveau plan: au lieu de maintenir notre code existant, nous allons le retravailler pour encapsuler les objets tat dans leur propre classe, puis dlguer ltat courant quand une action a lieu. Comme nous appliquons nos principes de conception, nous devrions aboutir une solution plus facile maintenir en aval. Voici comment nous allons procder:
1
Tout dabord, nous allons dfinir une interface Etat qui contiendra une mthode pour chaque action lie au Distributeur. Ensuite, nous allons implmenter une classe pour chaque tat de la machine. Ces classes seront responsables du comportement du distributeur quand il se trouvera dans ltat correspondant. Enfin, nous allons nous dbarrasser de toutes nos instructions conditionnelles et les remplacer par une dlgation la classe tat qui travaillera notre place.
Non seulement nous appliquons les principes de conception, mais, comme vous allez le voir, nous implmentons de fait le pattern tat. Mais nous ne nous occuperons pas de la dfinition officielle du pattern avant davoir retravaill notre code...
Maintenant, nous allons placer tout le comportement de ltat dans une classe. Ainsi, nous isolons le comportement et nous rendons le code plus facile maintenir et comprendre.
398
Chapitre 10
le pattern tat
Puis nous encapsulons chaque tat de notre conception dans une classe qui implmente linterface Etat.
<<interface>>
Etat
insererPiece() ejecterPiece() tournerPoignee() delivrer()
Pour savoir quels tats il nous faut, nous nous reportons au code prcdent...
EtatVendu
insererPiece() ejecterPiece() tournerPoignee() delivrer()
EtatEpuise
insererPiece() ejecterPiece() tournerPoignee() delivrer()
EtatSansPiece
insererPiece() ejecterPiece() tournerPoignee() delivrer()
EtatAPiece
insererPiece() ejecterPiece() tournerPoignee() delivrer()
public class Distributeur { final final final final static static static static int int int int VENDU = 3; EPUISE = 0; SANS_PIECE = 1; A_PIECE = 2;
Noubliez pas quil va nous falloir aussi un tat e gagnant qui implmente galement linterfac Etat. Nous y reviendrons aprs avoir rimplment la premire version du Distributeur.
EtatGagnant insererPiece() ejecterPiece() tournerPoignee() delivrer()
399
Pour implmenter nos tats, nous devons dabord spcifier le comportement des classes quand chaque action est appele. Annotez le diagramme cidessous en inscrivant le comportement de chaque action dans chaque classe. Nous en avons dj indiqu quelques-uns pour vous.
EtatSansPiece
insererPiece() ejecterPiece() tournerPoignee() delivrer()
EtatAPiece
insererPiece() ejecterPiece()
Aller EtatVendu
tournerPoignee() delivrer()
Dire au client de patienter car nous sommes en train de lui donner son bonbon
EtatVendu
insererPiece() ejecterPiece() tournerPoignee() delivrer()
Dlivrer un bonbon. Vrifier le nombre de bonbons. Sil est suprieur zro, aller EtatSansPiece, sinon aller EtatEpuise
EtatEpuise
EtatGagnant
insererPiece() ejecterPiece() tournerPoignee() delivrer()
le pattern tat
public class EtatSansPiece implements Etat { Distributeur distributeur; public EtatSansPiece(Distributeur distributeur) { this.distributeur = distributeur; } public void insererPiece() { System.out.println(Vous avez insr une pice); distributeur.setEtat(distributeur.getEtatAPiece()); }
On transmet une rfrence au Distributeur dans le constructeur et on la place dans une variable dinstance.
Si quelquun insre une pice, nous affichons un message indiquant quelle a t accepte et nous passons dans ltat EtatAPiece.
Vous allez voir dans une seconde comment ceci fonctionne... Vous ne pouvez pas reprendre votre pice: vous ne nous lavez pice); jamais donne!
Et si vous ne payez pas, vous ne pouvez pas avoir de bonbon.
public void ejecterPiece() { System.out.println(Vous navez pas insr de pice); } public void tournerPoignee() { System.out.println(Vous avez tourn, mais il ny a pas de } public void delivrer() { System.out.println(Il faut payer dabord); } }
Quavons-nous fait? Nous avons implment les comportements qui conviennent ltat dans lequel nous sommes. Dans certains cas, cela implique une transition du Distributeur vers un nouvel tat.
401
Retravaillons le distributeur
Avant de terminer les classes Etat, nous allons retravailler le Distributeur pour que vous voyiez comment tout cela sarticule. Nous commencerons par les variables dinstance et nous abandonnerons les entiers pour adopter des objets tat:
public class Distributeur { final final final final static static static static int int int int EPUISE = 0; SANS_PIECE = 1; A_PIECE = 2; VENDU = 3;
in asse Distributeur, af cl la ns iso al tu ac s s de ou e N les classes la plac ilaire dutiliser les nouvelL sim s tr t es code e s. ue iq at st s s des er ti en dans un cas nous avon : s pr n io pt ce ex e un des objets... entiers, dans lautre
Ancien code
public class Distributeur { Etat Etat Etat Etat etatEpuise; etatSansPiece; etatAPiece; etatVendu;
Nouveau code
402
Chapitre 10
le pattern tat
Etat etat = etatEpuise; int nombre = 0; public Distributeur(int nombreBonbons) { etatEpuise = new EtatEpuise(this); etatSansPiece = new EtatSansPiece(this); etatAPiece = new EtatAPiece(this); etatVendu = new EtatVendu(this); this.nombre = nombreBonbons; if (nombreBonbons > 0) { etat = etatSansPiece; } } public void insererPiece() { etat.insererPiece(); } public void ejecterPiece() { etat.ejecterPiece(); } public void tournerPoignee() { etat.tournerPoignee(); etat.delivrer(); } void setEtat(Etat etat) { this.etat = etat; }
tats... Voici de nouveau les ...et la variable dinstance etat. La variable dinstance nombre contient le nombre de bonbons initialement, le distributeur est vide. accepte le Notre constructeur initial et le stocke nombre de bonbons stance. dans une variable din s instances des Il cre galement le . tats, une pour chacun
es intenant, ellr a M s. n io t c Il a s Au tour de FACILES implmente . . sont TRS lguer ltat courant suffit de d
void liberer() { System.out.println(Un bonbon va sortir...); if (nombre != 0) { La machine prend en charge une mthode nombre = nombre - 1; auxiliaire, liberer(), qui libre le bonbon et } dcrmente variable dinstance nombre. } // Autres mthodes, dont une mthode get pour chaque tat... }
Notez que nous navons pas besoin de mthode daction pour delivrer() dans la classe Distributeur parce que ce nest quune action interne: lutilisateur ne peut pas demander directement la machine de dlivrer. Mais nous appelons bien delivrer() sur lobjet tat depuis la mthode tournerPoignee(). Cette mthode permet dautres objets (comme nos objets tat) de dclencher une transition de la machine vers un autre tat.
Par exemple des mthodes comme getEtatSansPiece() pour obtenir chaque objet tat et getNombre() pour obtenir le nombre de bonbons.
vous tes ici
403
public class EtatAPiece implements Etat { Distributeur distributeur; public EtatAPiece(Distributeur distributeur) { this.distributeur = distributeur; }
us instanci, noc st e t a t l d Quan e rfren e un s n o t t e sm n a t lui tr eur. Cela serd la au Distribut n io transit et. a dclencher la t e autr machine vers un Une action pour inapproprie cet tat.
On rend sa pice au client et on retourne EtatSansPiece. Quand la poigne a t tourne, nous dclenchons la transition vers EtatVendu en appelant la mthode setEtat() et en lui transmettant lobjet EtatVendu. Lobjet EtatVendu est extrait par la mthode getEtatVendu() (il y a une de ces mthodes get pour chaque tat).
public void insererPiece() { System.out.println(Vous ne pouvez pas insrer dautre pice); } public void ejecterPiece() { System.out.println(Pice retourne); distributeur.setEtat(distributeur.getEtatSansPiece()); } public void tournerPoignee() { System.out.println(Vous avez tourn...); distributeur.setEtat(distributeur.getEtatVendu()); } public void delivrer() { System.out.println(Pas de bonbon dlivr); } }
404
Chapitre 10
le pattern tat
public class EtatVendu implements Etat { //constructeur et variables dinstance public void insererPiece() { System.out.println(Veuillez patienter, le bonbon va tomber); } public void ejecterPiece() { System.out.println(Vous avez dj tourn la poigne); } public void tournerPoignee() { System.out.println(Inutile de tourner deux fois!); } public void delivrer() { distributeur.liberer(); if (distributeur.getNombre() > 0) { distributeur.setEtat(distributeur.getEtatSansPiece()); } else { System.out.println(Ae, plus de bonbons!); distributeur.setEtat(distributeur.getEtatEpuise()); } } }
du, ans EtatVenn ta e Nous sommesdd li c ire que le ire la ce qui veut e d vons donc pay. Nous dlibrer un bonbon. machine de
est emandons quel Puis nous lui edbonbons, et nous le nombre d EtatSansPiece soit passons soit se. EtatEpui
Regardez de nouveau limplmentation du Distributeur. Si la poigne a t tourne et que lopration choue (par exemple parce que le client na pas insr de pice), nous appelons quand mme delivrer() alors que cest inutile. Comment corrigeriez-vous cela?
405
Il reste une classe que nous navons pas implmente: EtatEpuise. Pourquoi ne le feriez-vous pas? Pour ce faire, rflchissez soigneusement la faon dont le Distributeur doit se comporter dans chaque situation. Vrifiez votre rponse au lieu de continuer... {
} }
406
Chapitre 10
le pattern tat
isol le comportement de chaque tat dans sa propre classe; supprim toutes ces fcheuses instructions conditionnelles qui auraient t difficiles
maintenir;
cr une base de code et une structure de classes qui correspondent plus troitement au
diagramme de Distribon, et qui sont plus faciles lire et comprendre. Regardons maintenant dun peu plus prs laspect fonctionnel:
t ats du Dist ri bu te ur
San
sPiece
tat courant
Di st r
ibuteur
APiece
Vendu
Epuise
407
Quand une action est appele, elle est dlgue ltat courant.
tournerPoignee()
S an
sPiece
tournerPoignee()
tat courant
Di st r
ibuteur
APiece
En loccurrence, la mthode tournerPoignee() est appele quand la machine est dans ltat APiece. En consquence, la machine effectue une transition vers ltat Vendu.
Vendu
Epuise
Sa
nsPiece
Di st r
ta
t co
ura
ibuteur
nt
A Pie c e
Vendu
rupture de stock
Epuise
408
Chapitre 10
le pattern tat
vos crayons
Tracez les tapes du Distributeur en commenant par ltat SansPiece. Annotez galement le diagramme en indiquant les actions et les rsultats. Pour cet exercice, vous pouvez supposer quil y a plein de bonbons dans le distributeur. 1
tats du dist ribu teu r
2
tats du dist ribu teu r
Sa nsPiece
Sa nsPiece
Di st r
AP
ibuteur
iece
Di st r
AP
ibuteur
iece
Vendu
Vendu
Epuise
Epuise
Sa nsPiece
Sa nsPiece
Di st r
AP
ibuteur
iece
Di st r
AP
ibuteur
iece
Vendu
Vendu
Epuise
Epuise
409
La premire partie de la description est limpide, non? Puisque le pattern encapsule ltat dans des classes spares et dlgue lobjet reprsentant ltat courant, nous savons que le comportement change avec ltat interne. Le Distributeur en est un bon exemple: quand la machine est dans lEtatSansPiece et que vous insrez une pice, vous obtenez un comportement diffrent (elle accepte la pice) que si elle avait t dans lEtatAPiece (la machine rejette la pice). Mais quen est-il de la seconde partie de la dfinition? Que veut dire tout se passera comme si lobjet changeait de classe? Reprsentez-vous le point de vue dun client: si un objet que vous utilisez peut changer compltement de comportement, il vous semble alors instanci partir dune autre classe. Mais, en ralit, vous savez que nous utilisons une composition pour donner lapparence dun changement de classe en rfrenant simplement des objets tat diffrents. Bien. Il est maintenant temps dtudier le diagramme de classes du pattern tat:
Le Contexte est la classe qui peut avoir plusieurs tats internes. Dans notre exemple, le Contexte correspond au Distributeur..
Contexte
requte()
Linterface Etat dfinit une interface commune pour tous les tats concrets. Si les tats implmentent tous la mme interface, ils sont interchangeables.
Etat
grer()
etat.gerer()
EtatConcretA
grer()
EtatConcretB
grer()
Chaque fois que la requt effectue sur le Contex e() est gestion est dlgue lte, sa tat.
Chaque EtatConcret gre les requtes du Contexte et fournit sa propre implmentation de la requte. De cette at, manire, quand le Contexte change dt son comportement change galement.
410
Chapitre 10
le pattern tat Une petite seconde! Si jai bonne mmoire, le diagramme de classes du pattern Stratgie, est EXACTEMENT le mme.
Vous tes trs observateur! Oui, les diagrammes de classes sont globalement identiques, mais lintention des deux patterns est diffrente. Avec le pattern tat, nous sommes en prsence dun ensemble de comportements encapsuls dans des objets tat. tout moment, le contexte dlgue lun de ces tats. Au fil du temps, ltat courant change pour lun de ces objets tat afin de reflter ltat interne du contexte, si bien que le comportement du contexte change galement. Le client ne sait gnralement pas grand-chose, voire rien, des objets tat. Avec Stratgie, le client spcifie gnralement lobjet stratgie avec lequel le contexte est compos. Maintenant, si le pattern permet de changer lobjet stratgie au moment de lexcution, il y a souvent un objet stratgie qui est particulirement adapt un objet contexte. Par exemple, au chapitre1, certains de nos canards taient configurs pour voler avec un comportement de vol classique (comme les colverts), tandis que dautres taient configurs avec un comportement de vol qui les clouait au sol (comme les canards en plastique et les leurres). En gnral, vous pouvez voir le pattern Stratgie comme une solution de rechange souple au sous-classement. Si vous utilisez lhritage pour dfinir le comportement dune classe, vous tes coinc si vous avez besoin de le modifier. Avec Stratgie, vous pouvez changer le comportement en composant avec un objet diffrent. Reprsentez-vous le pattern tat comme une solution qui permet dviter de placer une foule dinstructions conditionnelles dans votre contexte. En encapsulant les comportements dans des objets tat, il suffit de modifier ces derniers dans le contexte pour changer son comportement.
411
questions stupides
il ny a pas de
Q: R:
Dans le Distributeur, ce sont les tats qui dcident de ltat dans lequel ils doivent tre ensuite. Est-ce que cest toujours un EtatConcret qui dcident des transitions?
Pour partager vos tats, vous affecterez gnralement chaque tat une variable dinstance statique. Si un tat a besoin dutiliser des mthodes ou des variables de votre Contexte, vous devez galement lui fournir une rfrence au contexte dans chaque mthode gerer().
Non, pas toujours. Lautre solution consiste laisser le Contexte dcider du flux des transitions. En rgle gnrale, lorsque les transitions sont fixes, il est judicieux de les placer dans le Contexte. En revanche, si les transitions sont plus dynamiques, on les place classiquement dans les classes tat elles-mmes. Par exemple, dans le Distributeur, le choix de la transition vers SansPiece ou vers Epuise dpend du nombre de bonbons au moment de lexcution. Le fait de placer des transitions dans les classes tat prsente linconvnient de crer des dpendances entre ces classes. Dans notre implmentation du Distributeur, nous avons tent de le rduire en utilisant des mthodes get sur le Contexte au lieu de coder en dur des classes tat concrtes explicites. Remarquez que, en prenant cette dcision, vous dcidez galement quelles sont les classes fermes la modification le Contexte ou les classes tat mesure que le systme volue.
Q: R:
On dirait que lemploi du pattern tat augmente toujours le nombre de classes dans nos conceptions. Regardez combien notre Distributeur a de classes de plus que la version dorigine!
Vous avez raison. En encapsulant les comportements dans des classes tat distinctes, on finit toujours par avoir plus de classes. Cest souvent le prix payer pour conserver de la souplesse. moins que votre code ne soit une implmentation provisoire que vous jetterez ensuite, envisagez de le construire avec des classes supplmentaires et vous vous en fliciterez probablement en aval. Notez que le point important est souvent le nombre de classes que vous exposez vos clients, et quil existe des moyens pour leur cacher ces classes supplmentaires (par exemple en dclarant une visibilit package). Mais rflchissez. Si votre application a beaucoup dtats et que vous dcidez de ne pas utiliser des objets spars, vous allez vous retrouver avec dnormes instructions conditionnelles monolithiques, et votre code sera plus difficile maintenir et comprendre. En utilisant des objets, vous rendez les tats explicites et votre code est plus lisible et plus facile maintenir.
Q: R:
Est-ce quil arrive que les clients interagissent directement avec les tats?
Non. Comme le Contexte utilise les tats pour reprsenter son tat interne et son comportement, toutes les requtes aux tats proviennent du Contexte. Les clients ne modifient pas directement ltat du Contexte. Il appartient au Contexte de surveiller son tat, et lon ne veut gnralement pas quun client modifie ltat dun Contexte sans que celui-ci ne le sache.
Q: R:
Le diagramme de classes du pattern tat reprsente ltat comme une classe abstraite. Mais vous avez utilis une interface dans limplmentation de ltat du distributeur
Q: R:
Si jai beaucoup dinstances du Contexte dans mon application, est-il possible de partager les objets tat entre eux?
412
Oui, absolument. En fait, cest un scnario trs courant. La seule exigence est que vos objets tat ne mmorisent pas leur propre tat interne. Sinon, il vous faut une instance unique par contexte. Chapitre 10
Oui. tant donn que nous navions aucune fonctionnalit commune placer dans une classe abstraite, nous avons choisi une interface. Dans votre propre implmentation, vous prfrerez peut-tre une classe abstraite. Cette technique a lavantage de vous permettre dajouter des mthodes la classe abstraite plus tard, sans toucher aux implmentations des tats concrets.
le pattern tat
Terminons le jeu
Souvenez-vous, nous navons pas encore fini. Il nous reste le jeu programmer. Mais, maintenant que nous avons implment le pattern tat, nous allons le faire en un clin dil. Tout dabord, nous devons ajouter un tat la classe Distributeur:
public class Distributeur { Etat Etat Etat Etat Etat etatEpuise; etatSansPiece; etatAPiece; etatVendu; etatGagnant;
Il suffit dajouter ici le nouvel EtatGagnant et de linitialiser dans le constructeur Noubliez pas quil faudra galement ajouter ici une mthode getEtatGagnant.
Implmentons maintenant la classe EtatGagnant elle-mme. Elle est remarquablement similaire la classe EtatVendu:
public class EtatGagnant implements Etat { // variables dinstance et constructeur // message derreur insererPiece // message derreur ejecterPiece // message derreur tournerPoignee
me da Exactement com
ns EtatVendu.
public void delivrer() { System.out.println(VOUS AVEZ GAGN! Deux bonbons pour le prix dun!); distributeur.liberer(); if (distributeur.getNombre() == 0) { distributeur.setEtat(distributeur.getEtatEpuise()); } else { distributeur.liberer(); Tant quil reste if (distributeur.getNombre() > 0) { un second bonbon, distributeur.setEtat(distributeur.getEtatSansPiece()); nous le librons. } else { System.out.println(Ae, plus de bonbons!); distributeur.setEtat(distributeur.getEtatEpuise()); } } } } vous tes ici
413
implmenter le jeu
Terminons le jeu
Il reste une dernire modification apporter: nous devons implmenter le jeu de hasard et ajouter une transition vers EtatGagnant. Nous allons ajouter les deux EtatAPiece, puisque cest l o le client tourne la poigne:
public class EtatAPiece implements Etat { Random hasard = new Random(System.currentTimeMillis()); Distributeur distributeur; public EtatAPiece(Distributeur distributeur) { this.distributeur = distributeur; } public void insererPiece() { System.out.println(Vous ne pouvez pas insrer dautre pice); } public void ejecterPiece() { System.out.println(Pice retourne); distributeur.setEtat(distributeur.getEtatSansPiece()); } public void tournerPoignee() { System.out.println(Vous avez tourn...); int gagnant = hasard.nextInt(10); if ((gagnant == 0) && (distributeur.getNombre() > 1)) { distributeur.setEtat(distributeur.getEtatGagnant()); } else { distributeur.setEtat(distributeur.getEtatVendu()); } } public void delivrer() { System.out.println(Pas de bonbon dlivr); } }
d Nous ajoutons dabor de un gnrateur nombres alatoires x pour gnrer les dice pour cent de chan de gagner...
Sil a gagn et quil reste assez de bonbons pour quil en ait deux, nous passons EtatGagnant. Sinon, nous passons EtatVendu (comme nous lavons toujours fait).
Eh bien, il ny avait pas plus simple crire! Nous avons simplement ajout un nouvel tat au Distributeur puis nous lavons implment. partir de l, il nous a suffi dimplmenter le jeu de hasard et la transition vers ltat correct. On dirait que notre nouvelle stratgie de codage porte ses fruits...
414
Chapitre 10
le pattern tat
vraiment chang. Non, le code na pas ent raccourci un peu.. Nous lavons simplem
public class TestDistributeur { public static void main(String[] args) { Distributeur distributeur = new Distributeur(5); System.out.println(distributeur); distributeur.insererPiece(); distributeur.tournerPoignee(); System.out.println(distributeur); distributeur.insererPiece(); distributeur.tournerPoignee(); distributeur.insererPiece(); distributeur.tournerPoignee(); System.out.println(distributeur); } }
Comme nous voulons un tat gagnant, nous narrtons pas dinvestir des pices et de tourner la poigne. De temps autre, nous affichons ltat du distributeur....
ment au grand Lquipe de dveloppe porte de la salle complet derrire late ndant de voir de confrences, at ti on base sur le si la nouvelle concep nc tionner!! pattern tat va fo
415
tester le distributeur
Ouiii! a swingue!
%java TestDistributeur Distribon, SARL. Distributeur compatible Java, modle 2004 Stock: 5 bonbons Lappareil attend une pice Vous avez Vous avez VOUS AVEZ Un bonbon Un bonbon insr une pice tourn... GAGN! Deux bonbons pour le prix dun! va sortir... va sortir...
Distribon, SARL. Distributeur compatible Java, modle 2004 Stock: 3 bonbons Lappareil attend une pice Vous avez Vous avez Un bonbon Vous avez Vous avez VOUS AVEZ Un bonbon Un bonbon Ae, plus insr une pice tourn... va sortir... insr une pice tourn... GAGN! Deux bonbons pour le prix dun! va sortir... va sortir... de bonbons!
de la chance a alors!, On a eu e dmo au ou quoi? Dans notr s gagn une pa PDG, nous navons fois mais deux!
Distribon, SARL. Distributeur compatible Java, modle 2004 Stock: 0 bonbons Lappareil est en rupture de stock %
questions stupides
Excellente question. EtatVendu et EtatGagnant sont presque identiques, sauf quEtatGagnant dlivre deux bonbons au lieu dun. Vous pourriez sans doute placer le code qui dlivre les deux bonbons dans EtatVendu. Bien sr, il y a un inconvnient: vous avez maintenant DEUX tats reprsents dans une seule classe: ltat o vous tes gagnant et celui o vous ne ltes pas. Vous sacrifiez donc la clart pour rduire la duplication de code. Autre facteur considrer, le principe que vous avez appris au chapitre prcdent: une classe, une responsabilit. En plaant la responsabilit dEtatGagnant dans EtatVendu, vous avez attribu EtatVendu DEUX responsabilits. Que se passe-t-il quand la promotion se termine? Ou quand les enjeux du concours changent? Cest donc une situation de compromis qui entrane une dcision de conception.
Il ny a pas de
Q: R:
Pourquoi faut-il un EtatGagnant? Est-ce quEtatVendu ne pourrait pas juste dlivrer deux bonbons?
416
Chapitre 10
le pattern tat Bravo! Beau travail, lquipe. Nos ventes sont dj en train de crever le plafond avec ce nouveau jeu. Vous savez, nous faisons aussi des distributeurs de boissons, et je me disais quon pourrait leur ajouter des leviers de machines sous et en faire aussi un jeu. Nous avons des gosses de quatre ans qui parient avec les distributeurs de bonbons. Pourquoi nous arrter en si bon chemin?
Bilan de sant...
Oui, le PDG de Distribon a probablement besoin dun bilan de sant, mentale du moins, mais l nest pas notre propos. Rflchissons aux aspects du Distributeur que nous aimerions consolider avant de livrer la version de luxe:
Toute la logique des transitions est dans les classes tat. Quels
sont les problmes qui pourraient en rsulter? Allons-nous la transfrer dans le Distributeur? Quels seraient les avantages et les inconvnients de cette approche?
417
Face face :
Stratgie
H, frangin. Tu as su que jtais dans le chapitre1? Jtais parti donner un coup de main aux types du Patron de mthode: ils avaient besoin de moi pour finir leur chapitre. Alors, quoi soccupe mon noble frre ces temps derniers?
tat
Oui, cest ce que dit la rumeur publique.
Comme dhabitude: jaide les classes prsenter des comportements diffrents dans diffrents tats. Je ne sais pas, mais on dirait toujours que tu viens de pomper ce que je fais et que tu emploies dautres mots pour le dcrire. Rflchis: je permets aux objets dincorporer diffrents comportement ou algorithmes grce la composition ou la dlgation. Tu ne fais que me copier.
Jadmets que nous avons des pratiques apparentes, mais mon intention est totalement diffrente de la tienne. Et la faon dont jenseigne mes clients utiliser la composition et la dlgation nest absolument pas la mme. Eh bien, si tu passais un peu plus de temps penser autre chose qu toi, tu le saurais. Rflchis un peu ta faon de fonctionner: tu instancies une classe et tu lui donnes gnralement un objet stratgie qui implmente un comportement quelconque. Comme au chapitre1 o tu distribuais aux canards des comportements pour cancaner, daccord? Les vrais canards faisaient coin-coin pour de bon et les canards en plastique couinaient.
Oui, ctait du bon travail... et je suis sr que tu vois que cest beaucoup plus puissant que dhriter de ton comportement, non?
Oui, naturellement. Maintenant, rflchis ma faon de fonctionner: elle est compltement diffrente.
le pattern tat
Stratgie
tat
Bien. Quand mes objets Contexte sont crs, je peux leur indiquer leur tat initial, mais ils peuvent modifier leur propre tat ultrieurement.
Oh, arrte! Moi aussi, je peux changer de comportement lors de lexcution! Cest tout lintrt de la composition!
Bien sr, mais mon fonctionnement est construit autour dtats discrets. Mes objets Contexte changent dtat au fil du temps selon des transitions bien dfinies. Autrement dit, le changement dtat est intgr mon schma. Voil comment je fonctionne!
Bon, je te laccorde, je nencourage pas mes objets avoir des ensembles de transitions bien dfinis entre les tats. En fait, je prfre gnralement contrler la stratgie quils appliquent. coute, nous avons dj dit que notre structure tait semblable mais que notre intention tait diffrente. Admets-le: il y a assez de place pour nous deux. Oui, oui, je te laisse tes fantasmes. Tu te comportes comme si tu tais un grand pattern comme moi, mais, regarde bien: je suis dans le chapitre1 alors quils tont relgu au chapitre10. Tu peux me dire combien de lecteurs vont vraiment aller jusque l?
Tu plaisantes? Cest un ouvrage de la collection Tte la premire, et les lecteurs de Tte la premire assurent. Bien sr quils vont arriver au chapitre10!
419
exercice de remplissage
Distribon, SARL
e Plus d s bonbon
ns les ntionner une transition da Nous avons oubli de me... remplir la de yen mo un ut fa il nous spcifications dorigine vid mme. Pouvezgra e! Voici le nouveau dia si bon travail machine quand elle est nou du it s? Vous avez fa ajouter z vous limplmenter pour eur rre pou s vou que doute nul : but tri dis du te res le sur cette fonction en un clin dil! - Les ingnieurs de Distribon remplir
ice
A unee pic
pice
tou
rne
insrer p
rp
oig
ne
jecter
r dlivren o b bon
bonbo
n Bonbo u vend
ns =
420
Chapitre 10
le pattern tat
Vous avez fait un travail stupfiant! Jai encore quelques ides qui vont rvolutionner lindustrie des distributeurs et je veux que vous les implmentiez. Chut! Je vous en parlerai dans le prochain chapitre.
421
Pattern tat
Description Encapsule des comportements interchangeables et utilise la dlgation pour dcider lequel utiliser Les sous-classes dcident de la faon dimplmenter les tapes dun algorithme Encapsule des comportements bass sur des tats et dlgue le comportement ltat courant
Stratgie
Patron de mthode
422
Chapitre 10
le pattern tat
POINTS DIMPACT
Abstraction n Encapsulatio ce qui varie. Encapsulez e. me lhritag Polymorphis ncapsulation Prfrez le non des interfaces, Hritagee grammez des
Pro ions. implmentat ler vous de coup ui q Efforcezs t je les ob faiblement nt. interagisse re ouvertes doivent tm s se as s la cl es L mais fer e lextension n. modificatio ctions. Ne tes. des abstra s concr Dpendez pa as s des cl se ez d en p d . u vos amis Ne parlez q us vous pelez pas, no Ne nous aps. appelleron e r quune seul ne doit avoi Une classe ch anger. raison de
Principes OO
Bases de lOO
f e ce dsi de n uc unr pulu farcf c -e re an ee h t ie hu tn ct a a v in tg t lep r ab e e n ae s a g psu s a Str y c a it t s. O e h ir e in t c le e a r b in d t s, u f t a e e n e e s D je g n m e t b n u h a a o m je it h r b n it r l n c o ne o up ind ru io eu ca e e lg p t eut f . is a tr in a s D dae t nD e sq ic m n d s r r , e n iq lo t e n b e d r m n , n it m je io e e e a s h b il F u e na p t o n le b it is q a y r a a n d t o n s la d m u e lg r , o e an n e b ap l ux rc ia o nd iuix a ue je fs ud s ess io u u b q ded qt F tx jo ere ot t q sit f eic n u r m u nn e c e o r is e t d ca h eb d li m c la p s n c a tmme u r s t t r io s le ie o a u n e e g t d o s j d g ie poeinco p e a , t n r t t ix so ss a n o c n o a n u io la e h qu t t o c n t c la e m Str it re t uie n m ss us et t r lu d le a le un o iso ur oe d seca un g rm o psa e n if ee e f in p ss t e x ul u S o t n p la n ps a n c u d io t t e in t t la se c t en n a u r n m e e ic a r ie so t c r is m e r n so b st e in p e x r lat a a abricle l ge .erface va enIl p u u in ea d n . a st n t io it .F le in a t n t riq u t t n e ra a r e m ie a se m t sa c t n r ve e m n t is m n . io p u e a n o s o la t c ra c iq u n C st e ia F t e pa c in q iv . is l e n le r il a t m a r i fac b ie e oce s es in d st c ns ancia n u tlt lo ea n lut e n nr e g t a ss m et aua l tinai e rr in lar t st a p oile an y e eo f t is u ncio n p r or g eta u s e ys t st d l itrqu o d eut au nre d in p ss f ,t r l A s t e , u c la u n r o d c c je es a e f a io e u ob t e end d t ss n un n g un u a o c la l c a e m n iv d e f e dediffrenm r u m dt ner e r neune n tes fd ass up e d t .. d e s a it in la n ss r c F s r u t la e u e e e c li o t n a a r u le t s. et d pa b p e n , bejets e a s ss r o es io d n se nt uat la c sje t nL ces Aifs de iesse e cstsous-d e.e qu clla d t b l st it o eu a re n ll r se t r s. n fon u o n e e u o e p u i li f q m It c o de if s e n u r c d u d n a sul lo e u t a e e u r a pi ll te et r qer, ns .e een le ca s ct as r io dou n snp of des so dat po e e b at .o O t ait e m r r nins O llL er sa om e op u c r d eq st c e es e er cC ra usc l d t d sy o a v e it s b at n d t sr e a r P iv / so u ili a f n s ss ib n so s u la t iernt e rs c d sa u t r if a ve u s a e t h p e t od r a t c d rmettan s fair un mm osa u la g r rlu p d..eco t g s st le a e t ie s d r u je e h e ge. e p p c e d ob e is y an r c s t il a t a a ch n t r f p e e u pe r i c d ne , e t h n ja t us er et s e son t pl nau in ils. sint rm d c u raie epe ae pe rt so at e angeait de f le e d n t s ib n s io lu e at t a t p t n p s a e e r t d m li n o c m an c r x s qu in st tmet au si lobjet ch ts p sy ea ces comport sur em f er te pen dinso . Il les obje pos a comme sser com pala ouetr se mme faon ons de ceux-ci Tit e d traclasse. t les combinais individuels e
423
Distribon, SARL
Le monde dans lequel les distributeurs ne sont jamais vides
Gagnant
tur have ns cran a wi k, we nner !
gumballs = 0
Plus de bonbons
gumba
lls >
pice
A une pice
poi
gn
et
our
insrer
ne
pice
,p
as
de
Pas de pice
bon
jecter
gag
nan t
bonbo n
bon
s=0
s>
r dlivren o b bon
n Bonbo u vend
424
Chapitre 10
le pattern tat
E. Nous navons pas encapsul ce qui varie. F. Les ajouts ultrieurs sont susceptibles de
provoquer des bogues dans le code.
Il reste une classe que nous navons pas implmente: EtatEpuise. Pourquoi ne le feriez-vous pas? Pour ce faire, rflchissez soigneusement la faon dont le Distributeur doit se comporter dans chaque situation. Vrifiez votre rponse avant de continuer...
public class EtatEpuise implements Etat { Distributeur distributeur; public EtatEpuise(Distributeur distributeur) { this.distributeur = distributeur; }
is, nous ne Dans ltat Epu ent rien faire pouvons absolumpas rempli le a n tant quon Distributeur..
public void insererPiece() { System.out.println(Vous ne pouvez pas insrer de pices, nous sommes en rupture de stock); } public void ejecterPiece() { System.out.println(jection impossible, vous navez pas insr de pice); } public void tournerPoignee() { System.out.println(Vous avez tourn, mais il ny a pas de bonbons); } public void delivrer() { System.out.println(Pas de bonbon dlivr); } } public String toString() { return sold out; } }
425
Pour implmenter nos tats, nous devons dabord spcifier le comportement des classes quand chaque action est appele. Annotez le diagramme cidessous en inscrivant le comportement de chaque action dans chaque classe. Nous en avons dj indiqu quelques-uns pour vous..
EtatSansPiece
insererPiece() ejecterPiece() tournerPoignee() delivrer()
Dire au client vous avez tourn mais il ny a pas de pice Dire au client il faut payer dabord Dire au client vous ne pouvez pas insrer dautre pice Rendre la pice, aller EtatSansPiece Aller EtatVendu Dire au client pas de bonbon dlivr Dire au client attendez, le bonbon va sortir
EtatAPiece
insererPiece() ejecterPiece() tournerPoignee() delivrer()
EtatVendu
insererPiece() ejecterPiece() tournerPoignee() delivrer()
Dire au client inutile de tourner deux fois Dlivrer un bonbon. Vrifier le nombre de bonbons. Sil est suprieur zro, aller EtatSansPiece, sinon aller EtatEpuise Dire au client vous navez pas insr de pice Dire au client il ny a plus de bonbons Dire au client pas de bonbon dlivr Dire au client pas de bonbon dlivr Dire au client attendez, le bonbon va sortir Dire au client vous avez dj tourn la poigne Dlivrer deux bonbons. Vrifier le nombre de bonbons restants. Sil est suprieur 0, aller EtatSansPiece, sinon aller EtatEpuise
426
Chapitre 10
EtatGagnant
insererPiece() ejecterPiece() tournerPoignee() delivrer()
le pattern tat
dlgue
tournerPoignee()
insererPiece()
ura at co
nt
Sa nsPiece
Sa nsPiece
tournerPoignee()
AP
tat courant
AP
Di st r
ibuteur
iece
Di st r
ibuteur
iece
action de la machine
Vendu
action de la machine
Vendu
Epuise
Epuise
dispense()
Sa nsPiece
tat
an our
Sa nsPiece
Di st r
ta
t co
ibuteur
ura
nt
AP
iece
Di st r
AP
ibuteur
iece
Vendu
Vendu
Epuise
Epuise
427
Pattern tat
Description Encapsule des comportements interchangeables et utilise la dlgation pour dcider lequel utiliser Les sous-classes dcident de la faon dimplmenter les tapes dun algorithme
Encapsule des comportements bass sur des tats et dlgue le comportement ltat courant
Stratgie
Patron de mthode
428
Chapitre 10
11 le pattern Proxy
h
g
Avez-vous dj jou gentil flic, mchant flic ? Vous tes le gentil policier et
vous rendez volontiers service de bonne grce, mais comme vous ne voulez pas que qui que ce soit vous sollicite, vous demandez au mchant de servir dintermdiaire. Cest l le rle dun proxy : contrler et grer les accs. Comme vous allez le voir, un proxy peut se substituer un objet de bien des manires. Les proxies sont connus pour transporter des appels de mthodes entiers sur lInternet la place dautres objets. On les connat galement pour remplacer patiemment un certain nombre dobjets bien paresseux.
nouveau chapitre
429
Bonjour tout le monde. Jaimerais vraiment bien disposer dun meilleur contrle sur les distributeurs de bonbons. Pouvezvous trouver un moyen de mafficher des rapports sur ltat du stock et sur celui des appareils ?
Cela semble assez facile. Si vous vous en souvenez, le code du Distributeur contient dj des mthodes qui permettent de connatre le nombre de bonbons (getNombre()) et ltat courant de la machine (getEtat()). Il nous suffit donc de crer un rapport que nous pourrons afficher et transmettre au PDG. Mmmm... nous devrons galement sans doute ajouter un champ emplacement chaque distributeur. Ainsi, le PDG pourra entretenir ses machines.
430
Chapitre 11
le pattern proxy
Coder le contrleur
Commenons par ajouter la classe Distributeur du code qui lui permettra de grer les emplacements :
public class Distributeur { // autres variables dinstance String emplacement;
public Distributeur(String emplacement, int nombre) { // reste du constructeur Lemplacement est transmis this.emplacement = emplacement; } constructeur et stock public String getEmplacement() { return emplacement; } // autres mthodes }
dinstance.
pour Ajoutons galement une mthode get ns avo en s nou trouver lemplacement quand besoin.
Crons maintenant une autre classe, ControleurDistrib, qui extrait lemplacement de la machine, le nombre de bonbons quelle a en stock et son tat courant, puis qui affiche un petit rapport bien propre :
public class ControleurDistrib { Distributeur machine; public ControleurDistrib(Distributeur machine) { this.machine = machine; }
Le contrleur accepte la machine dans son constructeur et laffecte la variable dinstance machine.
public void rapport() { System.out.println(Distributeur : + machine.getEmplacement()); System.out.println(Stock Courant : + machine.getNombre() + bonbons); System.out.println(tat courant : + machine.getEtat()); } }
431
contrle local
Tester le contrleur
Nous avons implment cela en un rien de temps. Le PDG va tre ravi et stupfait par notre expertise en dveloppement. Maintenant, il suffit dinstancier un ControleurDistrib et de lui donner une machine contrler :
public class TestDistributeur { public static void main(String[] args) { int nombre = 0;
if (args.length < 2) { System.out.println(TestDistributeur <nom> <stock>); System.exit(1); } nombre = Integer.parseInt(args[1]); Distributeur distributeur = new Distributeur(args[0], nombre); ControleurDistrib controleur = new ControleurDistrib(distributeur); // reste du code controleur.rapport(); } }
r et lui ...puis instancier un contrleulaq uelle il doit transmettre la machine sur faire un rapport.
Fichier dition Fentre Aide Bouillabaisse
Quand nous avons besoin dun rapport sur la machine, nous appelons la mthode rapport().
%java TestDistributeur Marseille 112 Distributeur : Marseille Stock Courant : 112 bonbons tat courant : En attente dune pice
Le rsultat a lair parfait, mais je crois que je nai pas t clair. Je veux contrler mes distributeurs DISTANCE ! Dailleurs, nous avons dj des rseaux en place pour cela. Allons les gars, vous tes censs tre la gnration Internet !
Et voil le rsultat !
432
Chapitre 11
le pattern proxy
Eh bien, cela nous apprendra analyser les besoins avant de nous prcipiter sur le codage. Jespre quil ne va pas falloir tout recommencer...
Ne vous inquitez pas. Je viens de rviser mes design patterns. Il suffit quon cre un proxy distant et nous serons fin prts.
Guy
Luc
Paul
Guy : Un quoi distant ? Paul : Un proxy distant. Rflchissez : nous avons dj crit le code du contrleur, exact ? Nous donnons ControleurDistrib une rfrence a une machine et il nous retourne un rapport. Le problme est que le contrleur sexcute dans la mme JVM que le distributeur et que le PDG veut rester tranquillement assis son bureau et contrler les machines distance ! Mais si nous laissions notre classe ControleurDistrib telle quelle en lui transmettant un proxy un objet distant ? Guy : Je ne suis pas sr de comprendre. Luc : Moi non plus. Paul : Commenons par le commencement... un proxy est un remplaant dun objet rel. En loccurrence, le proxy se comporte exactement comme sil tait un objet Distributeur, mais, dans les coulisses, il communique sur le rseau et converse avec le vrai Distributeur distant. Luc : Donc, tu dis que nous conservons le code tel quel et que nous passons au contrleur une rfrence une version proxy du Distributeur... Guy : Et ce proxy fait semblant dtre lobjet rel, mais il ne fait que communiquer sur le rseau avec celui-ci. Paul : Oui, cela rsume assez bien. Guy : Cela semble plus facile dire qu faire. Paul : Peut-tre, mais je suis plutt optimiste. Nous devons nous assurer que le distributeur peut se comporter comme un service et accepter des requtes sur le rseau. Nous devons galement donner notre contrleur un moyen dobtenir une rfrence un objet proxy, mais Java contient dj dexcellents outils intgrs qui vont nous aider. Mais dabord, parlons un peu plus des proxies distants... vous tes ici 433
proxy distant
Ordinateur du P
DG
Tas local
mblant dtre Le proxy faitt.se En ralit, ce lobjet distan prsentant de nest quun re Tas lobjet rel !
urDistrib Ici, le Controlent : il croit est lobjet clieributeur parler au dist parle quau rel, mais il ne proxy qui proxy. Cest lebuteur rel parle au distri sur le rseau.
Co
tr ib
ntr s oleurDi
Proxy
ist
ributeu
T tant ESlu is d t e j b Lo rel. Cest i qui lobjet thodes qui font a les mtravail. le vrai
Votre objet client se comporte comme si ctait lui qui excutait les appels de mthodes distance. En ralit, il appelle les mthodes sur un objet proxy local qui gre tous les dtails de bas niveau de la communication sur le rseau.
434
Chapitre 11
le pattern proxy
Voil une ide drlement astucieuse. Nous allons crire un code qui prend une invocation de mthode, qui la transfre par un moyen quelconque sur le rseau et qui invoque cette mme mthode sur un objet distant. Puis, je suppose que, quand lappel est termin, le rsultat est retransmis sur le rseau notre client. Mais il me semble que ce code va tre trs dlicat crire.
Pas si vite ! Nous nallons pas crire ce code nousmmes. Il fait pratiquement partie des fonctionnalits de Java. Nous navons rien dautre faire que de modifier notre code pour pouvoir tirer parti de RMI.
A A
Avant daller plus loin, rflchissez la faon dont vous concevriez un systme qui permettrait les appels de mthode distance. Comment faciliteriez-vous le travail du dveloppeur pour quil ait crire le moins de code possible ? Comment feriez-vous pour que linvocation distance soit transparente ?
Linvocation distance doit-elle tre totalement transparente ? Est-ce une bonne ide ? Quel est le problme potentiel de cette approche ?
435
Premirement, nous allons faire un dtour et ltudier un peu. Mme si vous connaissez bien RMI, vous voudrez peut-tre nous accompagner et admirer le paysage. Puis nous allons reprendre notre Distributeur et le transformer en un service distant fournissant un ensemble de mthodes qui peuvent tre appeles distance. Enfin, nous crerons un proxy capable de converser avec un Distributeur distant, en utilisant RMI, et nous rassemblerons le systme de contrle pour que le PDG puisse surveiller un nombre quelconque de machines distantes.
436
Chapitre 11
le pattern proxy
sembler familier...
Tas du
client fait Lauxiliaire dure le service, semblant dt un remplamais il nest qu rel. client ant de lobjet
Tas du serveur
client
Lobjet client vrai croit parler aue service. Il pens est que lauxiliaire faire lentit qui va le vrai travail.
Au
xiliaire du
serveur
Ob
jet servic
Lauxiliaire du service reoit la requte de lauxiliaire du client, la dballe et appelle la mthode sur le service rel.
le vice EST r e s t e j i b lu Lo vice. Cest qui vrai ser la mthode qui possde vrai travail. fait le
437
RMI
Tas du serveur
C li ent
Ob
jet client
Au xiliaire du
Au xiliaire du
se rvice
Ob
v jet du ser
Lauxiliaire du client emballe les informations sur lappel (nom de mthode, arguments, etc.) et lenvoie sur le rseau lauxiliaire du service. Tas du serveur Tas du client le client veut appeler une mthode
faireTravailCapital()
C li ent
Ob
jet client
Au xiliaire du
Au xiliaire du
se rvice
Ob
v jet du ser
Lauxiliaire du service dballe les informations quil a reues de lauxiliaire du client, dtermine quelle mthode appeler (et sur quel objet) et invoque la mthode relle sur lobjet service rel. Tas du client
le client veut appeler une mthode
Tas du serveur
faireTravailCapital()
faireTravailCapital()
se rvice
ice
v
ice
Ob
jet clie
nt
Au xiliaire du
Au xiliaire du
Ob
jet du ser
voici Souvenez-vous :ient la lobjet qui cont E, celui qui logique RELL vail ! fait le VRAI tra
C li ent
438
Chapitre 11
ice
le pattern proxy
La mthode est appele sur lobjet service, qui retourne un rsultat lauxiliaire du service. Tas du client Tas du serveur
rsultat
C li ent
se rvice
Ob
jet clie
nt
Au xiliaire du
Au xiliaire du
Ob
jet du ser
Lauxiliaire du service emballe les informations retournes par lappel et les renvoie sur le rseau lauxiliaire du client. Tas du serveur
rsultat emball
Tas du client
C li ent
Ob
jet client
Au xiliaire du
Au xiliaire du
se rvice
Ob
v jet du ser
Lauxiliaire du client dballe les valeurs retournes et les transmet lobjet client. Pour lobjet client, tout ce processus a t transparent. Tas du client
rsultat
Tas du serveur
C li ent
Ob
jet client
Au xiliaire du
Au xiliaire du
se rvice
Ob
v jet du ser
ice
ice
ice
v
439
Terminologie RMI: pour RMI, lauxiliaire du client est une souche et lauxiliaire du client est un squelette.
Tas du client
c li ent
se rvice
SOUCHE RMI
Ob
jet client
Au xiliaire du
Au xiliaire du
Ob
jet service
Voyons maintenant les tapes ncessaires pour transformer un objet en service qui accepte des appels distants, et celles qui permettent au client deffectuer des appels distants. Attachez vos ceintures: les tapes sont nombreuses et la route parseme de bosses et de virages. Rien de vraiment inquitant toutefois. 440
Chapitre 11
Les versions rcentes de Java nexigent pas de squelette explicite, mais il y a toujours quelque chose c serveur qui gre t comportement dule squelette.
le pattern proxy
tape 1 :
Crer une Interface distante Linterface distante dfinit les mthodes quun client peut appeler. Cest elle que le client va MonService.java utiliser comme type pour votre service. La souche et le service rel limplmentent tous les deux!
it les min f d e c terfa s que vous voulez Cette ind istante thodes clients appellent. que les
tape 2:
Crer une Implmentation distante Cest la classe qui fait le VRAI travail. Cest elle qui possde limplmentation relle des mthodes distantes dfinies dans linterface distante. Cest lobjet sur lequel le client va appeler les mthodes (par exemple notre Distributeur!).
MonServiceImpl.java
sse qui posLe service rel ; la cla nt le vrai fo i sde les mthodes qunte linterface travail. Elle implme distante.
tape 3:
Gnrer les souches et les squelettes en excutant rmic Ce sont les auxiliaires du client et du serveur. Vous navez pas crer ces classes ni mme regarder le code source qui les gnre. Tout est gr automatiquement quand vous excutez loutil rmic livr avec votre kit de dveloppement Java.
%rmic MonServiceImpl
MonServiceImpl_Stub.class
101101 10 110 1 0 11 0 001 10 001 01
tape 4:
Excuter le registre RMI (rmiregistry) Le registre RMI est comparable aux pages blanches dun annuaire tlphonique. Cest l que le client va chercher le proxy (lobjet auxiliaire/souche).
Fichier dition Fentre Aide Boire
MonServiceImpl_Skel.class
%rmiregistry
tape 5:
Lancer le service distant Vous devez mettre lobjet service en service. Vote classe dimplmentation cre une instance du service et lenregistre dans le registre RMI. Ce faisant, elle met ce service la disposition des clients.
%java MonServiceImpl
441
tendre java.rmi.Remote Remote est une interface marqueur, ce qui veut dire quelle na pas de mthodes. Toutefois, cela a une signification spciale pour RMI et vous devez appliquer cette rgle. Remarquez que nous avons crit extends: une interface est autorise tendre une autre interface.
Dclarer que toutes les mthodes lancent une RemoteException Linterface distante est celle que le client utilise comme type pour le service. Autrement dit, le client invoque des mthodes sur une entit qui implmente linterface distante. Cette entit est la souche, bien sr, et comme cest la souche qui se charge du rseau et des E/S, toutes sortes de Gros Problmes peuvent survenir. Le client doit reconnatre lexistence de ces risques en grant ou en dclarant les exceptions distantes. Si les mthodes dune interface dclarent des exceptions, tout code appelant des mthodes sur une rfrence de ce type (le type de linterface) doit grer ou dclarer les exceptions. .
ue que nous Ceci nous indiqli nterface pour allons utiliser ar des appels prendre en ch ge distants.
import java.rmi.* ;
java.rmi
Tout appel de mthode public interface MonService extends Remote { public String direBonjour() throws RemoteException; distant est considr comme risqu. Dclarer une }
3
Sassurer que les arguments et les valeurs de retour sont de type primitif ou srialisables Les arguments et les valeurs de retour dune mthode distante doivent tre de type primitif ou srialisable. Rflchissez: tout argument dune mthode distante doit tre emball et expdi sur le rseau et cela se fait au moyen de la srialisation. Il en va de mme pour les valeurs de retour. Si vous utilisez des types primitifs, des chanes de caractres et la majorit des types de lAPI (y compris les tableaux et les collections), tout ira bien. Si vous transmettez vos propres types, vrifiez que vos classes implmentent Serializable.
RemoteException pour chaque mthode oblige le client faire attention et reconnatre quil pourrait y avoir un dysfonctionnement.
Consultez Java-Tte la premire si vous avez besoin de vous rafrachir le mmoire sur Serializable.
Comme le serveur va rexpdie cet te valeur de retour sur le rseau au client, celle-ci doir t tr cette faon que les arguments et e srialisable. Cest de les valeurs de retour sont emballs et transmis.
442
Chapitre 11
le pattern proxy
Implmenter linterface distante Votre service doit implmenter linterface distante celle qui possde les mthodes que votre client va appeler.
public class MonServiceImpl extends UnicastRemoteObject implements MonService { public String direBonjour() { return Le serveur dit, Bonjour; Le compilateu r va vrifier que vous } avez implment toutes les mthod // reste du code de la classe de linterface que vous implment es ez. } Dans ce cas, il
ny en a quune.
tendre UnicastRemoteObject Pour pouvoir fonctionner comme service distant, votre objet doit disposer dune fonctionnalit lie la distance. Le moyen le plus simple consiste tendre UnicastRemoteObject (du package java.rmi.server) et laisser cette classe (votre superclasse) travailler votre place. public class MonServiceImpl extends UnicastRemoteObject implements MonService {
crire un constructeur sans arguments qui dclare une RemoteException Votre nouvelle superclasse, UnicastRemoteObject, prsente un petit problme: son constructeur lance une RemoteException. Le seul moyen de le rsoudre est de dclarer un constructeur pour votre implmentation distante, dans le seul but de disposer dun endroit pour dclarer une RemoteException. Souvenez-vous: lorsquune classe est instancie, le constructeur de a superclasse est toujours appel. Si celui-ci lance une exception, vous navez pas dautre choix que de dclarer que votre constructeur lance galement une exception. public MonServiceImpl() throws RemoteException { } Enregistrer le service dans le registre RMI
in de placer quoi Vous navez pas beso constructeur. Il que ce soit dans le t un moyen de e vous faut simplemen nstructeur de votr dclarer que le coun exception. superclasse lance e
Maintenant que vous avez un service distant, vous devez le mettre la disposition des clients distants. Pour ce faire, vous linstanciez et vous le placez dans le registre RMI (qui doit tre en train de sexcuter, sinon cette ligne de code choue). Quand vous enregistrez lobjet implmentation, le systme RMI place la souche dans le registre, puisque cest delle dont le client a besoin en ralit. Enregistrez votre service grce la mthode statique rebind() de la classe java.rmi.Naming. le e service (pour quere tr vo m no try { un z ne Don dans le regist ) et MonService service = new MonServiceImpl(); client puisse le rechercher nd vous registre RMI. Quaic le ns da le zNaming.rebind(BonjourDistant, service); re par la st gi enre I remplace le serv e RM e, ic rv se } catch(Exception ex) {...} et bj lo liez registre.
-ci dans le
443
souches et squelettes
Excutez rmic sur la classe de limplmentation distante (non sur linterface distante) Loutil rmic, livr avec le kit de dveloppement Java, prend une implmentation de service et cre deux nouvelles classes, la souche et le squelette. Il applique une convention de nommage: le nom de votre implmentation avec soit _Stub soit _Skel ajout la fin. rmic dispose dautres options, notamment ne pas gnrer de squelettes, lire le code source de ces classes et mme utiliser IIOP comme protocole. Ici, nous procdons comme vous procderez gnralement. Les classes atterriront dans le rpertoire courant (celui dans lequel vous vous tes dplac avec la commande cd). Souvenez-vous: puisque rmic doit tre capable de voir la classe de votre implmentation distante, vous excuterez probablement rmic depuis le rpertoire dans lequel elle se trouve. (Cest exprs que nous nutilisons pas de packages ici pour simplifier. Dans le monde rel, vous devriez prendre en compte les structures de rpertoires des packages et les noms qualifis.)
%rmic MonServiceImpl
MonServiceImpl_Stub.class
101101 10 110 1 0 11 0 001 10 001 01
MonServiceImpl_Skel.class
Ouvrez une fentre de commandes et lancez rmiregistry. Vrifiez bien que vous partez dun rpertoire qui a accs vos classes. Le plus simple consiste partir de votre rpertoire classes.
%rmiregistry
444
Chapitre 11
le pattern proxy
import java.rmi.*;
terface Remote RemoteException et lin .rmi. sont dans le package java java.rmi.Remote Votre interface DOIT tendre
ns ct est da e j b O e t o us m est le moyen le pl UnicastRgee java.rmi.server. t ec bj eO ot em R tendre Unicastun objet distant. le packa simple de crer
Vous devez bien sr implmenter toutes les mthodes de linterface. Mais remarquez que vous navez PAS dclarer de RemoteException.
public static void main (String[] args) { try { MonService service = new MonServiceImpl(); Naming.rebind(BonjourDistant, service); } catch(Exception ex) { Comme le constructeur de votre superclasse (pou ex.printStackTrace(); Un icastRemoteObject) d r } devez crire un construc clare une exception, VOUS } teur, parc
sse (pour Comme le constructeur de votre supercla eption, UnicastRemoteObject) dclare une exc e que parc ur, cte tru cons VOUS devez crire un du elle app ur cte tru cons cela signifie que votre . eur) ruct onst r-c code risques (son supe
e que cela signifie que votre constructe appelle du code risqu (son super-constructeur es ur).
vous tes ici
445
a marche
Code la loupe
Le client utilise toujours linterfac e distante comme type du servic e. En fait, il na jamais besoin de conna tre le vrai nom de classe du servic e distant.
Vous devez donner le type de linterface, puisque la mthode lookup() retourne un type Object.
446
Chapitre 11
le pattern proxy
Client
r() onjou direB
Serveur
Ob
jet client
S ou c h e
1 lo oku p()
so uc he re to ur n 2 e
S qu e
Souche
Comment a marche...
1 Le client consulte le registre RMI
Naming.lookup(rmi://127.0.0.1/BonjourDistant) ;
2 Le registre RMI retourne lobjet souche (comme valeur de retour de la mthode lookup ()) et RMI le dsrialise automatiquement. Vous DEVEZ avoir la classe de la souche (que rmic a gnre pour vous) sur client ou la souche ne sera pas dsrialise.
Le client invoque une mthode sur la souche, comme si elle TAIT le service rel
447
import java.rmi.*;
public class monClientDistant { public static void main (String[] args) { new monClientDistant().go(); } public void go() {
try { MonService service = (MonService) Naming.lookup(rmi://127.0.0.1/BonjourDistant); String s = service.direBonjour(); System.out.println(s); } catch(Exception ex) { ex.printStackTrace(); } } }
On dirait vraiment un bon vieil appel de mthode! (s au dclarer la RemoteEx f quil doit ception.)
Trucs de geeks
Voici maintenant la question intressante. Dune manire ou dune autre, le client doit avoir la classe de la souche (que vous avez gnre plus tt avec rmic) au moment o il effectue la recherche, sinon la souche ne sera pas dsrialise sur le client et tout explosera. Le client doit galement avoir les classes de tous les objets srialiss retourns par les appels de mthode lobjet distant. Dans un systme simple, vous pouvez vous contenter de fournir ces classes directement au client. Mais il existe un moyen beaucoup plus cool, bien quil dpasse la porte de cet ouvrage. Nanmoins, si vous tes intress, ce moyen beaucoup plus cool se nomme tlchargement dynamique des classes. Avec le tlchargement dynamique des classes, les objets srialiss (comme la souche) sont estampills avec une URL qui indique au systme RMI rsidant sur le client o trouver le fichier .class correspondant lobjet. Puis, au cours du processus de dsrialisation, si RMI ne trouve pas la classe localement, il utilise cette URL pour mettre une requte HTTP GET et rcuprer le fichier .class. Il vous faut donc un serveur web simple pour servir les fichiers .class. Vous devez galement modifier certains paramtres de scurit sur le client. Le tlchargement dynamique des classes prsente encore quelques aspects un peu dlicats, mais vous savez le principal. En ce qui concerne spcifiquement lobjet souche, le client dispose dun autre moyen dobtenir la classe, mais seulement en Java5, Nous en parlerons brivement vers la fin de ce chapitre.
448
Chapitre 11
le pattern proxy
.
Regardez !
Les trois principales erreurs que les programmeurs commettent avec RMI: 1) Oublier de lancer rmiregistry avant de dmarrer le service distant (quand le service est enregistr au moyen de Naming.rebind(), le registre RMI doit tre en train de sexcuter!) 2) Oublier de rendre les arguments et les types de retour srialisables (vous ne le saurez pas avant lexcution parce que le compilateur ne le dtectera pas). 3) Oublier de donner la classe souche au client.
Client
Serveur
S qu e
Ob
jet client
101101 10 110 1 0 11 0 001 10 001 01
lette
S ou c h e
Ob
jet service
S ou c h e
101101 10 110 1 0 11 0 001 10 001 01 101101 10 110 1 0 11 0 001 10 001 01 101101 10 110 1 0 11 0 001 10 001 01
Noubliez pas que le client utilise linterface distante pour appeler les mthodes sur la souche. La JVM du client Client.class MonServiceImpl_Stub.class a besoin de la classe de la souche, mais le client ny fait jamais rfrence dans le code. Le client utilise MonService.class toujours linterface distante, Comme si elle ETAIT lobjet distant rel.
101101 10 110 1 0 11 0 001 10 001 01
MonServiceImpl.class
101101 10 110 1 0 11 0 001 10 001 01
MonServiceImpl_Stub.class
101101 10 110 1 0 11 0 001 10 001 01
MonServiceImpl_Skel.class
MonService.class
Le serveur a besoin des classes de la souche et du squelette ainsi que du service et de linterface distante. Il a besoin de la classe de la souche parce que, souvenez-vous, la souche est substitue au service rel quand celui-ci est li au registre RMI.
449
FIN DV DE IATIO N
D Ordinateur du P
Voici le code de notre con- se trleur. Il utili un proxy pour converser avec rs les distributeu distants.
rib
rib
ributeur e Le Distr servic est not .eIl va distant une exposer ce distante interfa lient pourra que le c utiliser.
450
Chapitre 11
le pattern proxy
java.rmi.*
public interface DistributeurDistant extends Remote { public int getNombre() throws RemoteException; public String getEmplacement() throws RemoteException; public Etat getEtat() throws RemoteException; }
prendre en charge. Et voil les mthodes que nous allons ept Chacune delles lance une RemoteExc ion..
Nous navons quun seul type de retour qui nest pas srialisable: la classe Etat. Arrangeons cela...
import java.io.*;
Serializable est dans le package java.io. erface Nous nous contentons dtendre lintmt hode). ne aucu Serializable (qui ne contient Maintenant, ltat de toutes les sous-classes peut tre transfr sur le rseau.
public interface Etat extends Serializable { public void insererPiece(); public void ejecterPiece(); public void tournerPoignee(); public void delivrer(); }
451
En ralit, nous nen avons pas encore fini avec Serializable: nous avons un problme avec Etat. Comme vous vous en souvenez peut-tre, chaque objet Etat maintient une rfrence un distributeur pour pouvoir appeler ses mthodes et modifier son tat. Mais nous ne voulons pas que tout le distributeur soit srialis et transfr avec lobjet Etat. La solution est simple:
public class EtatSansPiece implements Etat { transient Distributeur distributeur; // toutes les autres mthodes }
Dans chaque implmentation dEtat, nous ajoutons le mot-cl transient ce la dclaration de la variable dinstan distributeur. Nous indiquons ainsi la JVM quelle ne doit pas srialiser ce champ.
Nous avons dj implment notre Distributeur, mais nous devons nous assurer quil peut se comporter en service et grer les requtes qui lui parviennent sur le rseau. Pour ce faire, nous devons vrifier que le Distributeur fait tout ce quil faut pour implmenter linterface DistributeurDistant. Comme vous lavez vu dans notre dtour par RMI, lopration est trs simple: il suffit dajouter deux ou trois lignes de code...
public class Distributeur extends UnicastRemoteObject implements DistributeurDistant { // variables dinstance public Distributeur(String emplacement, int nombreBonbons) throws RemoteException { // reste du code du constructeur } public int getNombre() { return nombre; } public Etat getEtat() { return etat; } public String getEmplacement() { return emplacement; } // autres mthodes }
...et le constructeur doit lancer une exception, parce que la superclasse le fait.
452
Chapitre 11
le pattern proxy
devons dabord ajouter un bloc try/ catch autour de linstanciation du dist ributeur, parce que notre constructeur peut mainte nant lancer des exceptions.
} }
Distributeur distributeur = new Distributeur(args[0], count); Naming.rebind(// + args[0] + /distributeur, distributeur); } catch (Exception e) { e.printStackTrace(); } Nous ajoutons galement lappel
de du souche la publie qui d(), g.rebin Namin r. buteu distri nom le sous r ibuteu Distr
Excutons le test...
% rmiregistry
Fichier dition Fentre Aide Comment?
453
que nous age RMI parce us ck pa le er rt ... po Nous devons im se RemoteException ci-desso utilisons le clas sur import java.rmi.*; us nous reposonsr la classe Maintenant, not te et non su linterface discoan public class ControleurDistrib { rte. Distributeur nc DistributeurDistant machine;
public ControleurDistrib(DistributeurDistant machine) { this.machine = machine; } public void rapport() { try { System.out.println(Distributeur: + machine.getEmplacement()); System.out.println(Stock Courant: + machine.getNombre() + bonbons); System.out.println(tat courant: + machine.getEtat()); } catch (RemoteException e) { e.printStackTrace(); } Nous devons galement intercepter toutes } les }
exceptions distantes qui pourraient survenir quand nous essayons dinvoquer des mt sexcutent finalement sur le rseau. hodes qui
454
Chapitre 11
le pattern proxy
placements Voici tous les em ntrler. que nous allons co Nous crons un tableau demplacements, un pour chaque machine.
public static void main(String[] args) { String[] emplacement = {rmi://rennes.distribon.com/distributeur, rmi://lille.distribon.com/distributeur, rmi://marseille.distribon.com/distributeur}; ControleurDistrib[] controleur = new ControleurDistrib[emplacement.length]; for (int i=0;i < emplacement.length; i++) { try { DistributeurDistant machine = (DistributeurDistant) Naming.lookup(emplacement[i]); controleur[i] = new ControleurDistrib(machine); System.out.println(controleur[i]); } catch (Exception e) { e.printStackTrace(); } } Il nous faut for(int i=0; i < controleur.length; i++) { controleur[i].rapport(); } } }
ne Puis nous itrons sur chaque machi t. por rap un et nous affichons
455
le proxy du distributeur
Code la loupe
Ce code retourne un proxy au Distributeur distant (ou lance une exception sil nen trouve pas).
try { DistributeurDistant machine = (DistributeurDistant) Naming.lookup(emplacement[i]); controleur[i] = new ControleurDistrib(machine); } catch (Exception e) { e.printStackTrace(); }
Souvenez-vous: Naming.lookup() est une mthode statique du package RMI qui prend un emplacement et un nom de service et recherche dans cet emplacement dans le registre RMI.
ons un proxy pour av us no e qu is fo ne U te, nous crons un lui la machine distan rDistrib et nous r. nouveau Controleu hine contrle transmettons la mac
Sur chaque machine, excutez rmiregistry en arrire-plan ou dans une fentre de commandes spare....
Ficher dition Fentre Aide Comment?
le pattern proxy
Et maintenant, mettons le contrleur entre les mains du PDG. Esprons que cette fois il sera content
% java TestControleurDistrib Distributeur: rennes.distribon.com Stock courant: 99 bonbons Etat courant: en attente de pice Distributeur: lille.distribon.com Stock courant: 44 bonbons Etat courant: en attente de poigne tourne Distributeur: marseille.distribon.com Stock courant: 187 bonbons Etat courant: en attente de pice %
Cest stupfiant! Ce programme va rvolutionner notre mtier et balayer la concurrence!
Le contrleur itre sur chaque machine distante et appelle ses mthodes getEmplacement(), getNombre() et getEtat().
Quand on invoque des mthodes sur le proxy, un appel distant est transmis sur le rseau. Il retourne une chane de caractres, un entier et un objet Etat. Comme nous utilisons un proxy, le ControleurDistrib ne sait pas, et na pas besoin de savoir, que les appels sont distants (mais il doit quand mme se soucier des exceptions).
vous tes ici
457
Le PDG excute le contrleur. Celui-ci accde dabord aux proxies des distributeurs distants, puis il appelle getEtat() sur chacun (ainsi que getNombre() et getEmplacement()).
D Ordinateur du P
rDistant
tat() getE
trib
Co
is ntroleurD
h P ro xy/Souc
e pr ox
yr et ou rn
S qu e
2
1 lo o
Proxy/Souche
458
Chapitre 11
le pattern proxy
getEtat() est appele sur le proxy, qui transmet lappel au service distant. Le squelette reoit la requte, puis la fait suivre au distributeur.
getEtat()
tat() getE
Co
is ntroleurD
Le Distributeur retourne ltat au squelette, qui le srialise et le retransfre au proxy via le rseau. Le proxy le dsrialise et le retourne sous forme dobjet au contrleur.
Etat srialis Objet Etat Objet Etat
trib
Pro he xy/Souc
S qu e
Co
is ntroleurD
Pr e oxy /Souch
chang a pas du tout ut n ur le r nt co e L il sait quil pe du tout, sauf quexceptions distantes. rencontrer des ent linterface Il utilise galemistant au lieu dune DistributeurDn concrte. implmentatio
trib
S qu e
De mme, le Distributeur implmente une autre interface et peut lancer une exception distante dans son constructeur. Ceci mis part, le code na pas chang.
Il faut galement un petit fragment de code pour enregistrer et localiser les souches en utilisant le registre RMI. Mais, en tout tat de cause, si nous crivions quelque chose qui doive fonctionner sur lInternet, il nous faudrait une forme ou une autre de service de localisation.
vous tes ici
459
Bien. Nous avons vu comment le pattern Proxy fournissait un remplaant un autre objet. Nous avons galement dcrit un proxy comme un reprsentant dun autre objet. Mais quen estil du contrle daccs? Un peu trange, non? Ne vous inquitez pas. Dans le cas du distributeur de bonbons, pensez simplement que le proxy contrle laccs lobjet distant. Il doit contrler laccs parce que notre client, le contrleur, ne sait pas comment sadresser un objet distant. Donc, dans un certain sens, le proxy distant contrle laccs pour pouvoir grer notre place les dtails lis au rseau. Comme nous lavons mentionn, il existe de nombreuses variantes du pattern Proxy et ces variantes tournent gnralement autour de la faon dont le proxy contrle laccs. Nous y reviendrons plus tard. Pour linstant, voici un certain nombre de types de contrle daccs:
Utilisez le pattern Proxy pour crer un objet remplaant qui contrle laccs un autre objet qui peut tre distant, coteux crer ou qui doit tre scuris.
460
Chapitre 11
le pattern proxy
<<interface>>
Sujet
requte()
jetRel Le Proxy et le Su us deux implmentent tot. Cela permet linterface Sujetraiter le proxy tout client de mme le SujetRel. exactement co
SujetRel
requte()
sujet
Proxy
requte()
Le SujetRel est Le Proxy instancie souvent gnralement lobjet qui ralise la plus grande partie le SujetRel ou en gre la cration. du vrai travail. Le Proxy en contrle laccs.
conserve une Comme le Proxy ujet, il peut rfrence au Se les requtes lui transmettr ce ssaire. lorsque cest n
Parcourons le diagramme... Tout dabord, nous avons un Sujet qui fournit une interface pour le SujetRel et le Proxy. Puisquils implmentent la mme interface, le Proxy peut toujours tre substitu au SujetRel. Le SujetRel est lobjet qui fait le vrai travail. Cest lui que le Proxy reprsente et auquel il contrle laccs. Le Proxy contient une rfrence au SujetRel. Dans certains cas, le Proxy peut tre responsable de la cration et de la destruction du SujetRel. Les clients interagissent avec le SujetRel via le Proxy. Comme le Proxy et le SujetRel implmentent la mme interface (Sujet), le Proxy peut tre substitue au sujet partout o ce dernier peut tre utilis. Le Proxy contrle galement laccs au SujetRel; ce contrle peut tre ncessaire si le sujet sexcute sur une machine distante, si le sujet est coteux crer ou si laccs au sujet doit tre protg pour une raison quelconque. Maintenant que vous comprenez le pattern gnrique, voyons un certain nombre dautres faons dutiliser un proxy en dehors du Proxy Distant...
461
proxy virtuel
Proxy Distant
Avec Proxy Distant, le proxy se comporte comme un reprsentant local dun objet qui rside dans une JVM diffrente. Quand on appelle une mthode sur le proxy, lappel est transfr sur le rseau, la mthode est invoque distance et le rsultat est retourn au proxy, puis au Client.
te() requ
te() requ
C li e n t
Proxy
Su
jetR el
ux crer.
Proxy Virtuel
Proxy Virtuel agit en tant que reprsentant dun objet dont la cration peut tre coteuse. Il diffre souvent la cration de lobjet jusquau moment o elle est ncessaire. Il sert galement de substitut lobjet avant sa cration. Aprs quoi, le proxy dlgue les requtes directement au SujetRel.
te() requ
C li e n t
Le proxy peut grer les requtes, ou, si le SujetRel a t cr, lui dlguer les appels.
Su
jetR el
462
Chapitre 11
le pattern proxy
463
Voici linterface Swing Icon qui sert afficher des images dans une interface utilisateur.
<<interface>>
Icon
ImageIcon
getIconWidth() getIconHeight() paintIcon()
sujet
ProxyDImage
getIconWidth() getIconHeight() paintIcon()
Voil notre proxy. Il commence par afficher un message. Quand limage est charge, il dlgue son affichage ImageIcon.
ProxyDImage cre dabord une ImageIcon et commence la charger partir dune URL donne. Pendant quil rcupre les octets qui composent limage, ProxyDImage affiche Chargement en cours, patientez.... Lorsque limage est entirement charge, ProxyDImage dlgue tous les appels de mthode ImageIcon, notamment paintIcon(), getWidth() et getHeight(). Si lutilisateur demande une nouvelle image, nous crons un nouveau proxy et nous rptons le processus.
464
Chapitre 11
le pattern proxy
crire ProxyDImage
class ProxyDImage implements Icon { ImageIcon image; URL urlImage; Thread threadChargement; boolean chargement = false;
<<interface>>
Icon
public ProxyDImage(URL url) { urlImage = url; } public int getIconWidth() { if (image != null) { return image.getIconWidth(); } else { return 800; } } public int getIconHeight() { if (image != null) { return image.getIconHeight(); } else { return 600; } }
image est licne RELLE, celle que nous voulons voir safficher quand elle aura t charge. Nous transmettons lURL de limage dans le constructeur. Cest limage que nous voulons tlcharger et afficher! Nous retournons une largeur et une hauteur par dfaut tant que limage nest pas charge. Ensuite, nous retournons les valeurs relles.
public void paintIcon(final Component c, Graphics g, int x, int y) { if (image != null) { image.paintIcon(c, g, x, y); } else { g.drawString(Chargement en cours. Patientez..., x+300, y+190); if (!chargement) { chargement = true; threadChargement = new Thread(new Runnable() { public void run() { try { image = new ImageIcon(urlImage, Pochette de CD); c.repaint(); } catch (Exception e) { e.printStackTrace(); } Cest l que cela devient intressant. } Cette portion de code peint }); limage lcran (en dlguant image). threadChargement.start(); Mais si nous navons pas dImageIcon } } compltement cre, nous en crons } une. Nous allons voir cela de plus prs
page suivante...
465
ProxyDImage la loupe
Code la loupe
Cette mthode est appele quand il est temps dafficher limage lcran.
public void paintIcon(final Component c, Graphics g, int x, int y) { if (image != null) { image.paintIcon(c, g, x, y); } else {
chargement = true; threadChargement = new Thread(new Runnable() { public void run() { try { image = new ImageIcon(urlImage, Pochette de CD); c.repaint(); } catch (Exception e) { e.printStackTrace(); } } }); threadChargement.start(); } } }
z ELLE. Note R e g a im l s n st nous chargeo c IconImage e ermine t Cest l quegement dune image aveg se e n on Ic e r a a m h I c d la ne le r u que constructe pas charge. Comme ce le : e n o r h rans c c syn limage nest de mettre jour les er de e u q t n a t s e d pa aucune chancessage, nous allons procC ode au nous laisse h m e r s n not da s r e il a t ic f d f s a le d s et nchrone. Tou ... manire asy page suivante e p o sc o r ic m
466
Chapitre 11
le pattern proxy
Code au microscope
de rcuprer limage... Si nous ne sommes pas dj en train faire (au cas o vous poseriez ...il est temps de commencer le paint(): nous pouvons donc la question, un seul thread appelle on des threads!). tre tranquilles sur lordre dexcuti (!chargement) { Comme nous ne voulons pas que se chargement = true; linterface utilisateur risque der un threadChargement = new Thread(new Runnable() { planter, nous allons utilise public void run() { autre thread pour extraire limage.
if
} });
try { image = new ImageIcon(urlImage, Pochette de CD); c.repaint(); Dans notre thread, } catch (Exception e) { n ous instancio e.printStackTrace(); lobjet Icon. ns } Son
threadChargement.start(); }
Quand nous avons limage, nous disons Swing quil faut la repeindre.
Ainsi, la prochaine fois que laffichage changera aprs linstanciation dImageIcon, la mthode paintIcon() affichera limage, pas le message.
467
problme de conception
Problme de conception
Il semblerait que la classe ProxyDImage possde deux tats qui sont contrls par des instructions conditionnelles. Connaissez-vous un autre pattern qui pourrait permettre darranger ce code? Comment pourriez-vous revoir la conception de ProxyDImage?
class ProxyDImage implements Icon { // variables dinstance et constructeur public int getIconWidth() { if (image != null) { return image.getIconWidth(); } else { return 800; } } public int getIconHeight() { if (image != null) { return image.getIconHeight(); } else { return 600; } }
Deux tats
Deux tats
public void paintIcon(final Component c, Graphics g, int x, int y) { if (image != null) { image.paintIcon(c, g, x, y); } else { g.drawString(Chargement en cours. Patientez..., x+300, y+190); // reste du code } } }
Deux tats
468
Chapitre 11
le pattern proxy
Ici, nous crons un proxy pour limage et nous lui affectons une URL initiale. Chaque fois que vous effectuez une slection dans le menu, vous obtenez un nouveau proxy.
Puis nous enveloppons notre proxy dans un composant pour pouvoir lajouter au cadre. Le composant se chargera de la taille du proxy et autres dtails similaires.
% java TestProxyDImage
Utilisez le menu pour charger diffrentes pochettes de CD; regardez le proxy afficher le message jusqu larrive de limage. Redimensionnez la fentre pendant laffichage du message. Remarquez que le proxy gre le chargement sans planter la fentre Swing. Ajoutez vos propres CD favoris TestProxyDImage.
469
Quavons-nous fait?
1
Nous avons cr un ProxyDImage pour laffichage. La mthode paintIcon() est appele et ProxyDImage initialise un thread pour extraire limage et crer lobjet ImageIcon.
paintIcon()
obtenir limage
e Pro xyDImag
affiche le message de chargement
Im ag
eIcon
image obtenue
Im ag
eIcon
Quand lImageIcon est cre, la prochaine fois que paintIcon() est appele, le proxy dlgue lImageIcon.
paintIcon()
paintIcon()
e Pro xyDImag
affiche limage relle
Im ag
eIcon
470
Chapitre 11
le pattern proxy
questions stupides
il ny a pas de
Q: R:
Le Proxy Distant et le Proxy Virtuel me semblent vraiment trs diffrents; sagit-il vraiment du MME pattern?
Vous trouverez beaucoup de variantes du pattern Proxy dans le monde rel. Ils prsentent tous un point commun: ils interceptent une invocation de mthode que le client effectue sur le sujet. Ce niveau dindirection nous permet de faire beaucoup de choses, notamment transmettre des requtes un sujet distant, procurer un reprsentant un objet dont la cration est coteuse, ou, comme nous allons le voir, fournir un certain niveau de protection capable de dterminer quels clients peuvent appeler certaines mthodes. Et ce nest quun dbut: le pattern Proxy a un grand nombre dapplications diffrentes et nous en aborderons quelques unes la fin de ce chapitre.
Cest vrai en un sens, mais ce nest pas le plus important. Le plus important, cest que ProxyDImage contrle laccs une ImageIcon. Et comment contrlet-il laccs? Vous pouvez penser en ces termes: le proxy dcouple le client de lImageIcon. Sils taient coupls, le client devrait attendre que limage soit charge avant dafficher toute son interface. Le proxy contrle laccs lImageIcon en affichant une autre reprsentation lcran tant quelle nest pas entirement cre. Une fois lImageIcon cre, le proxy autorise laccs.
Vous parlez l dune forme spcialise de Proxy virtuel nomme Proxy de mise en cache. Un tel proxy mmorise tous les objets crs antrieurement et, si cest possible, retourne lobjet en cache quand une requte est mise. Nous verrons cette variante du pattern Proxy ainsi que quelques autres la fin du chapitre.
R:
Q: R:
Je vois bien le rapport entre Dcorateur et Proxy, mais quen est-il dAdaptateur? Un adaptateur semble galement trs similaire. Proxy et Adaptateur sinterposent tous deux entre le client et un objet et lui transmettent les requtes. Mais noubliez pas quun adaptateur modifie linterface de lobjet quil adapte alors quun proxy implmente la mme interface. Il existe aussi une autre similarit en rapport avec le Proxy de Protection. Un proxy de protection peut autoriser ou interdire un client daccder certaines mthodes dun objet, en fonction du rle de ce client. Il peut donc se contenter doffrir une interface partielle un client, de faon assez similaire celle de certains adaptateurs. Nous allons jeter un coup dil un proxy de protection dans quelques pages.
Q: R:
Q:
Pour moi, ProxyDImage ressemble tout fait un Dcorateur. Je veux dire quen substance nous enveloppons un objet dans un autre, puis que nous dlguons les appels lImageIcon. Est-ce que jai loup quelque chose?
Bonne question. Une technique courante consiste fournir un objet fabrique qui instancie et retourne le sujet. Comme il sagit dune mthode de fabrique, nous pouvons envelopper lobjet dans un proxy avant de le retourner. Le client ne sait jamais quil utilise un proxy la place du sujet rel, et il ne sen soucie mme pas.
Q:
Il arrive que Proxy et Dcorateur aient lair trs similaires, mais leurs objectifs sont diffrents. Un dcorateur ajoute un comportement une classe, tandis quun proxy en contrle laccs. Vous pourriez rtorquer que le message de chargement ajoute un comportement.
R:
Dans lexemple de ProxyDImage, jai remarqu quon crait toujours un nouvel objet ImageIcon pour obtenir limage, mme si limage avait dj t charge. Pourrait-on implmenter quelque chose de similaire ProxyDImage pour rcuprer les chargements antrieurs?
471
Face face :
Proxy
Bonjour, Dcorateur. Je prsume que vous tes l parce quon nous confond parfois?
Dcorateur
Eh bien je crois que les gens nous confondent parce que vous vous baladez en prtendant tre un pattern totalement diffrent alors que vous ntes en fait quun Dcorateur dguis. Et je pense que vous devriez arrter de copier toutes mes ides.
Moi? Copier vos ides? Sil vous plat! Je contrle laccs aux objets. Vous, vous vous contentez de les dcorer. Mon rle est tellement plus important que le vtre que ce nest mme pas drle.
Daccord, vous ntes peut-tre pas entirement frivole... Mais je ne comprends toujours pas pourquoi vous pensez que je copie toutes vos ides. Je sers uniquement reprsenter mes sujets, pas les dcorer.
Me contenter de dcorer? Vous croyez que la dcoration est une tche frivole et sans importance? Laissez-moi vous dire quelque chose, mon pote: jajoute des comportements. Cest la chose la plus importante pour un objet ce quil fait!
Jai limpression que vous navez pas trs bien saisi, Dcorateur. Je ne me contente pas dajouter des comportements: je remplace mes sujets. Les clients mutilisent comme un substitut du sujet rel, parce que je les protge contre les accs indsirables ou parce que jempche leur IHM de ramer pendant quils attendent le chargement dun gros objet, ou encore parce que je masque le fait que leurs sujets sexcutent sur des machines distantes. Je dirais que cest l un objectif trs diffrent du vtre!
Vous avez beau parler de reprsentation. Mais si a ressemble un canard et que a marche comme un canard... Regardez donc votre Proxy Virtuel: cest juste une autre faon dajouter un comportement pour faire quelque chose pendant quon charge un gros objet coteux, et votre Proxy Distant est un moyen de communiquer avec des objets distants pour que vos clients naient pas besoin de sen occuper eux-mmes. Comme je viens de le dire, il ny a l que des comportements.
Dites ce que vous voudrez. Jimplmente la mme interface que les objets que jenveloppe, et vous aussi.
472
Chapitre 11
le pattern proxy
Proxy
Daccord. Je retire ce que jai dit. Vous enveloppez un objet. Mais si je dis parfois de faon informelle quun proxy enveloppe son sujet, ce nest pas vraiment le terme exact. Pensez un proxy distant... Quel est lobjet que jenveloppe? Lobjet que je reprsente et dont je contrle laccs rside sur une autre machine! Quest-ce que vous en dites?
Dcorateur
Ah oui? Pourquoi?
Daccord, mais on sait bien que les proxies distants sont un peu bizarres. Vous avez un autre exemple? Cela mtonnerait.
Bien sr. Prenez les proxies virtuels... Pensez lexemple du programme qui affiche des pochettes de CD. Quand le client mutilise comme proxy, le sujet nexiste mme pas! Quest-ce que je pourrais bien envelopper? Je ne savais pas que les dcorateurs taient aussi stupides! Bien sr quil marrive de crer des objets. Comment croyez-vous quun proxy virtuel obtient son sujet? Et puis vous venez de souligner une autre grosse diffrence entre nous: nous savons tous les deux que les dcorateurs se contentent de faire de lhabillage et quils ninstancient jamais rien.
Ah ah! Et vous allez bientt me dire quen fait vous crez des objets.
Ah oui? Instanciez-moi donc a! Eh, aprs cette conversation je suis convaincu que vous ntes quun proxy passif !
Un proxy passif!? Jaimerais bien vous voir envelopper rcursivement un objet avec dix dcorateurs et garder la tte droite en mme temps.
Cest bien rare quon voit un proxy saventurer envelopper un objet plusieurs fois. En fait, si vous enveloppez quelque chose dix fois, vous feriez peuttre bien de rexaminer votre conception. Et que dire dun proxy qui fait semblant dtre rel alors quil ne fait que servir dintermdiaire avec les objets qui font le vrai travail? Tenez, vous me faites de la peine.
vous tes ici
473
proxy de protection
ES IR TA ILI UT JAVA
ler, auquel sont Vous fournissez lInvocationHand e qui sont hod transmis tous les appels de mt ionH ler contrle cat invoqus sur le Proxy. LInvo Sujand el. etR du laccs toutes les mthodes
Comme Java cre votre place la classe Proxy, il vous faut un moyen dindiquer celle-ci ce quelle doit faire. Vous ne pouvez pas placer ce code dans la classe Proxy comme nous lavons fait tout lheure, parce que nous ne limplmentons pas directement. Alors, si vous ne pouvez pas insrer ce code dans la classe Proxy, o allez-vous le placer? Dans un InvocationHandler. La tche de lInvocationHandler consiste rpondre tous les appels de mthode envoys au proxy. Reprsentez-vous lInvocationHandler comme lobjet auquel le Proxy demande de faire tout le vrai travail aprs quil a reu les appels de mthode. Voyons maintenant quelles sont les tapes ncessaires pour utiliser le proxy dynamique...
474
Chapitre 11
le pattern proxy
NON
public interface BeanPersonne { String getNom(); String getSexe(); String getInterets(); int getSexyOuNon(); void void void void } setNom(String nom); setSexe(String sexe); setInterets(String interets); setSexyOuNon(int note);
Voil o nous sur le nom de obtenons les information ses centres d la personne, son sexe, s SexyOuNon (dintrt et son niveau e 1 10).
Nous pouvons galement modifier les mmes informations au moyen de leurs appels de mthode respectifs.
Regardons maintenant limplmentation
475
Limplmentation de BeanPersonne
BeanPersonneImpl implmente linterface BeanPersonne
public class BeanPersonneImpl implements BeanPersonne { String nom; String sexe; Les variables dinstance. String interets; int note; int nombreDeNotes = 0; public String getNom() { return nom; } public String getSexe() { return sexe; } public String getInterets() { return interets; } public int getSexyOuNon() { if (nombreDeNotes == 0) return 0; return (note/nombreDeNotes); } public void setNom(String nom) { this.nom = nom; } public void setSexe(String sexe) { this.sexe = sexe; } public void setInterets(String interets) { this.interets = interets; } public void setSexyOuNon(int note) { this.note += note; nombreDeNotes++; } }
Toutes les mthodes get. Chacune delles retourne le contenu de la variable dinstance approprie...
...sauf getSexyOuNon() qui calcule la moyenne en divisant les notes par le nombreDeNotes.
Et voici toutes les mthodes set qui modifient les variables dinstance correspondantes.
Enfin, la mthode setSexyOuNon() in nombreDeNotes etcrmente note au total cour ajoute la ant.
476
Chapitre 11
le pattern proxy
Je narrivais pas obtenir des rendezvous. Et puis je me suis rendu compte que quelquun avait modifi mes centres dintrt. Jai aussi remarqu que beaucoup de gens gonflaient leurs scores SexyOuNon en sattribuant des notes trs leves. On ne devrait pas pouvoir modifier les centres dintrt de quelquun dautre ou se donner soi-mme des notes!
Mme si nous souponnons que dautres facteurs pourraient bien expliquer les checs dric, il a nanmoins raison: on ne devrait pas pouvoir voter pour soi ni changer les donnes dun autre client. De la faon dont notre BeanPersonne est dfini, nimporte quel client peut appeler nimporte quelle mthode. Voil un exemple parfait de situation dans laquelle nous pourrions utiliser un Proxy de Protection. Quest-ce quun Proxy de Protection? Cest un proxy qui contrle laccs un objet en fonction de droits daccs. Si par exemple nous avions un objet Employ, un proxy de protection pourrait permettre lemploy dappeler certaines mthodes tandis quun manager pourrait appeler des mthodes supplmentaires (comme setSalaire()) et quun agent des ressources humaines pourrait appeler toutes les mthodes de lobjet. Dans notre service de rencontres, nous voulons tre certains quun client peut modifier les informations qui le concernent tout en empchant les autres den faire autant. Et nous voulons exactement le contraire avec les notes SexyOuNon: les autres clients doivent pouvoir attribuer la note, mais pas ce client particulier. Le BeanPersonne possde galement un certain nombre de mthodes get, et, comme aucune dentre elles ne retourne dinformations confidentielles, tout client doit pouvoir les appeler.
ric
477
comdie express
Elle est prise... euh ... par une runion en ce moment. quoi pensiez-vous?
Jacques Poincom
Agent
roxy de Comme un p, lagent protection ccs son sujet protge la passer que et ne laisse pels... certains ap
Allons, allons! Vous perdez votre temps! Aucune chance! Rappelez-nous quand vous aurez une meilleure offre.
Nous pensons pouvoir lui offrir 15% de plus que son salaire actuel.
478
Chapitre 11
le pattern proxy
Pour crer ces proxies, nous allons utiliser le proxy dynamique de lAPI Java que vous avez vu il y a quelques pages. Java va crer deux proxies pour nous et il nous suffira de fournir des gestionnaires qui sachent quoi faire quand une mthode est appele sur un proxy.
<<interface>> Sujet
requte()
invoke()
tape 1:
Crer deux InvocationHandler, autrement dit deux gestionnaires dinvocations. Les InvocationHandler implmentent le comportement du proxy. Comme vous allez le voir, Java se chargera de crer la classe et lobjet proxy, et il nous faudra seulement fournir le gestionnaire qui sait ce quil faut faire lors dun appel de mthode.
RealSubject requte()
Proxy requte()
InvocationHandler invoke()
tape2:
crire le code qui cre les proxies dynamiques. Nous devons crire un peu de code pour gnrer la classe proxy et linstancier. Nous tudierons ce code dans quelques instants.
Proxy
InvocationHandlerProprietaire
invoke()
tape3:
Envelopper chaque objet BeanPersonne dans le proxy qui convient. Quand nous avons besoin dutiliser un objet BeanPersonne, soit il sagit de lobjet du client lui-mme (auquel cas nous lappelons le propritaire), soit il sagit de celui dun autre client du service (et nous lappellerons nonpropritaire). Dans les deux cas, nous crons le proxy appropri pour le BeanPersonne.
requte()
n
un dautre.
InvocationHandlerNonProprietaire
479
invoke()
Cette interface ne contient quune seule mthode, invoke(), et, quelle que soit la mthode appele sur le proxy, cest cette mthode invoke() qui est appele sur le gestionnaire. Voyons comment cela fonctionne:
proxy.setSexyOuNon(9);
3 Le gestionnaire dtermine ce quil doit faire de la requte et la transmet ventuellement au SujetRel. Comment le gestionnaire dcide-t-il? Nous allons bientt le savoir.
La classe Method, qui fait partie du package java.lang. e reflect, nous dit quelle mthod a t appele sur le proxy via sa mthode getName().
arguments Ici, nous invoquons la Mais maintenant avec les mthode initiale qui a t nous lappelons sur dorigine. appele sur le proxy. Cet le SujetRel... objet nous a t transmis dans lappel de invoke().
480
Chapitre 11
le pattern proxy
tie du package java. Comme InvocationHandler fait par lang.reflect, nous devons limporter.
import java.lang.reflect.*;
Tous les gestionnaires dinvocations Le et rel nous implmentent linterface est suj transmis dans le InvocationHandler. constructeur et nous mmorisons une rfrence celui-ci.
public class InvocationHandlerProprietaire implements InvocationHandler { BeanPersonne personne; public InvocationHandlerProprietaire(BeanPersonne personne) { this.personne = personne; } public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException { try { if (method.getName().startsWith(get)) { return method.invoke(personne, args); } else if (method.getName().equals(setSexyOuNon)) { throw new IllegalAccessException(); } else if (method.getName().startsWith(set)) { return method.invoke(personne, args); } } catch (InvocationTargetException e) { e.printStackTrace(); } return null; Comme no
Voici la mthode invoke() qui est appele chaque fois quune mthode est invoque sur le proxy. Si cest une mthode get, nous continuons et nous lappelons sur le sujet rel. Sinon, si cest la mthode setSexyOuNon(), nous empchons de lappeler en lanant une IllegalAccessException.
} }
Si une autre mthode qu elconque est appele, nous prf r on retourner null au lieu de s prendre un risque.
us sommes le propritaire, nous avons le droit dappeler nimporte quelle mthode set. Nous continuons et nous lappelons sur le sujet rel.
481
Exercice
InvocationHandlerNonProprietaire fonctionne exactement comme InvocationHandlerProprietaire, except quil autorise les appels de setSexyOuNon() et quil interdit ceux de toutes les autres mthodes set. Allez-y! crivez ce gestionnaire.
482
Chapitre 11
le pattern proxy
Cette mthode accepte un objet personne le (le sujet rel) et retourne un proxy qui va e remplacer. Comme le proxy a la mme interfac que le sujet, nous retournons un BeanPersonne.
Cette portion de code cre le proxy. Comme elle est un peu complique, nous allons ltudier soigneusement. Pour crer un proxy, nous utilisons la mthode statique newProxyInstance() de la classe Proxy... Nous lui transmettons le chargeur de classe pour notre sujet...
BeanPersonne getProxyProprietaire(BeanPersonne personne) { return (BeanPersonne) Proxy.newProxyInstance( personne.getClass().getClassLoader(), personne.getClass().getInterfaces(), new InvocationHandlerProprietaire(personne)); }
Nous transmettons le sujet rel dans le constructeur du gestionnaire dinvocations. Si vous vous reportez deux pages en arrire, vous verrez que cest ainsi que le gestionnaire accde au sujet rel.
Mme si elle est un peu complique, la cration dun proxy dynamique nest pas bien difficile. Pourquoi ncririez-vous pas getProxyNonProprietaire(), qui retourne un proxy pour InvocationHandlerNonProprietaire?
Encore plus fort: pouvez-vous crire une mthode getProxy() qui accepte en argument un gestionnaire et une personne et qui retourne un proxy qui utilise ce gestionnaire?
483
public class TestRencontres { // variables dinstance public static void main(String[] args) { TestRencontres test = new TestRencontres(); test.tester(); } public TestRencontres() { initialiserBD(); }
La mthode main() cre simplement le test et appelle sa mthode tester(). Le constructeur initialise la base de donnes des personnes abonnes notre service de rencontres. Extrayons une personne de la base de donnes
public void tester() { BeanPersonne luc = getPersonneDepuisBD(Luc Javabine); ...et crons un proxy BeanPersonne proxyProprietaire = getProxyProprietaire(luc); System.out.println(Nom = + proxyProprietaire.getNom()); propritaire. proxyProprietaire.setInterets(tennis, jeu de go); Appelons une mthode get System.out.println(Intrts fixs par le proxy propritaire); puis une mthode set try { puis essayons de proxyProprietaire.setSexyOuNon(10); modifier la note } catch (Exception e) { System.out.println(Le proxy propritaire ne peut pas modifier la note); } ceci ne doit pas marcher! System.out.println(Note = + proxyProprietaire.getSexyOuNon());
BeanPersonne proxyNonProprietaire = getProxyNonProprietaire(luc); System.out.println(Nom = + proxyNonProprietaire.getNom()); try { ...et appelons une mthode get proxyNonProprietaire.setInterets(tennis, jeu de go); } catch (Exception e) { suivie dune mthode set System.out.println(Le proxy non propritaire ne peut pas modifier les intrts); } Ceci ne doit pas marcher! proxyNonProprietaire.setSexyOuNon(3); System.out.println(Note modifie par le proxy non propritaire); System.out.println(Note = + proxyNonProprietaire.getSexyOuNon()); Puis essayons de
changer la note
484
Chapitre 11
le pattern proxy
Excuter le code...
Ficher dition Fentre Aide Dynamiser
% java TestRencontres Nom = Luc Javabine Intrts fixs par le proxy propritaire
Notre proxy propritaire autorise les accs et les modifications, sauf pour la note SexyOuNon.
Notre proxy nonpropritaire nautorise que les accs, mais il permet galement de modifier la note SexyOuNon.
Nom = Luc Javabine Note modifie par le proxy non propritaire Note = 5 %
485
questions stupides
il ny a pas de
Q:
Que signifie donc le mot dynamique dans lexpression proxy dynamique? Est-ce li au fait que jinstancie le proxy et que je laffecte un gestionnaire au moment de lexcution?
Non, le proxy est dynamique parce que sa classe est cre au moment de lexcution. Rflchissez: avant que votre code ne sexcute, il ny a pas de classe proxy. Elle est cre la demande partir de lensemble dinterfaces que vous lui transmettez.
R:
Oui. La classe Proxy a une mthode statique nomme isProxyClass(). Lappel de cette mthode avec une classe retourne true si la classe est un proxy dynamique. part cela, la classe proxy se comportera comme toute autre classe qui implmente un ensemble particulier dinterfaces.
R:
Q: R:
Y a-t-il des restrictions sur le type dinterfaces que je peux transmettre dans newProxyInstance() ?
Vous avez raison, nous navons pas rellement besoin de gnrer des squelettes. partir de Java 1.2, RMI peut transmettre directement les appels des clients au service distant en utilisant lintrospection au moment de lexcution. Mais nous prfrons montrer les squelettes, parce que, du point de vue conceptuel, cela aide comprendre quil y a quelque chose dans les coulisses qui permet la souche du client et au service distant de communiquer.
R:
Q: R:
Mon InvocationHandler ma lair dun proxy bien trange: il nimplmente aucune des mthodes de la classe laquelle il se substitue.
Cest parce que lInvocationHandler nest pas un proxy: cest une classe laquelle le proxy transmet les appels de mthode pour quelle les gre. Le proxy luimme est cr dynamiquement lors de lexcution par la mthode statique Proxy.newProxyInstance( ).
Q:
Existe-t-il un moyen quelconque de savoir si une classe est une classe Proxy?
Oui, il y en a quelques unes. Tout dabord, il est intressant de souligner que nous passons toujours newProxyInstance() un tableau dinterfaces seules les interfaces sont autorises, pas les classes. La restriction la plus importante est que toutes les interfaces non publiques doivent appartenir au mme package. De plus, vous ne pouvez pas avoir des interfaces dont les noms de mthode soient conflictuels (autrement dit, deux interfaces ayant des mthodes dont la signature est la mme). Il y a encore quelques nuances mineures et vous devrez donc un moment donn lire ce qui est crit en petits caractres dans la javadoc.
Q: R:
Jai entendu dire quen Java5, je navais mme plus besoin de gnrer des souches. Est-ce que cest vrai?
Q:
Pourquoi utilisez-vous des squelettes? Je croyais quon sen tait dbarrass depuis Java1.2.
Certainement. En Java 5, RMI et le Proxy dynamique sont fusionns, et les souches sont maintenant gnres dynamiquement en utilisant le Proxy dynamique. La souche de lobjet distant est une instance de java.lang. reflect.Proxy (avec un gestionnaire dinvocations) qui est gnre automatiquement et qui gre tous les dtails de la transmission des appels de mthodes locaux du client lobjet distant. En consquence, vous navez plus du tout besoin dexcuter rmic: tout ce quil faut pour que le client converse avec lobjet distant est gr de manire transparente votre place.
486
Chapitre 11
le pattern proxy
Pattern Dcorateur
Description Enveloppe un autre objet et lui fournit une interface diffrente Enveloppe un autre objet et lui fournit un comportement supplmentaire Enveloppe un autre objet pour en contrler laccs Enveloppe un groupe dobjets pour simplifier leur interface
Faade
Proxy
Adaptateur
487
Proxy pare-feu contrle laccs un ensemble de ressources rseau et protge le sujet des clients malveillants.
s de systmes
Proxy de mise en cache permet de stocker temporairement les rsultats doprations coteuses. Il peut galement permettre plusieurs clients de partager les rsultats afin de rduire les temps de calcul ou de remdier la latence du rseau. 488
Chapitre 11
Habitat: souvent rencontr dans les serveurs web ainsi que dans les systmes de publication et de gestion de contenus.
le pattern proxy
Vu en train de traner dans des JavaSpaces, o il cont r le synchronis un ensemb laccs dobjets dans un environle sous-jacent nement distribu.
Proxy Copy-On-Write (copie-calque) contrle la copie dun objet en la diffrant jusqu ce quun client en ait besoin. Cest une variante du Proxy Virtuel.
Proxy de masquage de la complexit. Comme son nom lindique, masque la complexit dun ensemble de classes et en contrle laccs. Parfois appel Proxy Faade pour des raisons videntes. Il diffre du pattern Faade en ce que le proxy contrle laccs alors que la Faade se contente de fournir une autre interface.
Notes de terrain: ajoutez ici vos observations dautres proxies ltat sauvage:
489
mots-croiss
Ctait un trs LONG chapitre. Pourquoi ne pas vous dtendre en faisant un mots-croiss avant de le terminer?
1 3 6 7 10 4 8
2 5 9
11
12 13 14
15 18 19
16
17
20
21
Horizontalement 3. La classe du proxy _________ est cre au moment de lexcution. 6. Vous pouvez en afficher les pochettes. 8. Sans _______, pas de services. 10. Ce que contrle un proxy. 11. Il faut lexcuter pour enregistrer un service. 16. Toute _________ est transmise au gestionnaire dinvocations. 19. Pas vraiment rel. 20. Fournis par un serveur. 21. Cest ainsi que RMI nomme le proxy. 22. Certains constructeurs nen ont pas. 23. Manque de rendez-vous.
Verticalement 1. Dans RMI, auxiliaire du serveur. 2. Pas C++, pas C#, _______. 3. Distributeur de distributeurs. 4. Entres et sorties familires. 5. Substitut. 7. Similaire Proxy, son intention est diffrente. 9. Rencontres. 12. Remote Method __________. 13. Nous en avons fait un pour dcouvrir RMI. 14. Protge les mthodes des appels non autoriss. 15. Appeler. 17. On en voit un page 469. 18. On y rencontre des foules de proxies.
490
Chapitre 11
le pattern proxy
POINTS DIMPACT
ce Encapsulez lation lhritage. Encapsu n io t la su ap nc Prfrez le phisme es, non des Polymor ac f er t in es d Programmez ions. Hritage implmentat ler vous de coup ui q Efforcezs t je ob s le faiblement nt. interagisse ouvertes doivent tre Les classeson mais fermes la lextensi ion. modificat ctions. Ne tes. des abstra s concr Dpendez pa as cl s des se dpendez . u vos amis Ne parlez q us vous pelez pas, no Ne nous aps. appelleron e seule t avoir quun oi d ne se as Une cl changer. raison de
Un Proxy de protection
contrle laccs aux mthodes dun objet en fonction de lappelant.
ce cipes dans re in r p x u a e v v Pas de nou si vous fermiez le li ? ; s u e o r t chapit leriez-vous de vous rappe
f e dsi de n uc unr pulu farcf c -e re an ee h t ie hu tn ct a a v in tg t lep r ab e e su n ae s g s a Str y c a it t s. O e ace he ir in t cn le ep a r b in d t s,tes u f t a e e n e D je g m e b n u h a a o m je it h r b n it r l n c o n o u in r p io e c e e lg p t e t f r . is u a u a t eF sd D d enD t e sq urec ic m nm dil sanlg r d r ,n e,a n iq l lo t eu n b e d rq m n sse na , in n a it m je io e e e a sna hn b p t o le b it is a y r a a n d s d u r e o e b ux ia o nd p iuix ue je ft uo sla sa io u u b qf ded qt F t jo ere ot t q sit x eic a n u r m u n e c e o r is e t d c h e d li n m c la p s n c a tmme u r t t r io s le ie o a e u n e e g t d o s j d g ie poeinco p e a , b t n r t ix n o c n a n u io la et heuor t cso n tss c la mn Str re t t ut n sso esqu r da e le a ue le un oulu rtso i oe d seca un g s rm so o psa e n if eit f in p ss t e x ul u S ot t p la n ps am n c u d io t t e in t t la se c en n a rface n m e e ic ie a r ie so t c r is m e n b st e in p e x r la a a l vare u u in e d nIl n . a F st n p t iq io it ge . a t nse t r r. Far u t t niv e ra .ef a rin era m ie a m t sa ic c n t r ve e m r nle t is m n . io p u e a b n o s o t la t c c iq u n C st e le ia t e pa c in q a is l e n le ac il a t m a i b ie e o a s d r st c ns n u t lo e n lut e n n u e ai g in io r t a ss et a alt t linst e rm in la classescn t st a p oile ce anl y e cria in t isr u nceo n p r or g eta u s e ys t d l it o d eut au nn d ,t ra A t e ,ff ues cs u r uf o d je es a e a ep u ob t e end dcio ss n un n g qu o c laun l re a e m n d e f e d riv et m n e r t a r e u e d una e t .. d en ss p , et dt e s it in la r diffru ss r es cpa F stne r ut t lala e u eu ea ca li o d n f a e u le n n t s. d u b p e n bejets e a s m ss r o io d se nt t s t n c A e ie e c je e qu cl d t L b l n s it so a re .tulatifs e u des clau se reca s. e nr fodesdso o nt r elle .n e uu d o p un ic li f q m It o s e neif u r c d d n a lo e a et u r a pi , llss q f it een r le s s e ct ns a es sous-t r o io dou n snp po ee at .e et O aboiliP e m r r nit O llL e ser sa om e op u c r ns d eq st e es e er un ra usc l d t d sy oe a v it sc b at n dss da desso t r ecC a r arnt iv / so a f n s ib n s la u la t ie e rs p cu d sa u sa t r if a m ve o u a e t e h p t od r a r t ir c s m m a u o n la g lu r e f c u r p d t g s st le a e t it .. an .. ie s d n r t u je e h e e p p c e d et u ob e is cs y an r c s o t il a t a rm a f ch n a t un r ft p Pr e tat peu i ja dre ner l cge cu n y et s eh ie plus,na in x le ils. sae in rer rm d o c u rt tt ae rt n so e f le e o d n t spe ib n s io lu nc nec e at de t a t p e t so n p s a t e r r t d m ai li u p n o o e ge m an e c p x s qu an in st , u r t t s ch a p sy e e je en c sur em bjet fa co e poebrmaet rt po te ut Il dinso .r s comme si loles objets oa pm sser com pala ouetr se mme faon ons de ceux-ci Tit e d traclasse. t les combinais individuels e
Problme de conception
Il semblerait que la classe ProxyDImage possde deux tats qui sont contrls par des instructions conditionnelles. Connaissez-vous un autre pattern qui pourrait permettre darranger ce code? Comment pourriez-vous revoir la conception de ProxyDImage?
Utiliser le pattern tat, implmenter deux tats, ImageCharge et ImageNonCharge. Puis placer le code des instructions conditionnelles dans leurs tats respectifs. Commencer dans ltat ImageNonCharge puis effecteur la transition vers ImageCharge une fois que lImageIcon a t rcupre. 492
Chapitre 11
le pattern proxy
BeanPersonne getProxyNonProprietaire(BeanPersonne personne) { return (BeanPersonne) Proxy.newProxyInstance( personne.getClass().getClassLoader() , personne.getClass().getInterfaces() , new InvocationHandlerNonProprietaire(personne)) ; }
1 3 6
S U S
4
J
5
D I
A
10
M C
I C
Q U E L E
E S E R
A V A E U
P R
9
D E C
R E
S T R M I B O N
18
O X Y
N D E
14
O
11
12
T T E
Y
13
A T
15
N V O W E B
21
D E E
Z V O U E S
R O T E V I C T I O N
I I
E U R T
16
17
E P E
T O U
N
19
C L A T I O N
M
20
O Q U E
22
R E U
23
493
package headfirst.proxy.virtualproxy; import java.net.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.*; public class TestProxyDImage { ComposantDImage composant; JFrame cadre = new JFrame(Pochettes de CD); JMenuBar barreDeMenus; JMenu menu; Hashtable cd = new Hashtable(); public static void main (String[] args) throws Exception { TestProxyDImage test = new TestProxyDImage(); } public TestProxyDImage() throws Exception{ cd.put(La marche de lempereur,http://images-eu.amazon.com/images/P/B00070FX3M.08. LZZZZZZZ.jpg); cd.put(Ferr: Lespoir,http://images-eu.amazon.com/images/P/B00008ZPD3.08. LZZZZZZZ.jpg ); cd.put(Excalibur,http://images-eu.amazon.com/images/P/B00004VRKV.08.LZZZZZZZ.jpg ); cd.put(Carlos Nunez,http://images-eu.amazon.com/images/P/B000063WSL.08.LZZZZZZZ. jpg); cd.put(Variations Goldberg,http://images-eu.amazon.com/images/P/B000025NYA.08. LZZZZZZZ.jpg); cd.put(Aubry: Signes,http://images-eu.amazon.com/images/P/B0000085FR.08.LZZZZZZZ. jpg ); cd.put(Rokia Traor,http://images-eu.amazon.com/images/P/B0002M5T9I.01.LZZZZZZZ. jpg); URL urlInitiale = new URL((String)cd.get(La marche de lempereur)); barreDeMenus = new JMenuBar(); menu = new JMenu(CD favoris); barreDeMenus.add(menu); cadre.setJMenuBar(barreDeMenus);
494
Chapitre 11
le pattern proxy
for(Enumeration e = cd.keys(); e.hasMoreElements();) { String nom = (String)e.nextElement(); JMenuItem menuItem = new JMenuItem(nom); menu.add(menuItem); menuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { composant.setIcon(new ProxyDImage(getUrlCD(event.getActionCommand()))); cadre.repaint(); } }); } // installer le cadre et les menus Icon icone = new ProxyDImage(urlInitiale); composant = new ComposantDImage(icone); cadre.getContentPane().add(composant); cadre.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); cadre.setSize(800,600); cadre.setVisible(true); } URL getUrlCD(String nom) { try { return new URL((String)cd.get(nom)); } catch (MalformedURLException e) { e.printStackTrace(); return null; } } }
495
package headfirst.proxy.virtualproxy; import java.net.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; class ProxyDImage implements Icon { ImageIcon image; URL urlImage; Thread threadChargement; boolean chargement = false; public ProxyDImage(URL url) { urlImage = url; } public int getIconWidth() { if (image != null) { return image.getIconWidth(); } else { return 800; } } public int getIconHeight() { if (image != null) { return image.getIconHeight(); } else { return 600; } } public void paintIcon(final Component c, Graphics g, int x, int y) { if (image != null) { image.paintIcon(c, g, x, y); } else { g.drawString(Chargement en cours. Patientez..., x+300, y+190); if (!chargement) { chargement = true; threadChargement = new Thread(new Runnable() { public void run() { try { image = new ImageIcon(urlImage, Pochette de CD); c.repaint(); } catch (Exception e) {
496
Chapitre 11
le pattern proxy
package headfirst.proxy.virtualproxy; import java.awt.*; import javax.swing.*; class ComposantDImage extends JComponent { private Icon icone; public ComposantDImage(Icon icone) { this.icone = icone; } public void setIcon(Icon icone) { this.icone = icone; } public void paintComponent(Graphics g) { super.paintComponent(g); int w = icone.getIconWidth(); int h = icone.getIconHeight(); int x = (800 - w)/2; int y = (600 - h)/2; icone.paintIcon(this, g, x, y); } }
497
12 patterns composs
h Patterns
de patterns
Composites
Adaptateurs
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.
499
Collaboration
Lune des meilleures faons dutiliser les patterns est de les sortir de leur cage afin quils puissent interagir avec leurs congnres. Plus vous utiliserez les patterns, plus vous les verrez apparatre ensemble dans vos conceptions. Nous disposons dun nom spcial pour dsigner un ensemble de patterns qui collaborent dans une conception qui peut sappliquer de nombreux problmes: nous disons que ce sont des patterns composs. Cest juste, nous allons maintenant parler de patterns composs de patterns! Vous rencontrerez beaucoup de patterns composs dans le monde rel. Maintenant que vous avez assimil les concepts, vous allez voir quil sagit simplement de patterns qui collaborent, ce qui les rend plus faciles comprendre. Nous allons commencer ce chapitre en retrouvant les sympathiques canards de notre simulateur de canards, SuperCanard. Ce nest que justice quils rapparaissent au moment o nous allons combiner des patterns. Aprs tout, ils nous ont accompagns tout au long de ce livre, et ils se sont montrs bons joueurs en prenant part de nombreuses dmonstrations de patterns. Les canards vont vous aider comprendre comment les patterns peuvent collaborer pour parvenir une solution commune. Mais le seul fait de combiner des patterns ne signifie pas pour autant que notre solution qualifie un pattern compos. Pour cela, cette solution doit tre suffisamment gnraliste pour tre applicable de nombreux problmes. Cest pourquoi nous verrons dans la seconde moiti de ce chapitre un vrai pattern compos: Monsieur Modle-Vue-Contrleur en personne. Si vous nen avez jamais entendu parler, ce sera loccasion de faire connaissance et vous verrez que cest lun des patterns les plus puissants que vous puissiez ajouter votre bote outils de concepteur.
On combine souvent des patterns pour obtenir une solution de conception. Un pattern compos combine deux ou plusieurs patterns pour mettre au point la solution dun problme gnral ou rcurrent.
500
Chapitre 12
patterns composs
Runion de famille
Comme vous venez de lapprendre, nous allons de nouveau travailler avec les canards. Cette fois, ils vont vous montrer comment des patterns peuvent coexister et mme cooprer au sein dune mme solution. Nous allons reconstruire notre simulateur de canards ex nihilo et lui ajouter un certain nombre de fonctionnalits intressantes en utilisant un groupe de patterns. OK, allons-y...
1
Dabord, nous allons crer une interface Cancaneur. Comme nous vous lavons dit, nous partons de zro. Cette fois, les Canards vont implmenter une interface Cancaneur. Ainsi, nous saurons quels sont les objets du simulateurs qui peuvent cancaner(), comme les colverts, les mandarins, les appelants et il se pourrait mme que le canard en plastique viennent pointer son nez.
urs ne font Les Cancane et le font bien: quune chose ! ils cancanent
Maintenant, crons des Canards qui implmentent Cancaneur quoi bon une interface sans classes pour limplmenter? Il est temps de crer quelques canards concrets (mais pas du genre nains de jardin, si vous voyez ce que nous voulons dire).
Il faut bien varier les espces si nous voulons que ce simulateur soit intressant.
501
Ce ne serait pas drle si nous najoutions pas quelques types de Canards. Vous souvenez-vous de la dernire fois? Nous avions des appelants (comme ceux que les chasseurs utilisent et qui, de toute vidence, cancanent) et des canards en plastique.
public class Appelant implements Cancaneur { public void cancaner() { System.out.println(Couincouin); } }
Un Appelant qui cancane mais qui na pas lair tout fait vrai.
public class CanardEnPlastique implements Cancaneur { public void cancaner() { System.out.println(Couic); Un CanardEnPlastique } il cancane. quand }
qui couine
Bien. Nous avons nos canards, il nous faut maintenant le simulateur. Concoctons un simulateur qui cre quelques canards et vrifions quils cancanent... Voici
notre mthode main() n.. qui dclenche lexcutio Nous crons un simulateur et nous appelons sa mthode simuler().
public class SimulateurDeCanards { public static void main(String[] args) { SimulateurDeCanards simulateur = new SimulateurDeCanards(); simulateur.simuler(); } } void simuler() { Cancaneur colvert = new Colvert(); Cancaneur mandarin = new Mandarin(); Cancaneur appelant = new Appelant(); Cancaneur canardEnPlastique = new CanardEnPlastique(); System.out.println(\nSimulateur de canards); simuler(colvert); simuler(mandarin); simuler(appelant); simuler(canardEnPlastique); } void simuler(Cancaneur canard) { canard.cancaner(); }
ues Comme il nous faut quelq canards, nous crons un rte... Cancaneur de chaque so
Et l, nous laissons la magie du polymorphisme oprer: quel que soit le genre de Cancaneur transmis, la mthode simuler() lui demande de cancaner.
502
Chapitre 12
patterns composs
% java SimulateurDeCanards
Simulateur de canards Coincoin Coincoin Couincouin Couic
interface Ils implmentent tous la mme ations leur ent Cancaneur, mais leurs implm guise. permettent de cancaner leur
On dirait que tout fonctionne. Jusque l, cest un sans-faute.
Quand il y a des canards, les oies ne sont pas loin. Un palmipde peut en cacher un autre. Voici une Oie qui tait en train de traner autour du simulateur.
Supposez que nous voulions pouvoir utiliser une Oie partout o nous voulons utiliser un Canard. Aprs tout, les oies mettent des sons, les oies volent, les oies nagent. Pourquoi ny aurait-il pas des oies dans le simulateur? Quel est le pattern qui nous permettrait de mlanger facilement des Oies avec des Canards?
503
un adaptateur doies
Il nous faut un adaptateur doies. Notre simulateur sattend voir des interfaces Cancaneur. Comme les oies ne cancanent pas (elles cacardent), nous pouvons utiliser un adaptateur pour adapter une oie un canard.
public class AdaptateurDOie implements Cancaneur { Oie oie; } public AdaptateurDOie(Oie oie) { this.oie = oie; } public void cancaner() { oie.cacarder();
tateur Souvenez-vous: un Adap cible, qui implmente linterface aneur. est en loccurrence Canc
Quand cancaner() est appele, lappel est dlgu la mthode cacarder() de loie.
Maintenant, les oies devraient pouvoir jouer leur rle dans le simulateur. Il suffit de crer une Oie, de lenvelopper dans un adaptateur qui implmente Cancaneur et normalement le tour est jou.
public } class SimulateurDeCanards { public static void main(String[] args) { SimulateurDeCanards simulateur = new SimulateurDeCanards(); simulateur.simuler(); } i se void simuler() { Nous crons une Oie qu nard Cancaneur colvert = new Colvert(); Ca un e comporte comm Cancaneur mandarin = new Mandarin(); en lenveloppant dans Cancaneur appelant = new Appelant(); rDOie. Cancaneur canardEnPlastique = new CanardEnPlastique(); lAdaptateu Cancaneur canardOie = new AdaptateurDOie(new Oie()); System.out.println(\nSimulateur de canards: avec adaptateur doies); simuler(colvert); simuler(mandarin); simuler(appelant); simuler(canardEnPlastique); simuler(canardOie); } void simuler(Cancaneur canard) { canard.cancaner(); }
Une fois lOie enveloppe, nous pouvons la traiter Comme nimporte quel autre Cancaneur.
504
Chapitre 12
patterns composs
Et maintenant, une petite excution rapide.... Cette fois, quand nous excutons le simulateur, la liste des objets transmis la mthode simuler() comprend une Oie enveloppe dans un adaptateur. Le rsultat? Cela devrait cacarder!
% java SimulateurDeCanards
Simulateur de canards: avec adaptateur doies Coincoin Coincoin Couincouin Couic Ouinc
Voici notre oie! Maintenant, elle peut cancaner avec le reste des canards.
Cancanologie
Les cancanologues sont fascins par tous les aspects du comportement des Cancaneurs. Et il y a une chose quils rvent dtudier depuis toujours: le nombre de couacs que fait une troupe de canards. Comment pouvons-nous ajouter la capacit de compter les couacs sans modifier les classes Canard? Voyez-vous un pattern qui pourrait nous aider?
505
un dcorateur de canards
Nous allons faire le bonheur de ces cancanologues et leur permettre de compter des couacs. Comment? Attribuons aux canards un nouveau comportement (la capacit de compter) en les enveloppant dans un objet dcorateur. Il ny aura absolument rien changer dans le code de Canard.
Comme pour Adaptateur, nous devons implmenter linterface cible. Nous avons une variable dinstance pour le cancaneur que nous dcorons. implements Cancaneur {
public CompteurDeCouacs (Cancaneur canard) { this.canard = canard; } public void cancaner() { canard.cancaner(); nombreDeCouacs++; } public static int getCouacs() { return nombreDeCouacs; } }
Et comme nous comptons TOUS les couacs, nous utilisons une variable statique pour les mmoriser. Nous plaons la rfrence au Cancaneur que nous dcorons dans le constructeur.
Quand cancaner() es lappel au Cancaneu t appele, nous dlguons r que nous dcorons ... puis nous incrmen tons le nombre de couacs.
Nous ajoutons une autre mthode au dcorateur. Cette mthode statique se contente de retourner le nombre de couacs mis par tous les Cancaneurs.
506
Chapitre 12
patterns composs
Nous devons mettre jour le simulateur pour crer des canards dcors. Nous devons maintenant envelopper chaque objet Cancaneur que nous instancions dans un dcorateur CompteurDeCouacs. Sinon, nous ne parviendrons jamais compter tous ces couacs.
public class SimulateurDeCanards { public static void main(String[] args) { SimulateurDeCanards simulateur = new SimulateurDeCanards(); Chaque fois que nous crons un Cancaneur, simulateur.simuler(); nous lenveloppons dans } void simuler() { un nouveau dcorateur. Cancaneur colvert = new CompteurDeCouacs(new Colvert()); Cancaneur mandarin = new CompteurDeCouacs(new Mandarin()); Cancaneur appelant = new CompteurDeCouacs(new Appelant()); Cancaneur canardEnPlastique = new CompteurDeCouacs(new CanardEnPlastique()); Cancaneur canardOie = new AdaptateurDOie(new Oie()); System.out.println(\nSimulateur de canards: avec Dcorateur); simuler(colvert); simuler(mandarin); simuler(appelant); simuler(canardEnPlastique); simuler(canardOie);
Comme le garde nous a dit quil ne voulait pas compter les cris des oies, nous ne les dcorons pas.
Voila o nous System.out.println(Nous avons compt + recueillons les CompteurDeCouacs.getCouacs() + couacs); comportements de } cancanement pour les Cancanologues. void simuler(Cancaneur canard) { canard.cancaner(); jets dcors Ici, rien ne change. Les obeurs. } sont toujours des Cancan
% java SimulateurDeCanards Simulateur de canards: avec Dcorateur Coincoin Coincoin Couincouin Couic Ouinc Nous avons compt 4 couacs %
507
fabrique de canards
Gnial, ce programme de comptage. Tous ces petits cancaneurs nous livrent des secrets insouponns. Mais je trouve quil y a trop de couacs qui ne sont pas compts. Avez-vous une solution?
10
public abstract class FabriqueDeCanardsAbstraite { public public public public } abstract abstract abstract abstract Cancaneur Cancaneur Cancaneur Cancaneur creerColvert(); creerMandarin(); creerAppelant(); creerCanardEnPlastique();
ique abstraite que Nous dfinissons une fabr menter pour crer les sous-classes vont impl diffrentes familles.
508
Chapitre 12
patterns composs
Commenons par une fabrique qui cre des canards sans dcorateurs, pour bien comprendre le principe:
public class FabriqueDeCanards extends FabriqueDeCanardsAbstraite { public Cancaneur creerColvert() { return new Colvert(); } public Cancaneur creerMandarin() { return new Mandarin(); } public Cancaneur creerAppelant() { return new Appelant(); } public Cancaneur creerCanardEnPlastique() { return new CanardEnPlastique(); } }
produit: Chaque mthode cre un ncaneur. un type particulier de Ca pas le Le simulateur ne connat lement produit rel il sait seu quil reoit un Cancaneur.
public class FabriqueDeComptage extends FabriqueDeCanardsAbstraite { public Cancaneur creerColvert() { return new CompteurDeCouacs(new Colvert()); } public Cancaneur creerMandarin() { return new CompteurDeCouacs(new Mandarin()); } public Cancaneur creerAppelant() { return new CompteurDeCouacs(new Appelant()); } public Cancaneur creerCanardEnPlastique() { return new CompteurDeCouacs(new CanardEnPlastique()); } }
Chaque mthode enveloppe le Cancaneur dans le dcorateur qui va permettre de compter les couacs. Le simulateur ne verra jamais la diffrence: il reoit toujours un Cancaneur.Mais maintenant, nos gardes forestiers peuvent tre srs que tous les couacs seront compts.
509
familles de canards
11
Faisons en sorte que le simulateur utilise la fabrique. Vous souvenez-vous du fonctionnement de Fabrique Abstraite? On cre une mthode polymorphe qui accepte une fabrique et lutilise pour crer des objets. En transmettant diffrentes fabriques, on parvient utiliser diffrentes familles de produits dans la mthode. Nous allons modifier la mthode simuler() pour quelle accepte une fabrique et lutilise pour crer des canards.
public class SimulateurDeCanards { public static void main(String[] args) { SimulateurDeCanards simulateur = new SimulateurDeCanards(); FabriqueDeCanardsAbstraite fabriqueDeCanards = new FabriqueDeComptage(); simulateur.simuler(fabriqueDeCanards); }
abord la Nous crons dnous allons fabrique que dans la transmettre er(). mthode simul
void simuler(FabriqueDeCanardsAbstraite fabriqueDeCanards) { Cancaneur colvert = fabriqueDeCanards.creerColvert(); Cancaneur mandarin = fabriqueDeCanards.creerMandarin(); Cancaneur appelant = fabriqueDeCanards.creerAppelant(); Cancaneur canardEnPlastique = fabriqueDeCanards.creerCanardEnPlastique(); Cancaneur canardOie = new AdaptateurDOie(new Oie()); System.out.println(\nSimulateur de canards: avec Fabrique abstraite); simuler(colvert); simuler(mandarin); simuler(appelant); simuler(canardEnPlastique); simuler(canardOie); System.out.println(Nous avons compt + CompteurDeCouacs.getCouacs() + couacs); } void simuler(Cancaneur canard) { canard.cancaner(); } }
La mthode simuler() accepte une FabriqueDeCanardsAbstraite et lutilise pour crer des canards au lieu de les instancier directement.
510
Chapitre 12
patterns composs
lheure, Comme tout ois, nous mais, cette fque les sommes srs tous canards sont e que dcors parc la nous utilisons omptage. FabriqueDeC
% java SimulateurDeCanards Simulateur de canards: avec Fabrique abstraite Coincoin Coincoin Couincouin Couic Ouinc Nous avons compt 4 couacs %
511
Nous commenons avoir du mal grer tous ces types de canards sparment. Auriez-vous un moyen de nous aider les traiter comme un tout, et peut-tre mme de nous permettre de grer quelques familles de canards que nous voulons suivre plus particulirement?
Ce quil nous faut, cest une faon de grer des collections, et mme des sous-collections de canards (pour satisfaire la requte de notre garde forestier). Ce serait galement agrable de pouvoir appliquer des oprations tout lensemble de canards. Quel est le pattern qui peut nous aider?
512
Chapitre 12
patterns composs
12
Crons une troupe de canards (en ralit une troupe de Cancaneurs). Vous souvenez-vous du pattern Composite qui nous permet de traiter une collection dobjets de la mme faon que des objets individuels? Quel mailleur composite y a-t-il quune troupe de Cancaneurs? Voyons un peu comment cela va fonctionner:
doit implmenter Souvenez-vous: le composite ments feuilles. la mme interface que les l Cancaneurs. Nos lments feuilles sont les
public class Troupe implements Cancaneur { ArrayList cancaneurs = new ArrayList(); public void add(Cancaneur cancaneur) { cancaneurs.add(cancaneur); } public void cancaner() { Iterator iterateur = cancaneurs.iterator(); while (iterateur.hasNext()) { Cancaneur cancaneur = (Cancaneur)iterateur.next(); cancaneur.cancaner(); } } }
Dans chaque Troupe, nous utilisons une ArrayList pour contenir les Cancaneurs qui appartiennent la Troupe..
s tout, la Troupe est aussi Maintenant, la mthode cancaner()- apr Troupe doit travailler sur un Cancaneur. La mthode cancaner() de rayList et nous appelons la Troupe entire. Ici, nous parcourons lAr cancaner() sur chaque lment.
Code la loupe
Avez-vous remarqu que nous avons essay dintroduire un design pattern en douce?
public void cancaner() { Iterator iterateur = cancaneurs.iterateur(); while (iterateur.hasNext()) { Cancaneur cancaneur = (Cancaneur)iterateur.next(); cancaneur.cancaner(); } }
513
canard composite
13
Maintenant, nous devons modifier le simulateur. Notre composite est prt; il suffit dun peu de codes pour intgrer les canards la structure composite.
void simuler(FabriqueDeCanardsAbstraite fabriqueDeCanards) { Cancaneur mandarin = fabriqueDeCanards.creerMandarin(); Cancaneur appelant = fabriqueDeCanards.creerAppelant(); Cancaneur canardEnPlastique = fabriqueDeCanards.creerCanardEnPlastique(); Cancaneur canardOie = new AdaptateurDOie(new Oie()); System.out.println(\nSimulateur de canards: avec Composite - Troupes); Troupe troupeDeCanards = new Troupe(); troupeDeCanards.add(mandarin); troupeDeCanards.add(appelant); troupeDeCanards.add(canardEnPlastique); troupeDeCanards.add(canardOie); Troupe troupeDeColverts = new Troupe(); Cancaneur Cancaneur Cancaneur Cancaneur colvertUn = fabriqueDeCanards.creerColvert(); colvertDeux = fabriqueDeCanards.creerColvert(); colvertTrois = fabriqueDeCanards.creerColvert(); colvertQuatre = fabriqueDeCanards.creerColvert();
Nous crons dabord une Troupe, puis nous la remplissons de Cancaneurs. Puis nous crons une nouvelle troupe de colverts. Ici, nous crons une petite famille de colverts... ...et nous lajoutons la troupe de colverts.
Puis nous ajoutons la troupe de colverts la troupe principale. Testons sur toute la Troupe! sur la troupe de colverts.
System.out.println(\nSimulateur de canards: Troupe de colverts); simuler(troupeDeColverts); Puis testons uniquement System.out.println(\nNous avons compt + CompteurDeCouacs.getCouacs() + couacs); } void simuler(Cancaneur canard) { canard.cancaner(); } }
patterns composs
Lanons lexcution
Fichier dition Fentre Aide LaMeilleureFaonDeMarcher
% java SimulateurDeCanards Simulateur de canards: avec Composite - Troupes Simulateur de canards: Toute la troupe Coincoin Couincouin Voici la premire troupe. Couic Ouinc Coincoin Coincoin Coincoin Coincoin Simulateur de canards : Troupe de colverts
Les donnes semblent correctes (noubliez pas que nous ne comptons pas les oies).
Scurit ou transparence?
Vous vous souvenez peut-tre quau chapitre consacr au pattern Composite, les composites (les Menus) et les nuds feuilles (les Plats) avaient exactement le mme ensemble de mthodes, y compris la mthode add(). En consquence, nous pouvions appeler sur les plats des mthodes qui navaient pas vraiment de sens (comme essayer dajouter quelque chose un plat en appelant add()). Cette approche prsentait un avantage: la distinction entre les feuilles et les composites tait transparente. Le client navait pas besoin de savoir sil avait faire une feuille ou un composite et il appelait simplement les mmes mthodes sur les deux. Ici, nous avons dcid de sparer les mthodes de maintenance des enfants du composite des nuds feuilles. Autrement dit, seule les Troupes ont la mthode add(). Nous savons que cela ne veut rien dire dajouter quelque chose un Canard, et, dans cette implmentation, cest impossible. Vous ne pouvez ajouter un lment qu une Troupe. En consquence, cette conception est plus sre vous ne pouvez pas appeler de mthodes qui nont pas de sens sur les composants mais elle est moins transparente. Maintenant, le client doit savoir quun Cancaneur est une Troupe pour pouvoir lui ajouter des cancaneurs. Comme toujours, la conception OO oblige faire des choix, et vous devrez considrer les avantages et les inconvnients quand vous crerez vos propres composites.
515
observateur de canards Le Composite est parfait! Merci! Maintenant, nous avons la demande inverse: nous devons galement tudier les canards individuellement. Pouvez-vous nous fournir un moyen de compter les couacs de chaque canard en temps rel?
14
ace que les CouacObservable est linterf er pour ent lm imp Cancaneurs doivent . ver quon puisse les obser
public interface CouacObservable { public void enregistrerObservateur(Observateur observateur); public void notifierObservateurs(); Elle possde }
Elle dispose galement dune mthode pour tenir les observateurs au courant: notifierObservateurs().
une mthode permettant denregistrer des observateurs. Tout objet implmentant linterface Observateur peut couter les couacs. Nous dfinirons linterface Observateur dans une seconde.
Maintenant, nous devons faire en sorte que tous les Cancaneurs implmentent cette interface...
patterns composs
15
Maintenant, nous devons faire en sorte que toutes les classes concrtes qui implmentent Cancaneur puissent tre un CouacObservable. Pour ce faire, nous pourrions implmenter lenregistrement et la notification dans chaque classe (comme nous lavons fait au chapitre2). Mais nous prfrons cette fois procder un peu diffremment: nous allons encapsuler lenregistrement et la notification dans une autre classe, appelons-la Observable, et la composer avec un CouacObservable. Ainsi, nous ncrirons le code rel quune seule fois et CouacObservable naura besoin que du code ncessaire pour dlguer la classe auxiliaire, Observable. Commenons par la classe auxiliaire Observable
es les Observable implmente toutncaneur a besoin Ca fonctionnalits dont un fit de linsrer pour tre observable. Il suf en sorte que dans une classe et de faire rvable. cette classe dlgue lObse
CouacObservable
Observable doit implmenter CouacObservable parce que ce sont les mmes appels de mthode qui vont lui tre dlgus. transmettons Dans le constructeur, nous e cet ilis ut le CouacObservable qui tement objet pour grer son compor public class Observable implements CouacObservable { ode th m la observable. Regardez ArrayList observateurs = new ArrayList(); us: vous sso de ci() notifierObservateurs CouacObservable canard; cation a lieu, verrez que quand une notifi objet pour public Observable(CouacObservable canard) { lObservable transmet cet l est lobjet que he this.canard = canard; que lobservateur sac } qui cancane.
public void enregistrerObservateur(Observateur observateur) { observateurs.add(observateur); }
public void notifierObservateurs() { Iterator iterateur = observateurs.iterator(); while (iterateur.hasNext()) { Observateur observateur = (Observateur)iterateur.next(); observateur.actualiser(canard); } Et celui } }
517
16
Intgrer lauxiliaire Observable avec les classes Cancaneur. Cela semble prometteur. Il nous suffit de faire en sorte que les classes Cancaneur soient composes avec un Observable et quelles sachent comment lui dlguer. Aprs quoi, elles seront prtes tre des observables. Voici limplmentation de Colvert; celle des autres canards est identique.
public class Colvert implements Cancaneur { Observable observable; public Colvert() { observable = new Observable(this); } public void cancaner() { System.out.println(Coincoin); notiferObservateurs(); }
Dans le constructeur, nous crons un Observable et nous lui transmettons une rfrence lobjet Colvert. Quand nous cancanons, nous devons en informer les observateurs.
Voici nos deux mthodes de CouacObservable. Remarquez que nous dlguons simplement lauxiliaire.
518
Chapitre 12
patterns composs
17
Nous y sommes presque! Il ny a plus qu soccuper du ct Observateur du pattern. Nous avons implment tout ce quil faut pour les observables. Maintenant, il nous faut quelques observateurs. Nous allons commencer par linterface Observer:
Linterface Observateu mthode, actualiser(), r na quune laquelle on transmet le CouacObser vable qui vient de cancaner.
public interface Observateur { public void actualiser(CouacObservable canard); }
Nous devons implmenter linterface Observateur, sinon nous ne pourrons pas nous enregistrer auprs dun CouacObservable.
public class Cancanologue implements Observateur { public void actualiser(CouacObservable canard) { System.out.println(Cancanologue: + canard + vient de cancaner.); } }
Le Cancanologue est sim mthode, actualiser(), ple: il na quune Cancaneur qui vient de qui affiche le cancaner
519
520
Chapitre 12
patterns composs
18
public class SimulateurDeCanards { public static void main(String[] args) { SimulateurDeCanards simulateur = new SimulateurDeCanards(); FabriqueDeCanardsAbstraite fabriqueDeCanards = new FabriqueDeComptage(); simulateur.simuler(fabriqueDeCanards); } void simuler(FabriqueDeCanardsAbstraite fabriqueDeCanards) { // crer les fabriques de canards et les canards // crer les troupes System.out.println(\nSimulateur de canards: avec Observateur); Cancanologue cancanologue = new Cancanologue(); Ici, nous crons simplement troupeDeCanards.enregistrerObservateur(cancanologue); Cancanologue et nous simuler(troupeDeCanards); System.out.println(\nNous avons compt + CompteurDeCouacs.getCouacs() + couacs); } void simuler(Cancaneur canard) { canard.cancaner(); } }
521
lapothose
Et voici lapothose. Cinq, non, six patterns se sont runis pour crer cette prodigieuse simulation. Sans plus de crmonie, nous avons le plaisir de vous prsenter SimulateurDeCanards!
Fichier dition Fentre Aide RienQueDesCanards
% java SimulateurDeCanards Simulateur de canards: avec Observateur Aprs chaque Coincoin couac, quel que Cancanologue : Mandarin vient de cancaner. soit son type, Couincouin lobservateur Cancanologue : Appelant vient de cancaner. reoit une Couic notification. Cancanologue : Canard en plastique vient de cancaner. Ouinc Cancanologue : Oie dguise en Canard vient de cancaner. Coincoin Cancanologue : Colvert vient de cancaner. Coincoin Cancanologue : Colvert vient de cancaner. Coincoin Cancanologue : Colvert vient de cancaner. Coincoin Cancanologue : Colvert vient de cancaner. Et le cancanologue a Nous avons compt 7 couacs. toujours son compte. %
Q: R:
Q: R:
questions stupides
Il suffit parfois de se contenter dappliquer de bons principes de conception OO pour rsoudre un problme de faon satisfaisante. Nous dtaillerons ce point au chapitre suivant, mais sachez quil ne faut employer de patterns que o et quand ils ont un sens. On ne dmarre jamais avec lintention demployer des patterns juste pour le plaisir. Vous pouvez considrer la conception du SimulateurDeCanards comme force et artificielle. Mais bon, ctait amusant et cela nous a donn une bonne ide de la faon dont plusieurs patterns peuvent sinsrer dans une solution.
il ny a pas de
Non, ctait juste un ensemble de patterns qui collaboraient. Un pattern compos est form de quelques patterns que lon combine pour rsoudre un problme rcurrent. Dans quelques instants, nous allons jeter un coup dil un pattern compos, le pattern Modle-Vue-Contrleur. Cest une collection de patterns qui a t utilise maintes et maintes fois dans de nombreuses solutions de conception.
Cest donc cela la vraie beaut des Design Patterns: je peux prendre un problme et commencer lui appliquer des patterns jusqu ce que jaie une solution?
Non. Nous avons mis en scne tous ces canards pour vous montrer comment des patterns peuvent collaborer, mais vous naurez jamais besoin dapprocher une conception de cette faon. En fait, lapplication de certains patterns certains aspects de la solution du problme des canards relve peut-tre de lartillerie lourde.
522
Chapitre 12
patterns composs
Quavons-nous fait?
Nous avons commenc par une poigne de Cancaneurs... Puis une oie est arrive qui voulait se comporter comme un Cancaneur. Nous avons donc utilis le pattern Adaptateur pour adapter loie un Cancaneur. Maintenant, vous pouvez appeler cancaner() sur une oie enveloppe dans un adaptateur et elle cacardera! Ensuite, les Cancanologues ont dcid quils voulaient compter les couacs. Nous avons donc employ le pattern Dcorateur pour ajouter un dcorateur, CompteurDeCouacs, qui mmorise le nombre de fois o cancaner() est appele puis dlgue le couac au Cancaneur quil enveloppe. Mais les Cancanologues avaient peur davoir oubli dajouter le dcorateur CompteurDeCouacs. Nous avons donc appliqu le pattern Fabrique abstraite pour crer des canards. Maintenant, chaque fois quils veulent un canard, ils en demandent un la fabrique et elle leur retourne un canard dcor. (Et noubliez pas quils peuvent aussi faire appel une autre fabrique sils veulent un canard non dcor!) Nous avions des problmes de gestion avec toutes ces oies, tous ces canards et tous ces cancaneurs. Cest pourquoi nous avons employ le pattern Composite pour les grouper en troupes. Ce pattern permet galement au cancanologue de crer des sous-ensembles pour grer des familles de canards. Nous avons galement introduit le pattern Itrateur dans notre implmentation en utilisant lIterator de java.util dans une ArrayList. Les Cancanologues voulaient aussi tre informs quand un cancaneur quelconque cancanait. Nous avons alors utilis le pattern Observateur pour que les Cancanologues puissent senregistrer comme observateurs de cancaneurs. Maintenant, ils reoivent une notification chaque fois quun Cancaneur cancane. Nous avons de nouveau utilis Iterator dans cette implmentation. Les cancanologues peuvent mme utiliser le pattern Observateur avec leurs composites.
Ctait une belle sance dentranement aux design patterns. tudiez le diagramme de classes de la page suivante et faites une petite pause relaxation avant de continuer avec Modle-VueContrleur.
523
vue plongeante
SimulateurDeCanards
ds.
FabriqueDeCanardsAbstraite
creerColvert() creerMandarin() creerAppelant() creerCanardEnPlastique()
FabriqueDeCanards
creerColvert() creerMandarin() creerAppelant() creerCanardEnPlastique()
FabriqueDeComptage
creerColvert() creerMandarin() creerAppelant() creerCanardEnPlastique()
Si une classe implmente Observat elle peut observer deeur, Cancaneurs et elle s sera informe chaq fois quun Cancaneuue r cancanera.
<<interface>> Observateur
Observateuractualiser(CouacObservable)
Voici deux fabriques diffrentes qui crent la mme famille de produits. La FabriqueDeCanards cre des canards et la FabriqueDeComptage cre des Canards envelopps dans des dcorateurs CompteurDeCouacs.
Cancanologue
actualiserCouacObservable
Nous navons im pour les cancan plment quun type dobser va eurs le Canca classe qui implm nologue. Mais tteur en oute t e linterface Obs observer des ca er va t eu na r rd pe ut s. .. Et si on impl observateur Orn mentait un ithologue?
524
Chapitre 12
patterns composs
nous Linterface CouacObservable des que tho m de fournit un ensemble er. tout Observable doit implment
Cancaneur est linter quimplmentent tout face classes dont le comp es les consiste cancaner ortement .
<<interface>> CouacObservable
enregistrerObservateur(Observateur) notifierObservateurs()
Chaque Cancaneur a une instance dObservable qui lui permet de mmoriser ses observateurs et de les informer quand le Cancaneur cancane.
Observable
ArrayList observateurs CouacObservable canard
<<interface>> Cancaneur
cancaner()
enregistrerObservateur(Observateur) notifierObservateurs()
Colvert
cancaner() Mandarin enregistrerObservateur(Obs cancaner() Appelant ervateur)
Oie oie
AdaptateurDOie
Cet Adaptateur
Troupe
ArrayList canards
add(Cancaneur)
Nous avons deux types de Cancaneurs: des canards et ir un dautres objets qui veulent avo comme comportement de Cancaneur, une ppe elo env AdaptateurDOie qui r, Oie et la dguise en Cancaneu r neu nca Ca Troupe, qui est un cs Composite, et CompteurDeCoua aux ent tem qui ajoute un compor Cancaneurs.
.. ce Composite...
CompteurDeCouacs
Cancaneur canard
525
Tu peux modliser tous les trucs que tu veux La faon dont marche un bb ddeux ans Ou bien une bouteille de bon Chardonnay Toutes les stupidits que les gens racontent Ou le bruit que font les ufs en cuisant La drle de faon dont Hexley sdandine Un, deux, trois, quatre Modle Vue Tu peux modliser tous les modles qui posent pour GQ Modle Vue Contrleur La vue a les contrles qui affichent et ditent Cocoa en a tout un tas, bien crits il faut ldire. Prends une NSTextView, passe-lui une vieill chane Unicode Lutilisateur interagit avec, elle gre pratiquement tout Mais la vue ne connat pas le Modle Cette chane peut tre un nombre ou bien les uvres dAristote Garde le couplage lche Et tu pourras rutiliser tout ce que tu voudras Modle Vue, avec un joli rendu en Aqua blue Modle Vue Contrleur Tu te demandes maintenant Tu te demandes comment Les donnes passent entre Modle et Vue Le Contrleur est mdiateur Des tats changeants entre couches
Contrleur
Modle
Modle Vue Il a trois couches comme les Choco BN Modle Vue Moelleux Contrleur Le Modle est la raison dtre de ton application Des objets qui contiennent donnes, logique et cetera Tu cres des classes personnalises dans le domaine de ton problme Et tu peux choisir de permuter toutes les vues Mais les objets du modle restent les mmes.
526
Chapitre 12
patterns composs
Pour synchroniser les donnes des deux Il tire et pousse toutes les valeurs qui changent Modle Vue, grand bravo lquipe Smalltalk! Modle Vue Contrleur Modle Vue, a se prononce Oh Oh pas Ouh Ouh Modle Vue Contrleur Mais cette histoire nest pas finie Il reste de la route faire Personne ne semble tirer de gloire De lcriture du contrleur Car si lmodle est essentiel Et si la vue est magnifique Jsuis pttre feignant, mais parfois cest fou Toutes ces lignes de code qui sont que de la colle Et ce ne serait pas tellement tragique Mais ce code ne fait vraiment rien de magique Il ne fait que transmettre des valeurs Et sans vouloir tre mchant Mais cest vraiment rptitif Tous ces trucs que les contrleurs font Et jaimerais avoir dix centimes Pour les centaines de fois
Que jai envoy textField stringValue. Modle Vue Mais comment se dbarrasser de toute cette colle Modle Vue Contrleur Les Contrleurs connaissent intimement le Modle et la Vue Il y a souvent du code en dur, ce qui menace la rutilisabilit Mais maintenant vous pouvez connecter chaque valeur du modle chaque proprit de la vue Et une fois que vous commencerez lier Vous trouverez je crois moins de code dans votre source Oui, je suis enthousiasm par tout ce quils ont automatis et tout ce que vous pouvez avoir pour rien Et cela vaut la peine dtre rpt tout ce code dont tu nauras pas besoin g quand tu travailleras en IB. avec Swin Modle Vue, il gre mme plusieurs slections Modle Vue Contrleur Modle Vue, jparie que je livre mon appli le premier Modle Vue Contrleur
coutez
Ne vous contenez pas de lire! Aprs tout, nous sommes dans la collection Tte la premire. Attrapez votre iPod, tapez cette URL: http://www.wickedlysmart.com/headfirstdesignpatterns/media.html Faites une pause et coutez la version originale.
527
Jolie chanson, mais est-ce quelle est vraiment cense mexpliquer Modle-Vue-Contrleur? Jai dj essay dapprendre MVC et jai rcolt une de ces migraines!
528
Chapitre 12
patterns composs
Vue
Le modle dit la vue que ltat a chang
Contrleur
Le contrleur demande au modle de commencer jouer le morceau
le mo d v u e d l e i n fo rm u d ta change e la men t t
ts, Le modle contient tous les talica app e iqu log les donnes et la tive ncessaires pour grer et jouer des fichiers mp3.
Modle
529
mvc la loupe
CONTROLEUR Accepte les entres des utilisateurs et dtermine ce quelles signifient pour le modle. VUE Fournit une prsentation du modle. En gnral, la vue reoit directement du modle ltat et les donnes quelle doit afficher.
MODELE Le modle contient les tats, les donnes et la logique applicative. Il na pas conscience de la vue ni du contrleur, mme sil fournit une interface pour accder son tat et le manipuler et sil peut informer les observateurs des changements dtat.
2 1
Lutilisateur a fait quelque chose
Contrleur
3
laffichage Change
Change dtat
4
Jai chang!
Modle Vue
Voil linterface utilisateur
5
Jai besoin dinformations sur ton tat
530
Chapitre 12
patterns composs
Vous tes lutilisateur: vous interagissez avec la vue. Pour vous, la vue est une fentre ouverte sur le modle. Lorsque vous faites quelque chose (par exemple cliquer sur le bouton Play), la vue dit au contrleur ce que vous avez fait. Il appartient au contrleur de le grer. Le contrleur demande au modle de modifier son tat. Le contrleur reoit vos actions et les interprte. Si vous cliquez sur un bouton, cest au contrleur de dterminer ce que cela signifie et comment manipuler le modle en fonction de cette action. Le contrleur peut galement demander la vue de changer. Quand le contrleur reoit une action de la vue, il peut avoir besoin de dire la vue de changer en consquence. Par exemple, le contrleur peut activer ou dsactiver certains boutons ou certains lments de menu dans linterface. Le modle informe la vue quand son tat a chang. Lorsque quelque chose change dans le modle, que ce soit une rponse lune de vos actions (comme cliquer sur un bouton) ou une autre modification interne (par exemple le morceau suivant dans la liste a commenc), le modle notifie la vue ce changement dtat. La vue demande ltat au modle. La vue obtient ltat quelle affiche directement du modle. Par exemple, lorsque le modle informe la vue quun autre morceau a commenc, celle-ci demande le nom du morceau au modle et laffiche. Elle peut galement demander un tat au modle si le contrleur a requis un changement dans la vue.
Q: R:
Bien sr. Dans certaines conceptions, le contrleur senregistre auprs du modle et reoit des notifications de changement. Ce peut tre le cas quand un lment du modle affecte directement les commandes de linterface utilisateur. Par exemple, certains tats du modle peuvent dicter lactivation ou la dsactivation de certains lments de linterface. ce moment-l, cest rellement au contrleur quil appartient de demander la vue de mettre jour laffichage en consquence.
Si je ne me trompe, le contrleur se borne accepter les entres de lutilisateur et les transmettre au modle. Alors pourquoi sen encombrer sil ne fait rien dautre? Pourquoi ne pas placer le code dans la vue elle-mme? Dans la plupart des cas, le contrleur appelle juste une mthode sur le modle, non?
Q:
questions stupides
Cest possible, mais ce nest pas souhaitable pour deux raisons. Premirement, cela compliquerait le code de la vue parce quelle aurait alors deux responsabilits: grer linterface utilisateur et soccuper de la logique de contrle du modle. Deuximement, la vue et le modle seraient fortement coupls. Si vous voulez rutiliser la vue avec un autre modle, cest rp. Le contrleur spare la logique de contrle de la vue et dcouple la vue du modle. En maintenant un couplage faible entre la vue et le contrleur, vous construisez une conception plus souple et plus extensible, qui sera plus facile modifier en aval.
Il ny a pas de
Le contrleur ne se contente pas de transmettre les actions: il est charg de les interprter et de manipuler le modle en consquence. Mais votre vraie question est sans doute: pourquoi ne puis-je pas le faire dans le code de la vue?
R:
531
Stratgie
La vue et le contrleur mettent en uvre le pattern Stratgie classique: la vue est un objet qui est configur avec une stratgie. Le contrleur fournit la stratgie. La vue nest concerne que par les aspects visuels de lapplication et dlgue au contrleur toutes les dcisions sur le comportement de linterface. Lemploi du pattern Stratgie permet galement de dcoupler la vue du modle parce que cest le contrleur qui a la responsabilit dinteragir avec le modle pour excuter les requtes des utilisateurs. La vue ne sait absolument pas comment il procde.
Contrleur
laffichage Modifie
Change dtat
C om
e posi t
Ob s e r v a
class Lecteur { jouer(){} riper(){} graver(){} }
te u r
Jai chang!
Modle Vue
Laffichage consiste en un ensemble imbriqu de fentres, de panneaux, de boutons, de champs de texte, etc. Chaque composant de laffichage est un composite (comme une fentre) ou une feuille (comme un bouton). Quand le contrleur dit la vue de sactualiser, il lui suffit de sadresser au composant principal et le Composite prend soin du reste.
Jai besoin dinformations sur ton tat
Le modle implmente le pattern Observateur, afin que les objets intresss soient mis jour quand un changement dtat se produit. Lemploi du pattern Observateur garantit que le modle demeure compltement indpendant des vues et des contrleurs. Il permet galement dutiliser des vues diffrentes avec le mme modle, voire den utiliser plusieurs en mme temps.
532
Chapitre 12
patterns composs
Observateur
Observable
class Ceci { void cela() { faireCela(); } }
Observateurs
Mon tat a chang !
Tous ces observateurs seront informs chaque fois quil y aura un changement dtat dans le modle.
Vue
Modle
Contrleur
Vue
Vue
Tout objet intress par les changements dtat senregistre comme observateur auprs du modle.
Stratgie
La vue dlgue au contrleur la gestion des actions de lutilisateur.
Contrleur
t la Le contrleur es vue: cest stratgie pour la mment lobjet qui sait codes grer les actions utilisateurs.
Nous pouvons substitu un autre comporteme er dans la vue en change nt ant le contrleur.
Vue
Contrleur traduire les de charge se contrleur Le . prsentation la de que soucie se La vue ne modle. au transmettre les de et actions en lutilisateur entres de
Composite
paint()
Vue
La vue est un compositeHM constitu dlments dI ps de (panneaux, boutons, cham ant de texte, etc.). Le compos utres da t ien haut niveau cont nent euxcomposants qui contien ants, et os mp mmes dautres co quon ainsi de suite jusqu ce les. uil fe s atteigne les nud
vous tes ici
533
mvc et la vue dj
Une barre pulsante affiche les battements en temps rel. Cette zone affiche les BPM courants. Elle est mise jour automatiquement chaque fois la valeur des BPM change.
de La vue est constitu i qu lle ce : deux parties tat permet de visualiser l sert i qu lle ce du modle et s. su es oc pr le contrler
Vous pouvez entrer un BPM spcifique et cliquer sur le bouton Dfinir ou bien utiliser les boutons pour augmenter ou diminuer la valeur.
Diminue les BPM dun battement par minute.
534
Chapitre 12
patterns composs
Voici dautres faons de contrler la Vue DJ... son en Vous gnrez le rer dans choisissant Dmardes DJ. le menu Comman
Remarquez que Arrter est indisponible tant que vous navez pas dmarr..
Notez que Dmarrer est indisponible quand le son a commenc. Toutes les actions de lutilisateur sont transmises au contrleur.
Le contrleur accepte les entres de lutilisateur et dtermine comment les traduire en requtes au modle.
Contrleur
Noublions pas le modle sous-jacent...
Vous ne pouvez pas voir le modle, mais vous pouvez lentendre. Le modle sous-tend tout le reste: il gre le tempo et pilote les enceintes avec du code MIDI.
deleTem Mo p
Le ModeleTempo est le cur de lapplication. Il implmente la logique pour lancer et arrter le son, fixer le nombre de battements par minute (BPM) et gnrer le son.
marche()
setBPM()
ge
arret() ) ( tBPM
Le modle nous permet galement de connatre son tat courant via la mthode getBPM().
vous tes ici
535
Vue
Contrleur
Vue
Comme les BPM sont rgls 120 vue reoit une notification toutes , la les demi secondes.
arrt()
() etBPM
La vue est informe que les BPM ont chang. Elle appelle e. getBPM() sur ltat du modl
536
Chapitre 12
patterns composs
Voici les mthodes que le contrleur utilisera pour manipuler le modle en fonction des interactions des utilisateurs.
s appele apr anci. st e e d o h t Cette m eleTempo a t inst que le Mod Celles-ci dmarrent et arrtent le gnrateur. Et celle-l fixe le nombre de BPM. Aprs quelle a t appele, la frquence des battements change immdiatement.
La mthode getBPM() retourne le nombre de BPM courant ou 0 si le gnrateur est arrt.
Ces mthodes permettent la vue et au contrleur daccder ltat et de devenir des observateurs.
void enregistrerObservateur(ObservateurBattements o); void supprimerObservateur(ObservateurBattements o); void enregistrerObservateur(ObservateurBPM o); void supprimerObservateur(ObservateurBPM o);
Ce code devrait vous sembler familier. Ces mthodes permettent aux objets de senregistrer comme observateurs pour connatre les changements dtat.
Nous avons distingu deux types dobservateurs: ceux qui veulent tre au courant de chaque battement et ceux qui veulent simplement tre informs quand le nombre de BPM change.
537
le modle
public class ModeleTempo implements InterfaceModeleTempo, MetaEventListener { Sequencer sequenceur; Le squenceur est lobjet qui sait comment gnrer ArrayList observateursBattements = new ArrayList(); de vrais sons (que vous pouvez entendre!). ArrayList observateursBPM = new ArrayList(); int bpm = 90; Ces ArrayLists contiennent les deux types // autres variables dinstance public void initialiser() { setUpMidi(); construirePisteEtDemarrer(); } public void marche() { sequenceur.start(); setBPM(90); } public void arret() { setBPM(0); sequenceur.stop(); } public void setBPM(int bpm) { this.bpm = bpm; sequenceur.setTempoInBPM(getBPM()); notifierObservateursBPM(); } public int getBPM() { return bpm; } void evenementBattement() { notifierObservateursBattements(); }
dobservateur (ceux qui observent les battements et ceux qui observent les BMP). La variable dinstance bpm contient la frquence des battements par dfaut, 90 BPM.
Et arret() larrte en fixant les BPM 0 et en stoppant le squenceur.. La mthode setBPM() est le moyen qui permet au contrleur de manipuler le tempo. Elle fait trois choses: (1) Initialiser la variable dinstance bpm
// Code denregistrement et de notification des observateurs // Beaucoup de code MIDI pour grer le son }
(2) Demander au squenceur de changer la frquence des BPM. (3) Informer tous les ObservateurBPM que le nombre de BPM a chang. contenu de la variable La mthode getBPM() se contente de retourner le par minute actuel. ts dinstance bpm, qui indique le nombre de battemen La mthode evenementBattement(), qui nest pas dans I chaque fois InterfaceModeleTempo, est appele par le code MIDinforme tous les quun nouveau battement commence. Cette mthode ement. ObservateurBattements quil y a eu un nouveau batt
538
Chapitre 12
patterns composs
La Vue
Cest l que cela commence devenir amusant: nous allons installer une vue et visualiser le ModeleTempo! Remarquez dabord que nous avons implment la vue de manire lafficher dans deux fentres spares. Lune des fentres contient la frquence en BPM courante et la barre pulsante, tandis que lautre contient les commandes de linterface. Pourquoi? Parce que nous voulions mettre laccent sur la diffrence entre la partie de linterface qui contient la vue du modle et le reste de linterface qui contient lensemble des commandes utilisateur. Regardons de plus prs les deux parties de la vue:
Nous avons spar la vue sur le modle de la vue qui contient les commandes.
La vue affiche deux aspects du ModeleTempo... ...le nombre de battements par minute actuel, partir des notifications dObservateurBPM.... ...et une barre pulsante, synchronise avec le tempo, et pilote par les notifications dObservateurBattements.
Cette fentr vous utilisez p e est la partie de la vue que our modifier le tempo. Elle transmet contrleur. toutes vos actions au
Notre ModeleTempo ne sait rien de la vue. Le modle tant mis en uvre selon le pattern Observateur, il se contente dinformer chaque vue enregistre en tant quobservateur quand son tat change. La vue utilise lAPI du modle pour accder ltat. Nous navons implment quun type de vue. Voyez-vous dautres vues qui pourraient utiliser les notifications et les tats de ModeleTempo?
Un lightshow bas sur le tempo en temps rel. Une vue textuelle qui affiche le style de musique en fonction de la frquence en BPM (ambient, downbeat, techno, etc.).
539
la vue
Implmenter la Vue
Les deux parties de la vue la vue du modle et la vue qui contient les commandes utilisateur sont affiches dans deux fentres, mais elles rsident toutes les deux dans la mme classe Java. Nous allons commencer par le code qui cre la vue du modle, celui qui affiche le nombre de BPM courant et la barre pulsante. la page suivante, nous verrons le code qui cre les commandes de ModeleTempo, qui affiche les boutons et le champ de texte qui permet de dfinir le nombre de BPM.
pages Le code de ces deux ! te let ue sq un Regardez ! nest qu classe en DEUX, Nous avons scind UNE issant sur cette un partie de la vue appara vante. En sui e pag page et lautre la seule classe: ne du que git sa ralit, il ne le ez listing VueDJ.java. Vous trouver e. pitr complet la fin du cha
ements de BPM.
public class VueDJ implements ActionListener, ObservateurBattements, ObservateurBPM { InterfaceModeleTempo modele; La vue contient une rfrence au modle ET au contrleur. InterfaceControleur controleur; Le contrleur nest utilis que par linterface de contrle. JFrame cadreVue; JPanel panneauVue; Nous allons le voir dans une seconde... Ici, nous crons quelques BarrePulsante barre; composants destins laff JLabel affichageBPM; ichag
public VueDJ(InterfaceControleur controleur, InterfaceModeleTempo modele) { this.controleur = controleur; Le constructeur reoit les rfrences this.modele = modele; et nous modele.enregistrerObservateur((ObservateurBattements)this); qu contrleur et au modle, les dans ences rfr ces ons stock modele.enregistrerObservateur((ObservateurBPM)this); ance. dinst les } variab public void creerVue() { // Cration de tous les composants Swing }
public void majBPM() { int bpm = modele.getBPM(); if (bpm == 0) { affichageBPM.setText(hors ligne); } else { affichageBPM.setText(Nombre de BPM: + modele.getBPM()); } } public void majTempo() { barre.setValue(100); } }
Nous enregistrons galement un ObservateurBattements et un ObservateurBPM. La mthode majBPM() est appele quand un changement dtat survient dans le modle. ce moment-l, nous mettons jour laffichage avec la valeur courante de BPM. Nous obtenons cette valeur en la demandant directement au modle.
De mme, la mthode majTempo() est appele quand le modle commence un nouveau battement. ce moment-l, notre barre pulsante doit pulser. Pour ce faire, nous lui affectons sa valeur maximale (100) et nous la laissons grer lanimation.
540
Chapitre 12
patterns composs
Cette mthode cre toutes les comman dans linterface. Elle se charge galemedes et les place nt du menu. Quand lutilisateur slectionne Dmarrer ou Arr correspondante est appele sur le contr ter, la mthode leur. Toutes ces mthodes permettent dactive et de dsactiver Dmarrer et Arrter r dans le menu. Nous verrons que le contr les utilise pour modifier linterface. leur
Cette mthode est appele lorsquon clique sur un bouton. Si lon clique sur le bouton Dfinir, il est transmis au contrleur avec la nouvelle valeur de bpm. De mme, si les boutons pour augmenter ou diminuer sont cliqus, cette information est transmise au contrleur.
541
le contrleur
Et maintenant, le Contrleur
Il est temps dcrire le chanon manquant: le contrleur. Souvenez-vous: le contrleur est la stratgie que nous insrons dans la vue pour lui donner des comportements. Comme nous implmentons le pattern Stratgie, nous devons commencer par une interface qui puisse servir toute Stratgie qui pourrait tre insre dans la VueDJ. Nous allons lappeler InterfaceControleur.
public interface InterfaceControleur { void start(); void stop(); void augmenterBPM(); void diminuerBPM(); void setBPM(int bpm); }
Voici toutes les mthodes que la vue peut appeler sur le contrleur.
Ces mthodes devraient vous sembler familires aprs avoir vu linterface du modle. Vous pouvez lancer et arrter la gnration des sons et modifier le nombre de BPM. Cette interface est plus riche que linterfaceModeleTempo parce que vous pouvez ajuster les BPM avec les boutons augmenter et diminuer.
Problme de conception
Vous venez de voir que la vue et le contrleur emploient tous deux le pattern Stratgie. Pouvez-vous tracer un diagramme de classes qu reprsente ce pattern?
542
Chapitre 12
patterns composs
public ControleurTempo(InterfaceModeleTempo modele) { this.modele = modele; vue = new VueDJ(this, modele); vue.creerVue(); Le contrleur reoit le modle vue.creerCommandes(); dans le constructeur et cre vue.desactiverChoixStop(); ensuite la vue. vue.activerChoixStart(); modele.initialiser(); } public void start() { modele.marche(); vue.desactiverChoixStart(); vue.activerChoixStop(); } public void stop() { modele.arret(); vue.desactiverChoixStop(); vue.activerChoixStart(); } public void augmenterBPM() { int bpm = modele.getBPM(); modele.setBPM(bpm + 1); } public void diminuerBPM() { int bpm = modele.getBPM(); modele.setBPM(bpm - 1); } public void setBPM(int bpm) { modele.setBPM(bpm); } }
contrleur est le fourrage au milieu du Choco BN. Cest donc lobjet qui est li la vue et au modle et qui cimente le tout.
s le Quand vous choisissez Dmarrer dan active leur tr con menu de ModeleTempo, le tur lisa uti ace erf le modle puis modifie lint le nib ispo ind soit afin que le choix Dmarrer le. onib disp soit et que le choix Arrter
De mme, quand vous choisissez Arrter dans le menu, le contrleur dsactive le modle, puis modifie linterface utilisateur afin que le choix Arrter soit indisponible et le choix Dmarrer disponible.
Si on clique sur le bouton augmenter, le contrleur obtient le nombre de BPM actuel du modle, ajoute1 et fixe la nouvelle valeur de BPM. Pareil ici, sauf que nous soustrayons1.
Enfin, si lon utilise linterface utilisateur pour fixer un nombre arbitraire de BPM, le contrleur donne linstruction ncessaire au modle.
NOTE: cest le contrleur qui prend les dcisions intelligentes pour la vue. La vue sait uniquement activer et dsactiver les lments de menu. Elle ne connat pas les situations dans lesquelles elle doit les dsactiver
543
synthse
Synthse...
Nous avons tout ce quil nous faut: un modle, une vue et un contrleur. Il est temps de tout assembler dans un MVC! Nous allons voir et entendre le superbe rsultat de cette collaboration. Tout ce qui manque, cest un petit peu de code pour dmarrer. Il nen faut pas beaucoup:
public class TestDJ { public static void main (String[] args) { InterfaceModeleTempo modele = new ModeleTempo(); InterfaceControleur controleur = new ControleurTempo(modele); } }
Dabord, crer un mo
dle...
...puis crer un contrleur et lui transmettre le modle. Souvenez-vous: comme le contrleur cre la vue, nous navons pas besoin de le faire.
Et maintenant, testons...
Fichier dition Fentre Aide YaDLaJoie
% java TestDJ %
Mode demploi
1
Lancez la gnration du son en slectionnant Dmarrer dans le menu. Remarquez que le contrleur dsactive Dmarrer ensuite. Utilisez le champ de texte et les deux boutons pour modifier le nombre de BPM. Remarquez que laffichage reflte les modifications en dpit du fait quelle na pas de lien logique avec les commandes. Remarquez que la barre pulsante est toujours synchronise avec le tempo, puisquelle est un observateur du modle. Mettez votre chanson favorite et voyez si vous pouvez vous synchroniser son rythme avec les boutons pour augmenter et diminuer. Arrtez le gnrateur. Remarquez que le contrleur dsactive Arrter et active Dmarrer dans le menu.
544
Chapitre 12
patterns composs
ModeleCoeur
getRythmeCardiaque() enregistrerObservateur(ObservateurBattements) enregistrerObservateur(ObservateurBPM) // autre mthodes
e qui lit le Nous avons une mthod el. rythme cardiaque actu
Ce serait gnial de pouvoir rutiliser notre vue actuelle avec ModeleCoeur, mais il nous faut un contrleur qui fonctionne avec ce modle. De plus, linterface de ModeleCoeur ne correspond pas ce que la vue attend parce quelle a une mthode getRythmeCardiaque() au lieu dune mthode getBPM(). Comment concevriez-vous un ensemble de classes pour permettre la vue de rutiliser le nouveau modle?
545
mvc et adaptateur
Adapter le Modle
Pour commencer, nous allons devoir adapter le ModeleCoeur un ModeleTempo, faute de quoi la vue ne pourra pas fonctionner avec le modle parce quelle a une mthode getBPM() et que son quivalent dans ModeleCoeur est getRythmeCardiaque(). Comment allons-nous procder? Nous allons utiliser le pattern Adaptateur, bien sr! Il savre que cest une technique courante lorsquon travaille avec MVC: employer un adaptateur pour adapter un modle de sorte quil fonctionne avec des vues et des contrleurs existants. Voici le code qui permet dadapter un ModeleCoeur un ModeleTempo:
public class AdaptateurCoeur implements InterfaceModeleTempo { InterfaceModeleCoeur coeur; public AdaptateurCoeur(InterfaceModeleCoeur coeur) { this.coeur = coeur; } public void initialiser() {} public void marche() {} public void arret() {} public int getBPM() { return coeur.getRythmeCardiaque(); } public void setBPM(int bpm) {} public void enregistrerObservateur(ObservateurBattements o) { coeur.enregistrerObservateur(o); } public void supprimerObservateur(ObservateurBattements o) { coeur.supprimerObservateur(o); } public void enregistrerObservateur(ObservateurBPM o) { coeur.enregistrerObservateur(o); } public void supprimerObservateur(ObservateurBPM o) { coeur.supprimerObservateur(o); } }
Nous ne savons pas exactement ce que ces mthodes feraient un cur, mais nous en frissonnons davance. Mieux vaut en faire des oprations nulles. Lors de lappel de getBPM(), nous traduisons simplement ce dernier en appel de getRythmeCardiaque() sur ModeleCoeur. Ce ne sont pas des choses faire un cur! Encore une fois, mieux vaut une opration nulle. Voici nos mthodes lies aux observateurs. Nous les dlguons simplement au ModeleCoeur envelopp.
546
Chapitre 12
patterns composs
Comme auparavant, le public ControleurCoeur(InterfaceModeleCoeur modele) { contrleur cre la vue et this.modele = modele; cimente le tout. vue = new VueDJ(this, new AdaptateurCoeur(modele)); vue.creerVue(); vue.creerCommandes(); vue.desactiverChoixStop(); Il ny a quune modification: nous transmettons vue.desactiverChoixStart(); un ModeleCoeur, pas un ModeleTempo... }
public void start() {} public void stop() {} public void augmenterBPM() {} public void diminuerBPM() {} public void setBPM(int bpm) {} }
...et nous devons envelopper ce modle dans un adaptateur avant de le transmettre la vue. Enfin, le ControleurCoeur dsactive les lments de menu quand ils ne sont pas ncessaires.
Pas grand chose faire ici: aprs tout, nous ne pouvons pas contrler des curs comme on contrle des tempos.
547
tester le modle
Excutons le test...
% java TestCoeur %
Excutez ceci...
Mode demploi
1
Remarquez que laffichage fonctionne parfaitement avec un cur! On dirait vraiment des pulsations. Puisque ModeleCoeur prend galement en charge des ObservateurBattements et des ObservateurBPN, nous avons des mises jour en direct comme dans lapplication DJ. Comme le rythme cardiaque varie naturellement, remarquez que laffichage est actualis avec le nouveau nombre de BPM. Chaque fois que les BPM sont actualiss, ladaptateur remplit son rle: il traduit les appels de getBPM() en appels de getRythmeCardiaque(). Les choix Dmarrer et Arrter ne sont pas disponibles dans le menu parce que le contrleur les a dsactivs. Les autres boutons fonctionnent toujours, mais ils nont aucun effet parce que le contrleur implmente des oprations nulles. On pourrait modifier la vue pour les rendre indisponibles.
548
Chapitre 12
patterns composs
MVC et le Web
Le Web tait peine dploy que les dveloppeurs commenaient adapter MVC au modle navigateur/serveur. Ladaptation dominante est simplement connue sous le nom de Model2 et met en uvre une combinaison de servlets et de JSP pour obtenir la mme sparation du modle, de la vue et du contrleur que celle que nous voyons dans les IHM conventionnelles. Voici comment fonctionne Model2:
te H equ
TTP
in st
servlet/contrleur
3
<html> <body> Refactorisation <%= new CeciCela() %> <% // plus %> plus </body> </html>
an
ci e
DB
Navigateur web
4 4
rponse H
5
TTP
Client
jsp/vue
bean
Vous mettez une requte HTTP, qui est reue par une servlet. Vous effectuez une requte HTTP via votre navigateur web. En gnral, elle consiste envoyer les donnes dun formulaire, par exemple votre nom dutilisateur et votre mot de passe. Une servlet reoit ces donnes et les analyse. La servlet joue le rle de contrleur. La servlet joue le rle du contrleur et traite votre requte, trs probablement en interrogeant le modle (habituellement, une base de donnes). Le rsultat du traitement de la requte est gnralement empaquet sous la forme dun JavaBean. Le contrleur transfre le contrle la vue. La Vue est reprsente par une JSP. La seule tche de celle-ci consiste gnrer la page reprsentant la vue du modle ( 4 quelle obtient par lintermdiaire du JavaBean) ainsi que les contrles ncessaires pour les futures actions. La vue retourne une page au navigateur via HTTP. Une page est retourne au navigateur dans lequel elle est affiche sous forme de vue. Lutilisateur soumet les requtes suivantes qui sont traites de la mme faon.
549
model2
Comment tait la vie avant lentre en scne de Model2? Je ne vous raconte pas. Ctait pouvantable.
550
Chapitre 12
patterns composs
Le plan
1
Adapter le modle Eh bien, en fait, nous navons pas adapter le modle: il est trs bien comme il est! Crer une servlet contrleur Il nous faut une servlet simple qui puisse recevoir nos requtes HTTP et excuter quelques oprations sur le modle. Il suffit quelle sache dmarrer, arrter et modifier le nombre de battements par minute. Crer une vue HTML. Nous allons crer une vue simple avec une JSP. Celle-ci va recevoir du contrleur un ModeleTempo qui lui indiquera tout ce quelle a besoin dafficher. Ensuite, elle gnrera une interface HTML.
Trucs de geeks
Vous montrer comment un installer un environnement de servlets est un peu hors sujet pour un ouvrage sur les Design Patterns, moins que vous ne vouliez que ce livre pse trois tonnes! Ouvrez votre navigateur web et allez directement ladresse http://jakarta.apache.org/tomcat/ o vous pourrez consulter la page principale sur Tomcat, le conteneur de servlets du projet Apache Jakarta. Vous y trouverez tout ce quil vous faut pour tre oprationnel. Vous pouvez galement lire Head First Servlets & JSP de Bryan Basham, Kathy Sierra et Bert Bates.
551
tape1: le modle
Souvenez-vous: dans MVC, le modle ne sait rien des vues ni des contrleurs. Autrement dit, il est totalement dcoupl. Tout ce quil sait, cest quil peut y avoir de nombreux observateurs auxquels il doit envoyer des notifications. Cest toute la beaut du pattern Observateur. Il fournit galement une interface que les vues et les contrleurs peuvent utiliser pour accder son tat et le modifier. Il suffit donc dadapter le modle pour quil fonctionne dans un environnement web. Mais comme il ne dpend daucune classe extrieure, il ny a strictement rien faire. Nous pouvons utiliser notre ModeleTempo directement, sans aucune modification. Soyons donc productifs et passons ltape2!
public void initialiser() throws ServletException { ModeleTempo modeleTempo = new ModeleTempo(); modeleTempo.initialiser(); getServletContext().setAttribute(modeleTempo, modeleTempo); } // ici, la mthode doPost public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // ici limplmentation } }
Nous tendons la classe HttpServlet pour pouvoir faire ce que font les servlets, par exemple recevoir des requtes HTTP. Voici la mthode init(). Elle est appele lors de la cration de la servlet. Nous crons dabord un objet ModeleTempo...
...et nous plaons une rfrence cet objet dans le contexte de la servlet pour pouvoir y accder facilement.
Voici la mthode doGet(). Cest elle qui fait le vrai travail. Nous verrons son implmentation page suivante.
552
Chapitre 12
patterns composs
Premirement, nous prenons le modle dans le contexte de la servlet. En labsence de rfrence, nous ne pourrions pas le manipuler.
ModeleTempo modeleTempo = (ModeleTempo)getServletContext().getAttribute(modeleTempo); String bpm = request.getParameter(bpm); if (bpm == null) { bpm = modeleTempo.getBPM() + ; } String definir = request.getParameter(definir); if (definir != null) { int bpmNumber = 90; bpmNumber = Integer.parseInt(bpm); modeleTempo.setBPM(bpmNumber); } String diminuer = request.getParameter(diminuer); if (diminuer != null) { modeleTempo.setBPM(modeleTempo.getBPM() - 1); } String augmenter = request.getParameter(augmenter); if (augmenter != null) { modeleTempo.setBPM(modeleTempo.getBPM() + 1); } String on = request.getParameter(on); if (on != null) { modeleTempo.start(); } String off = request.getParameter(off); if (off != null) { modeleTempo.stop(); } request.setAttribute(modeleTempo, modeleTempo); RequestDispatcher dispatcher = request.getRequestDispatcher(/jsp/VueDJ.jsp); dispatcher.forward(request, response); }
Si nous avons une commande dfinir, nous prenons sa valeur et nous lindiquons au modle.
diminuer, nous Pour augmenter ou BPM actuel lisons le nombre de s ou le et nous lincrmenton dcrmentons de1.
Si nous avons une commande on ou off, nous disons au modle de commencer ou darrter. Enfin, notre tche de contrleur est termine. Il ne reste plus qu demander la vue de prendre le relais et de crer une vue HTML. Selon la dfinition de Model2, nous transmettons la JSP un bean qui contient ltat du modle. En loccurrence, nous lui transmettons le modle lui-mme, puisquil se trouve que cest un bean.
vous tes ici
553
jsp:useBean id=modeleTempo scope=request class=tetepremiere.mix.vuedj.ModeleTempo /> <html> <head> <title>Vue DJ</title> </head> <body>
<h1>Vue DJ</h1> Battements par minute = <jsp:getProperty name=modeleTempo property=BPM /> <br /> <hr> <br /> <form method=post action=/djview/servlet/VueDJ> BPM: <input type=text name=bpm value=<jsp:getProperty name=modeleTempo property=BPM />> <input type=submit <input type=submit <input type=submit <input type=submit <input type=submit </form> </body> </html> name=definir value=definir><br /> name=diminuer value=<<> name=augmenter value=>>><br /> name=on value=on> name=off value=off><br />
Puis nous gnrons la vue qui affiche le nombre actuel de battements par minute.
Et voici la partie commandes de la vue. Nous avons un champ de texte pour les BPM et des boutons pour dmarrer, arrter, augmenter et diminuer.
patterns composs
Tester Model2...
Il est temps de lancer votre navigateur web, daccder la servlet VueDJ et de tester...
u Voici la vue d modle. Et voil le jeu. de commandess Quand vous le utilisez, elles es sont transmis via HTTP la leur servlet contr qui les traite
(5) Lutilisateur entre un nouveau BPM dans le champ de texte. (6) Lutilisateur clique sur le bouton dfinir.
555
(9) La vue retourne une page HTML refltant ltat actuel du modle
Mode demploi
1
Premirement, atteindre la page web. Vous verrez que les BPM sont 0. Continuez et cliquez sur le bouton on. Vous devez maintenant voir la valeur par dfaut des BPM: 90. Vous devez galement entendre un rythme sur la machine sur laquelle le serveur sexcute. Entrez un tempo spcifique, disons120, et cliquez sur le bouton Dfinir. Lcran doit se rafrachir et afficher la nouvelle valeur (et vous devez entendre les battements sacclrer). Jouez maintenant avec les deux boutons pour acclrer et ralentir le tempo. Rflchissez la faon dont chaque partie du systme fonctionne. Linterface HTML envoie une requte la servlet (le contrleur). La servlet lanalyse, envoie une requte au modle puis transfre le contrle la JSP (la vue). Cette dernire cre la vue HTML qui est retourne et affiche.
2 3
4 5
556
Chapitre 12
patterns composs
Mme si Model2 ne ressemble pas vraiment au MVC des manuels, tous ses constituants sont toujours l: ils ont simplement t adapts pour reflter les idiosyncrasies du modle dun navigateur web. Regardons de nouveau... .
Observateur
La vue nest plus un observateur du modle au sens classique du terme; autrement dit, elle ne senregistre pas auprs du modle pour tre informe de ses changements dtat. En revanche, la vue reoit bien lquivalent de notifications, mais elle les reoit indirectement, car cest le contrleur qui linforme que le modle a chang. Le contrleur lui transmet mme un bean qui lui permet dextraire ltat du modle. Si vous rflchissez au modle du navigateur, vous constaterez que la vue na besoin dtre informe des changements dtat que lorsquune rponse HTTP est retourne au navigateur; tout autre moment, une notification serait inutile. Ce nest que lorsquune page est cre et retourne que la gnration de la vue et lincorporation de ltat du modle ont un sens.
Voici une nouvelle page afficher
Navigateur web
bean
<html> <body> BPM <jsp:getProperty /> plus </body> </html>
JSP/HTML Vue
Contrleur
La vue reoit maintenant des u notifications d d une contrleur quanaire, pas page est ncess ment chaque change le. dtat du mod
deleTem Mo p arche()
m
Change dtat
o
vous tes ici
setBPM()
arret()
M() getBP
557
Stratgie
Dans Model2, lobjet Stratgie est toujours la servlet contrleur, mais elle nest pas compose directement avec la vue de la faon classique. Cela dit, cest un objet qui implmente un comportement pour la vue et nous pouvons le remplacer par un autre contrleur si nous voulons un comportement diffrent.
Voici une nouvelle page afficher
Composite
Comme dans notre IHM Swing, la vue est finalement constitue dun ensemble de composants graphiques. En loccurrence, ils sont rendus par un navigateur web partir dune description HTML, mais ce qui sous-tend le tout, cest un systme dobjets qui forment trs probablement un composite.
Lutilisateur a fait quelque chose
Navigateur web
bean
<html> <body> BPM <jsp:getProperty /> plus </body> </html>
Actualise laffichage,
JSP/HTML Vue
Le contrleur continue fournir le comportement de la vue, mme sil nest pas compos avec la vue en utilisant la composition dobjets.
Contrleur
deleTem Mo p
marche() setBPM() arret() M() getBP
Change dtat
558
Chapitre 12
Q: R:
questions stupides
Quand MVC a t baptis, on a eu besoin dun mot commenant par M, sinon on naurait pas pu lappeler MVC. Mais, srieusement, nous sommes daccord avec vous. Tout le monde se gratte la tte et se demande ce quest un modle. Et puis tout le monde parvient la conclusion quil ne voit pas de meilleur terme.
Il ny a pas de
patterns composs modle? Ne pourrait-on pas appliquer le modle push et envoyer ltat avec la notification de changement?
On dirait que vous insistez lourdement sur le fait que le pattern Composite est vraiment dans MVC. Est-il rellement l?
R:
Oui, il y a vraiment un pattern Composite dansMVC. Mais cest une trs bonne question. Aujourdhui, les packages comme Swing sont devenus si sophistiqus que nous sommes peine conscients de leur structure interne et de lemploi de composites dans la construction et la mise jour de laffichage. Cest encore plus difficile percevoir quand on est en prsence dun navigateur web capable de convertir en interface utilisateur un code crit dans un langage balises. lpoque o MVC a t dcouvert, la cration dIHM ncessitait beaucoup plus dinterventions manuelles et lappartenance du pattern MVC tait plus vidente.
Q: R:
Vous avez beaucoup parl de ltat du modle. Cela veut-il dire quil implmente le pattern tat?
Non, nous parlons de la notion gnrale dtat. Mais il existe certainement des modles qui utilisent le pattern tat pour grer leurs tats internes.
Q: R:
Oui, le modle pourrait sans doute envoyer son tat avec la notification. En fait, si vous regardez de nouveau la vue JSP/HTML, cest exactement ce que nous faisons. Nous transmettons la totalit du modle dans un bean, et la vue accde ltat dont elle a besoin en utilisant les proprits du bean. Nous pourrions faire quelque chose de similaire avec le ModeleTempo en nous contentant denvoyer ltat qui intresse la vue. Mais si vous vous souvenez du chapitre sur le pattern Observateur, vous vous souvenez galement que cette pratique prsente quelques inconvnients si vous ny regardez pas deux fois.
R:
Q: R:
Jai lu des descriptions de MVC qui affirment que le contrleur est un mdiateur entre la vue et le modle. Est-ce que le contrleur implmente le pattern Mdiateur?
Q: R:
Si jai plusieurs vues, est-ce que jai toujours besoin de plusieurs contrleurs?
Non. Le contrleur implmente un comportement pour la vue. Cest le petit malin qui traduit les actions provenant de la vue en actions sur le modle. Le modle reoit ces actions et cest lui qui implmente la logique applicative pour dcider quoi faire en rponse ces actions. Le contrleur pourrait participer en dterminant quelles mthodes appeler sur le modle, mais on ne peut pas parler de logique applicative. La vritable logique applicative, cest le code qui gre et manipule vos donnes, et il rside dans le modle.
Q:
Jai toujours eu un peu de mal saisir le terme de modle. Maintenant, je me rends compte que cest le cur de lapplication, mais pourquoi avoir employ un mot aussi vague et aussi difficile comprendre pour dcrire cet aspect deMVC?
Comme nous navons pas abord le pattern Mdiateur (mme si vous en trouverez une description dans lannexe), nous nentrerons pas dans les dtails. Toutefois, le mdiateur a pour fonction dencapsuler la faon dont les objets interagissent et dencourager le couplage faible en empchant les rfrences mutuelles explicites entre deux objets. Ainsi, jusqu un certain point, on peut voir le contrleur comme un mdiateur puisque la vue ne modifie jamais directement ltat du modle mais quelle passe par le contrleur. Mais noubliez pas que la vue a besoin dune rfrence au modle pour accder son tat. Si le contrleur tait un vrai mdiateur, la vue devrait galement passer par le contrleur pour accder ltat du modle.
En gnral, vous avez besoin dun contrleur par vue au moment de lexcution. Mais la mme classe contrleur peut facilement grer plusieurs vues.
Q:
La vue nest pas cense manipuler le modle. Pourtant, jai remarqu dans votre implmentation que la vue accdait toutes les mthodes qui modifiaient ltat du modle. Nest-ce pas dangereux?
Q:
Vous avez raison ; nous avons permis la vue daccder toutes les mthodes du modle. Nous lavons fait pour simplifier, mais il peut y avoir des circonstances dans lesquelles vous voudrez limiter laccs. Il y a un grand design pattern qui permet dadapter une interface pour ne fournir quun sous-ensemble des mthodes. Savez-vous lequel? vous tes ici
R:
559
POINTS DIMPACT
ce qui varie. Encapsulez lhritage. apsulation nc le ez r f Pr es, non des des interfac ez m am gr Pro ions. implmentat lement coupler faib e d us vo z Efforce ui interagissent. les objets q ouvertes oivent tre s la d s se as cl Les mais ferme lextensionn. modificatio ions. Ne . es abstract Dpendez d s des classes concrtes dpendez pa . u vos amis Ne parlez q vous lez pas, nous Ne nous appe appellerons. seule avoir quune t oi d ne se Une clas anger. raison de ch
Principes OO
Bases de lOO
Abstraction n Encapsulatio me Polymorphis Hritage
Contrleur (MVC) est un pattern compos constitu des patterns Observateur, Stratgie et Composite.
Patterns OO
Nous avons une ae m a le il oo m nn ast d t a vi lnay f e e a n r e n in o n P f io u a e it s D it d y e s d in r in a e nouvelle catgorie! o h f u f ee haca t q d st d ch n dF e cth e t u n r u a aly -rM tac o ns r. it q h a u st e e t ie r t e ss s y r A t u e f lt e u ll la t q o a v a so a b t tg t c e le c r s aA n , s r o s ic la a e su a e t t t ie m s o y r a p su c t c il e a a p b r t Str t t s la r n la m je je c a c s o e p a y s. a b O n b c su su tn b r d je e t m glo o n le nn p eo b o la c E a n t rb s, in c c a a e c su e oh e in er e n n p p gf F m t je g uo a E a n h a n bg le d u MVC et Model2 su D c in t a e o a o n n w ng.es. it e it t h r it b o at n a b a c E c r ll op e oo r ta llle a s e m y c u r r y le u a c A lge c o c o o t c t g ,lin m a g n y t h f t f et p in rw e t o dad t in l d e sit o g e a a e d e a d S d C a t f n d w r ie in d t n st e a y c vi st e s e e it a le f o ss A a x t r c t il s a r r m t r a c la o y b e le n e p s h F r c f S g b r r si t e le je le n it r y n e d n h o in P e r b ib a t n o o t o e t h ic x a e in r h sp c h lg t in n le e e e t s n e w x t f h r c t c e l , u d n it n t i e e e t t d sont des patterns a je e a s u f u , r n eee tf je id d hen q o ed t p a b o st r co e o sd h d g b in e ee ,ncp.t d p t vi w d je init if e g m t u n ee b n s. n r r s e sd re r n n st o if je e o e o a it et d li o tn p o ah ao b h e pa d h n c n ss vi o le d cx t s a a h se e la la ie e ooss w c o c s e it r t d g a b se la e ie t w b r h a t t u r t su o t li r ar if s n a a n e c f rD t e t it r t e o n c p g M ec m le o e n Stw n p in li t m o a y e e d a r ss l r n o d ss s, iz il a e d a la t t n ir composs. n c w e s e c c st e e iz cm t rtF p a n te suab r logg rrn c e eest f o je d uu .h in a ea o p n b e in m og r eeqq y a tt a ths, e pe r ie e e ia if iv ar o h c t p t n a vard T n sp r lo e io st e o u t r in e lt o ia a t e n u . u y a e q t ll u n a st st q s. e s, e ic n in t s, il a uss m ye.r o ble ooppeerraattio q f lut .st ue ee tnoc liet r q ions. d uis a af la n r ss c io la t a u poss uunnd doaable o t p r p t r o s. p su e p ss su la Patterns com e deux ou subc s combin
Compo Un Pattern ns pour rsoudre un ter t pa plusieurs ent. ral ou rcurr n g e problm
560
Chapitre 12
patterns composs
public class CompteurDeCouacs implements Cancaneur { Cancaneur canard; static int nombreDeCouacs; public CompteurDeCouacs(Cancaneur canard) { this.canard = canard; } public void cancaner() { canard.cancaner(); nombreDeCouacs++; } public static int getCouacs() { return nombreDeCouacs; }
Voici le canard que le CompteurDeCouacs dcore. Cest ce canard qui doit rellement grer les mthodes de CouacObservable.
public void enregisterObservateur(Observateur observateur) { canard.enregistrerObservateur (observateur); } public void notifierObservateurs() { canard.notifierObservateurs(); } }
Voici les mthodes de CouacObservable. Remarquez que nous dlguons simplement les deux appels au canard que nous dcorons.
561
solution pointue
public void cancaner() { Iterator iterateur = canards.iterator(); while (iterateur.hasNext()) { Cancaneur canard = (Cancaneur)iterateur.next(); canard.cancaner(); } }
un Quand vous enregistrezla de s observateur aupr alit Troupe, vous tes en r ut ce to de enregistr auprs troupe, qui APPARTIENT laue aq ch autrement dit de un canard Cancaneur, que ce soit ou une autre Troupe.
public void enregistrerObservateur(Observateur observateur) { Iterator iterateur = canards.iterator(); while (iterateur.hasNext()) { Cancaneur canard = (Cancaneur)iterateur.next(); canard.enregistrerObservateur(observateur); } } public void notifierObservateurs() { } }
Puisque chaque Cancaneur met ses propres notifications, la Troupe na pas besoin de sen occuper. Cest ce qui se passe quand la Troupe dlgue cancaner() chaque Cancaneur de la Troupe.
Nous parcourons tous les Cancaneurs de la Troupe et dlguons lappel chaque Cancaneur. Si le Cancaneur est une autre Troupe, il fera de mme.
562
Chapitre 12
patterns composs
Vous pourriez ajouter une mthode creerCanardOie() aux fabriques de canards existantes. Ou bien vous pourriez crer une fabrique totalement spare pour crer des familles doies.
Problme de conception
Vous venez de voir que la vue et le contrleur emploient tous deux le pattern Stratgie. Pouvez-vous tracer un diagramme de classes qu reprsente ce pattern?
La vue dlgue le comportement au contrleur. Le comportement quelle dlgue est la faon de manipuler le modle en rponse aux entres de lutilisateur.
VueDJ
contrleur
<<interface>> InterfaceControleur
setBPM() augmenterBPM() diminuerBPM()
InterfaceControleur est linterface que tous les contrleurs concrets implmentent. Cest linterface de stratgie.
Contrleur
setBPM() augmenterBPM() diminuerBPM()
Nous pouvons insrer diffrents contrleurs pour fournir diffrents comportements la vue.
563
Voici limplmentation complte de lapplication DJ. Elle montre tout le code MIDI pour gnrer le son et tous les composants Swing pour crer la vue. Vous pouvez galement tlcharger ce code en anglais sur http://www.wickedlysmart.com ou en franais ladresse http://www.oreilly.fr/catalogue/2841773507.html. Amusez-vous bien!
public class TestDJ { public static void main (String[] args) { InterfaceModeleTempo modele = new ModeleTempo(); InterfaceControleur controleur = new ControleurTempo(modele); } }
Le Modle de Tempo
package tetepremiere.mix.vuedj; public interface InterfaceModeleTempo { void initialiser(); void marche(); void arret(); void setBPM(int bpm); int getBPM(); void enregistrerObservateur(ObservateurBattements o); void supprimerObservateur(ObservateurBattements o); void enregistrerObservateur(ObservateurBPM o); void supprimerObservateur(ObservateurBPM o); }
564
Chapitre 12
patterns composs
package tetepremiere.mix.vuedj; import javax.sound.midi.*; import java.util.*; public class ModeleTempo implements InterfaceModeleTempo, MetaEventListener { Sequencer sequenceur; ArrayList observateursBattements = new ArrayList(); ArrayList observateursBPM = new ArrayList(); int bpm = 90; // autres variables dinstance Sequence sequence; Track piste; public void initialiser() { setUpMidi(); construirePisteEtDemarrer(); } public void marche() { sequenceur.start(); setBPM(90); } public void arret() { setBPM(0); sequenceur.stop(); } public void setBPM(int bpm) { this.bpm = bpm; sequenceur.setTempoInBPM(getBPM()); notifierObservateursBPM(); } public int getBPM() { return bpm; } void evenementBattement() { notifierObservateursBattements(); } public void enregistrerObservateur(ObservateurBattements o) { observateursBattements.add(o); } public void notifierObservateursBattements() { for(int i = 0; i < observateursBattements.size(); i++) {
565
566
Chapitre 12
patterns composs
sequenceur.open(); sequenceur.addMetaEventListener(this); sequence = new Sequence(Sequence.PPQ,4); piste = sequence.createTrack(); sequenceur.setTempoInBPM(getBPM()); } catch(Exception e) { e.printStackTrace(); } } public void construirePisteEtDemarrer() { int[] listePistes = {35, 0, 46, 0}; sequence.deleteTrack(null); piste = sequence.createTrack(); makeTracks(listePistes); piste.add(makeEvent(192,9,1,0,4)); try { sequenceur.setSequence(sequence); } catch(Exception e) { e.printStackTrace(); } } public void makeTracks(int[] list) { for (int i = 0; i < list.length; i++) { int key = list[i]; if (key != 0) { piste.add(makeEvent(144,9,key, 100, i)); piste.add(makeEvent(128,9,key, 100, i+1)); } } } public MidiEvent makeEvent(int comd, int chan, int one, int two, int tick) { MidiEvent evenement = null; try { ShortMessage a = new ShortMessage(); a.setMessage(comd, chan, one, two); evenement = new MidiEvent(a, tick); } catch(Exception e) { e.printStackTrace(); } return evenement; } } vous tes ici
567
La Vue
package tetepremiere.mix.vuedj; public interface ObservateurBattements { void majTempo(); }
package tetepremiere.mix.vuedj; import java.awt.*; import java.awt.event.*; import javax.swing.*; public class VueDJ implements ActionListener, ObservateurBattements, ObservateurBPM { InterfaceModeleTempo modele; InterfaceControleur controleur; JFrame cadreVue; JPanel panneauVue; BarrePulsante barre; JLabel affichageBPM; JFrame cadreControle; JPanel panneauControle; JLabel labelBPM; JTextField champTxtBPM; JButton definirBPM; JButton augmenterBPM; JButton diminuerBPM; JMenuBar barreMenus; JMenu menu; JMenuItem choixStart; JMenuItem choixStop; public VueDJ(InterfaceControleur controleur, InterfaceModeleTempo modele) { this.controleur = controleur; this.modele = modele; modele.enregistrerObservateur((ObservateurBattements)this); modele.enregistrerObservateur((ObservateurBPM)this); } public void creerVue() {
568
Chapitre 12
patterns composs
// Cration de tous les composants Swing panneauVue = new JPanel(new GridLayout(1, 2)); cadreVue = new JFrame(Vue); cadreVue.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); cadreVue.setSize(new Dimension(100, 80)); affichageBPM = new JLabel(hors ligne, SwingConstants.CENTER); barre = new BarrePulsante(); barre.setValue(0); JPanel panneauBMP = new JPanel(new GridLayout(2, 1)); panneauBMP.add(barre); panneauBMP.add(affichageBPM); panneauVue.add(panneauBMP); cadreVue.getContentPane().add(panneauVue, BorderLayout.CENTER); cadreVue.pack(); cadreVue.setVisible(true); } public void creerCommandes() { // Cration de tous les composants Swing JFrame.setDefaultLookAndFeelDecorated(true); cadreControle = new JFrame(Commandes); cadreControle.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); cadreControle.setSize(new Dimension(100, 80)); panneauControle = new JPanel(new GridLayout(1, 2)); barreMenus = new JMenuBar(); menu = new JMenu(Commandes DJ); choixStart = new JMenuItem(Dmarrer); menu.add(choixStart); choixStart.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evenement) { controleur.start(); } }); choixStop = new JMenuItem(Arrter); menu.add(choixStop); choixStop.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evenement) { controleur.stop(); //affichageBPM.setText(hors ligne); } }); JMenuItem exit = new JMenuItem(Quitter); exit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evenement) { System.exit(0); } }); vous tes ici
569
570
Chapitre 12
patterns composs
} public void activerChoixStart() { choixStart.setEnabled(true); } public void desactiverChoixStart() { choixStart.setEnabled(false); } public void actionPerformed(ActionEvent evenement) { if (evenement.getSource() == definirBPM) { int bpm = Integer.parseInt(champTxtBPM.getText()); controleur.setBPM(bpm); } else if (evenement.getSource() == augmenterBPM) { controleur.augmenterBPM(); } else if (evenement.getSource() == diminuerBPM) { controleur.diminuerBPM(); } } public void majBPM() { int bpm = modele.getBPM(); if (bpm == 0) { affichageBPM.setText(hors ligne); } else { affichageBPM.setText(Nombre de BPM: + modele.getBPM()); } } public void majTempo() { barre.setValue(100); } }
Le Contrleur
package tetepremiere.mix.vuedj; public void void void void void } interface InterfaceControleur { start(); stop(); augmenterBPM(); diminuerBPM(); setBPM(int bpm);
571
package tetepremiere.mix.vuedj; public class ControleurTempo implements InterfaceControleur { InterfaceModeleTempo modele; VueDJ vue; public ControleurTempo(InterfaceModeleTempo modele) { this.modele = modele; vue = new VueDJ(this, modele); vue.creerVue(); vue.creerCommandes(); vue.desactiverChoixStop(); vue.activerChoixStart(); modele.initialiser(); } public void start() { modele.marche(); vue.desactiverChoixSart(); vue.activerChoixStop(); } public void stop() { modele.arret(); vue.desactiverChoixStop(); vue.activerChoixStart(); } public void augmenterBPM() { int bpm = modele.getBPM(); modele.setBPM(bpm + 1); } public void diminuerBPM() { int bpm = modele.getBPM(); modele.setBPM(bpm - 1); } public void setBPM(int bpm) { modele.setBPM(bpm); } }
572
Chapitre 12
patterns composs
Le modle du Cur
package tetepremiere.mix.vuedj; public class TestCoeur { public static void main (String[] args) { ModeleCoeur modeleCoeur = new ModeleCoeur(); InterfaceControleur modele = new ControleurCoeur(modeleCoeur); } } package tetepremiere.mix.vuedj; public interface InterfaceModeleCoeur { int getRythmeCardiaque(); void enregistrerObservateur(ObservateurBattements o); void supprimerObservateur(ObservateurBattements o); void enregistrerObservateur(ObservateurBPM o); void supprimerObservateur(ObservateurBPM o); } package tetepremiere.mix.vuedj; import java.util.*; public class ModeleCoeur implements InterfaceModeleCoeur, Runnable { ArrayList observateursBattements = new ArrayList(); ArrayList observateursBPM = new ArrayList(); int temps = 1000; int bpm = 90; Random random = new Random(System.currentTimeMillis()); Thread thread; public ModeleCoeur() { thread = new Thread(this); thread.start(); } public void run() { int dernierRythme = -1; for(;;) { int change = random.nextInt(10); if (random.nextInt(2) == 0) { change = 0 - change; } int rythme = 60000/(temps + change); if (rythme < 120 && rythme > 50) { temps += change; vous tes ici
573
code prt lemploi: le modle du cur notifierObservateursBattements(); if (rythme != dernierRythme) { dernierRythme = rythme; notifierObservateursBPM(); } } try { Thread.sleep(temps); } catch (Exception e) {} } } public int getRythmeCardiaque() { return 60000/temps; } public void enregistrerObservateur(ObservateurBattements o) { observateursBattements.add(o); } public void supprimerObservateur(ObservateurBattements o) { int i = observateursBattements.indexOf(o); if (i >= 0) { observateursBattements.remove(i); } } public void notifierObservateursBattements() { for(int i = 0; i < observateursBattements.size(); i++) { ObservateurBattements observateur = (ObservateurBattements)observateursBattements. get(i); observateur.majTempo(); } } public void enregistrerObservateur(ObservateurBPM o) { observateursBPM.add(o); } public void supprimerObservateur(ObservateurBPM o) { int i = observateursBPM.indexOf(o); if (i >= 0) { observateursBPM.remove(i); } } public void notifierObservateursBPM() { for(int i = 0; i < observateursBPM.size(); i++) { ObservateurBPM observateur = (ObservateurBPM)observateursBPM.get(i); observateur.majBPM(); } } }
574
Chapitre 12
patterns composs
LAdaptateur du Cur
package tetepremiere.mix.vuedj; public class AdaptateurCoeur implements InterfaceModeleTempo { InterfaceModeleCoeur coeur; public AdaptateurCoeur(InterfaceModeleCoeur coeur) { this.coeur = coeur; } public void initialiser() {} public void marche() {} public void arret() {} public int getBPM() { return coeur.getRythmeCardiaque(); } public void setBPM(int bpm) {} public void enregistrerObservateur(ObservateurBattements o) { coeur.enregistrerObservateur(o); } public void supprimerObservateur(ObservateurBattements o) { coeur.supprimerObservateur(o); } public void enregistrerObservateur(ObservateurBPM o) { coeur.enregistrerObservateur(o); } public void supprimerObservateur(ObservateurBPM o) { coeur.supprimerObservateur(o); } }
575
Le Contrleur
package tetepremiere.mix.vuedj; public class ControleurCoeur implements InterfaceControleur { InterfaceModeleCoeur modele; VueDJ vue; public ControleurCoeur(InterfaceModeleCoeur modele) { this.modele = modele; vue = new VueDJ(this, new AdaptateurCoeur(modele)); vue.creerVue(); vue.creerCommandes(); vue.desactiverChoixStop(); vue.desactiverChoixStart(); } public void start() {} public void stop() {} public void augmenterBPM() {} public void diminuerBPM() {} public void setBPM(int bpm) {} }
576
Chapitre 12
g g
Maintenant, vous tes prt vivre dans un monde nouveau, un monde peupl de Design Patterns. Mais avant de vous laisser ouvrir toutes
ces nouvelles portes, nous devons aborder quelques dtails que vous rencontrerez dans le monde rel. Eh oui, les choses y sont un peu plus complexes quelles ne le sont ici, Objectville. Suivez-nous! Vous trouverez la page suivante un guide extra qui vous aidera effectuer la transition...
nouveau chapitre
577
Il contient de n e. id u g e ide, vous allez: tr u o g n i ce is s o n a ch ir D vo . el a r Merci d ns dans le monde n er tt a p s le c ve a e tes sur la dfinitio vivr n ra u co s lu p s le nt les ides fausses so s le el u q re d n re b App ttern. dre dun design pa tterns et compren a p e d es u g lo ta erbes ca uil existe de sup q ir vr en procurer un. u s o u c vo t D en m b lu so b ez a pourquoi vous dev n pattern. ig es d n u d n u rt o pp e lemploi ino ci o ss a te n o h la r b Evite s appartiennent. n er tt a p s le es ri o tg atre quelles ca n n co re re d n re b App x gourous; lisez u a e rv se r s a p st ur de patterns. rte de patterns nue te ve u u a o n c u d i ss la u e a u s q b Voir pide et devenez vo ra ce n re f r atre e tr o n se Bande des qu u ie r st y m la e d t nd la vraie identi a u q t n se r p e tr b E sera rvle. chevet que tout e d es vr li s le r ce us laisser distaner vo s a p e n re d n d oit de poss . b Appre d se s n er tt a p e d utilisateur matre zen. n u e m m co t ri p e es dre exercer votr b Appren ner vos collgues n io ss re p im et is am ent vous faire deesvocabulaire. m m co ir vr u o c b D mliorant votr dveloppeurs en a
578
Chapitre 13
Plutt sibyllin comme dfinition, non? Ne vous inquitez pas, nous allons dcortiquer chacun de ses composants: contexte, problme et solution:
Cest lune de ces dfinitions quil faut un peu de temps pour assimiler, mais vous pouvez la dcomposer en tapes. Voici un petit procd mnmonique que vous pouvez vous rpter pour vous en souvenir:
Si vous vous trouvez dans un contexte avec un problme qui implique un but qui est affect par un ensemble de contraintes, vous pouvez appliquer une conception qui permet datteindre le but, de rsoudre les contraintes et de dboucher sur une solution.
Vous pensez sans doute que cela reprsente beaucoup de travail uniquement pour parvenir comprendre ce quest un design pattern. Aprs tout, vous savez dj quun pattern fournit une solution un problme de conception rcurrent. O donc tout ce formalisme nous mne-t-il? Eh bien, vous allez voir que le fait de disposer dune faon formelle de dcrire les patterns nous permet de crer des catalogues de patterns, ce qui prsente toutes sortes davantages.
vous tes ici
579
Jai bien rflchi cette dfinition en trois parties, et je nai pas du tout limpression quelle dcrit un pattern.
Vous avez peut-tre raison. Rflchissons un peu... Il nous faut un problme, un contexte et une solution: Problme: Comment faire pour arriver lheure au bureau? Contexte: Jai claqu la porte avec mes cls dans la voiture. Solution: Casser la vitre, monter dans la voiture, dmarrer et se rendre au bureau. Nous avons tous les composants de la dfinition: nous avons un problme, un but aller travailler et des contraintes de temps et de distance probablement assorties dautres facteurs. Nous avons galement un contexte, dans lequel les cls du vhicule sont inaccessibles. Enfin, nous avons une solution qui nous permet de rcuprer les cls et de respecter les contraintes de temps et de distance. Nous devons donc tre en prsence dun pattern! Quen pensez-vous?
A
580
Nous avons suivi notre dfinition et dcrit un problme, un contexte et une solution (qui fonctionne!). Sagit-il pour autant dun pattern? Sinon, pourquoi? Pourrions-nous commettre la mme erreur en dfinissant un design patternOO?
Chapitre 13
La prochaine fois que quelquun vous dit quun pattern est une solution un problme dans un contexte, hochez la tte et souriez. Vous savez ce quil veut dire, mme si ce nest pas une dfinition suffisante pour expliquer ce quest rellement un Design Pattern.
Q: R:
Ces descriptions, que vous trouvez gnralement dans les catalogues de patterns, sont dordinaire un peu plus explicites. Nous allons aborder en dtail ces catalogues de patterns dans une minute, mais sachez quils mentionnent lintention et la motivation du pattern, les circonstances dans lesquelles il peut sappliquer ainsi que la conception de la solution et les consquences (positives ou non) de son emploi. Ai-je le droit de modifier lgrement la structure dun pattern pour ladapter ma conception? Ou bien suis-je oblig dappliquer strictement la dfinition?
Bien sr, vous pouvez la modifier. linstar des principes de conception, les patterns ne sont pas censs tre des lois ou des rgles. Ce sont des lignes directrices que vous pouvez adapter en fonction de vos besoins. Comme vous lavez constat, de nombreux exemples du monde rel saccommodent mal des dfinitions classiques. Mais quand vous adaptez des patterns, cela ne fait jamais de mal dexpliciter en quoi votre pattern diffre de la conception classique cela permet aux autres dveloppeurs de reconnatre rapidement les patterns que vous utilisez, ainsi que les diffrences quils prsentent avec les patterns canoniques.
R:
Q: R:
Le premier catalogue de patterns, qui fait autorit, est Design Patterns: Catalogue de modles de conception rutilisables, de Gamma, Helm, Johnson & Vlissides (Vuibert informatique). Cet ouvrage dcrit vingt-trois patterns fondamentaux, et nous en parlerons plus en dtail dans quelques pages. De nombreux catalogues de patterns commencent paratre dans diffrents domaines, notamment les logiciels dentreprise, les systmes concurrents et les systmes de gestion.
Q:
581
Trucs de geeks
La dfinition du Design Pattern nous dit que le problme est constitu dun but et dun ensemble de contraintes. Les gourous des patterns ont un terme pour dsigner ces composants: ils parlent de forces. Pourquoi? Eh bien ils ont certainement leurs propres raisons, mais si vous vous souvenez du film, la force faonne et contrle lUnivers. De mme, dans la dfinition du pattern, les forces faonnent et contrlent la solution. Ce nest que lorsque les deux aspects de la force (le ct lumineux, votre but, et le ct sombre, les contraintes) sont quilibrs que vous avez un pattern utile. Ce terme de force peut tre droutant la premire fois que vous le rencontrez dans la prsentation dun pattern, mais il suffit de se souvenir que la force prsente deux aspects (les buts et les contraintes) et quil faut les quilibrer ou les rsoudre pour crer une solution. Ne vous laissez pas impressionner par le jargon, et que la force soit avec vous!
582
Chapitre 13
Guy
Paul
Yann
Paul: Mets-nous au courant, Yann. Jai seulement lu quelques articles sur les patterns ici et l. Yann: Bien sr. Chaque catalogue aborde un ensemble de patterns, dcrit chacun deux en dtail et explique leurs relations avec les autres patterns. Guy: Es-tu en train de dire quil existe plusieurs catalogues de patterns? Yann: Naturellement. Il y a des catalogues pour les patterns fondamentaux, et dautres pour des patterns spcifiques un domaine, par exemple les EJB. Paul: Et lequel regardes-tu? Yann: Cest le catalogue GoF classique; il contient vingt-trois design patterns fondamentaux. Paul: GoF? Yann: Oui, les initiales de Gang of Four, bande des quatre en franais. La bande des quatre, ce sont les auteurs qui ont assembl le premier catalogue de patterns. Guy: Et quest-ce quil y a dans le catalogue? Yann: Un ensemble de patterns apparents. La description de chaque pattern observe toujours la mme structure et fournit de nombreux dtails. Par exemple, chaque pattern porte un nom.
vous tes ici
583
Paul: Diable! Cest la rvlation de lanne un nom! Voyez-vous a. Yann: Pas si vite, Paul. En fait, le nom est rellement important. Quand un pattern est nomm, nous disposons dun moyen den parler, tu sais, toute cette histoire de vocabulaire partag Paul: Daccord, daccord, je plaisantais. Continue. Quy a-t-il dautre? Yann: Eh bien, comme je le disais, la description de chaque pattern observe la mme structure. Pour chacun, nous avons un nom et un certain nombre de sections qui lexpliquent en dtail. Par exemple, il y a une section Intention qui expose ce quest le pattern, comme une sorte de dfinition. Puis il y a les sections Motivation et Indications dutilisation qui dcrivent quand et o le pattern peut tre employ. Guy: Et la conception elle-mme? Yann: Il y a plusieurs sections qui dcrivent la conception, avec toutes les classes qui la constituent et le rle quelles jouent. Il y a aussi une section qui explique comment implmenter le pattern, et souvent des exemples de code. Paul: On dirait quils ont pens tout. Yann: Ce nest pas tout. Il y a aussi des exemples dutilisation des patterns dans des systmes rels, et une section que je considre comme lune des plus utiles: celle qui explique les rapports du pattern avec dautres patterns. Paul: Oh, tu veux dire quils disent par exemple en quoi tat et Stratgie diffrent. Yann: Exactement! Guy: Alors, Yann, comment utilises-tu le catalogue? Quand tu as un problme, tu vas la pche pour y trouver une solution? Yann: Jessaie dabord de me familiariser avec tous les patterns et leurs relations. Ensuite, quand jai besoin dun pattern, jai dj une bonne ide de celui quil me faut et je lis les sections Motivation et Indications dutilisation pour vrifier que cest le bon. Il y a aussi une section trs importante, nomme Consquences. Je la consulte pour tre sr quil ny aura pas deffets de bord accidentels sur ma conception. Paul: Cest logique. Donc, une fois que tu sais que tu as le bon pattern comment procdes-tu pour lincorporer ta conception et limplmenter? Yann: Cest l que les diagrammes de classes entrent en jeu. Je commence par lire la section Structure pour avoir une vue densemble avec le diagramme, puis la section Participants pour vrifier que jai bien compris le rle de chaque classe. partir de l, jincorpore le pattern ma conception, en apportant toutes les modifications ncessaires pour ladapter. Enfin, je lis les sections Implmentation et Exemples de code pour tre certain de connatre toutes les bonnes techniques dimplmentation et les piges que je pourrais rencontrer. Guy: Je vois comment un catalogue pourrait maider me mettre plus rapidement aux patterns! Paul: Absolument. Yann, pourrais-tu nous faire faire une visite commente? 584
Chapitre 13
Dans un catalogue, tous les patterns commencent par un nom. Le nom est une composante vitale du pattern en labsence de nom bien choisi, il ne peut pas faire partie du vocabulaire que vous partagez avec les autres dveloppeurs. La section Motivation prsente un scnario concret qui dcrit le problme et la faon dont la solution rsout celui-ci.
SINGLETON
Intention
Objet Crateur
Et aliquat, velesto ent lore feuis acillao rperci tat, quat nonsequam il ea at nim nos do enim qui eratio ex ea faci tet, sequis dion utat, volore magnisi.
Motivation
Et aliquat, velesto ent lore feuis acillao rperci tat, quat nonsequam il ea at nim nos do enim qui eratio ex ea faci tet, sequis dion utat, volore magnisi.Rud modolore dit laoreet augiam iril el dipis dionsequis dignibh eummy nibh esequat. Duis nulputem ipisim esecte conullut wissi. Os nisissenim et lumsandre do con el utpatuero corercipis augue doloreet luptat amet vel iuscidunt digna feugue dunt num etummy nim dui blaor sequat num vel etue magna augiat. Aliquis nonse vel exer se minissequis do dolortis ad magnit, sim zzrillut ipsummo dolorem dignibh euguer sequam ea am quate magnim illam zzrit ad magna feu facinit delit ut
Indications dutilisation
Duis nulputem ipisim esecte conullut wissiEctem ad magna aliqui blamet, conullandre dolore magna feuis nos alit ad magnim quate modolore vent lut luptat prat. Dui blaore min ea feuipit ing enit laore magnibh eniat wisissecte et, suscilla ad mincinci blam dolorpe rcilit irit, conse dolore dolore et, verci enis enit ip elesequisl ut ad esectem ing ea con eros autem diam nonullu tpatiss ismodignibh er.
Structure
Singleton
static uniqueInstance // Autres variables dinstance... static getInstance() // Autres mthodes...
La section Indications dutilisation dcrit les situations dans lesquelles le pattern peut tre appliqu.
Lintention dcrit brivement la fonction du pattern. Vous pouvez galement la considrer comme sa dfinition (cest ainsi que nous lavons nomme dans ce livre).
Participants
Duis nulputem ipisim esecte conullut wissiEctem ad magna aliqui blamet, conullandre dolore magna feuis nos alit ad magnim quate modolore vent lut luptat prat. Dui blaore min ea feuipit ing enit laore magnibh eniat wisissecte et, suscilla ad mincinci blam dolorpe rcilit irit, conse dolore dolore et, verci enis enit ip elesequisl ut ad esectem ing ea con eros autem diam nonullu tpatiss ismodignibh er
sses et Les participants sont les claCe tte les objets de la conception.sabilit s et section dcrit leurs respon pattern. le rle quils jouent dans le
Les consquences dcrivent les effets possibles positifs ou ngatifs de lutilisation de ce pattern.
A dolore dolore et, verci enis enit ip elesequisl ut ad esectem ing ea con eros autem diam nonullu tpatiss ismodignibh er A feuis nos alit ad magnim quate modolore vent lut luptat prat. Dui blaore min ea feuipit ing enit laore magnibh eniat wisissec Ad magnim quate modolore vent lut luptat prat. Dui blaore min ea feuipit ing enit
La section Structure contient un diagramme qui illustre les relations entre les classes qui participent au pattern.
La section Collaborations nous indique comment les participants collaborent dans le pattern.
Collaborations
Feuipit ing enit laore magnibh eniat wisissecte et, suscilla ad mincinci blam dolorpe rcilit irit, conse dolore.
Consquences
Duis nulputem ipisim esecte conullut wissiEctem ad magna aliqui blamet, conullandre: 1. Dolore dolore et, verci enis enit ip elesequisl ut ad esectem ing ea con eros autem diam nonullu tpatiss ismodignibh er. 2. Modolore vent lut luptat prat. Dui blaore min ea feuipit ing enit laore magnibh eniat wisissecte et, suscilla ad mincinci blam dolorpe rcilit irit, conse dolore dolore et, verci enis enit ip elesequisl ut ad esectem. 3. Dolore dolore et, verci enis enit ip elesequisl ut ad esectem ing ea con eros autem diam nonullu tpatiss ismodignibh er.
4. Modolore vent lut luptat prat. Dui blaore min ea feuipit ing enit laore magnibh eniat wisissecte et, suscilla ad mincinci blam dolorpe rcilit irit, conse dolore dolore et, verci enis enit ip elesequisl ut ad esectem.
Implmentation/Exemples de code
La section Implmentation fournit les techniques que vous devez employer pour implmenter ce pattern et signale les problmes auxquels vous devez tre attentif. La section Utilisations remarquables dcrit des exemples de ce pattern rencontrs dans des systmes rels.
DuDuis nulputem ipisim esecte conullut wissiEctem ad magna aliqui blamet, conullandre dolore magna feuis nos alit ad magnim quate modolore vent lut luptat prat. Dui blaore min ea feuipit ing enit laore magnibh eniat wisissecte et, suscilla ad mincinci blam dolorpe rcilit irit, conse dolore dolore et, verci enis enit ip elesequisl ut ad esectem ing ea con eros autem diam nonullu tpatiss ismodignibh er.
public class Singleton { private static Singleton
uniqueInstance;
// autres variables dinstance private Singleton() {} public static synchroniz ed Singleton getInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton( ); } return uniqueInstance; } // autres mthodes }
Les exemple d e code sont des frasgm en programme qui ts de vous aider dan peuvent implmentations votre .
Nos alit ad magnim quate modolore vent lut luptat prat. Dui blaore min ea feuipit ing enit laore magnibh eniat wisissecte et, suscilla ad mincinci blam dolorpe rcilit irit, conse dolore dolore et, verci enis enit ip elesequisl ut ad esectem ing ea con eros autem diam nonullu tpatiss ismodignibh er.
Utilisations remarquables
DuDuis nulputem ipisim esecte conullut wissiEctem ad magna aliqui blamet, conullandre dolore magna feuis nos alit ad magnim quate modolore vent lut luptat prat. Dui blaore min ea feuipit ing enit laore magnibh eniat wisissecte et, suscilla ad mincinci blam dolorpe rcilit irit, conse dolore dolore et, verci enis enit ip elesequisl ut ad esectem ing ea con eros autem diam nonullu tpatiss ismodignibh er.
DuDuis nulputem ipisim esecte conullut wissiEctem ad magna aliqui blamet, conullandre dolore magna feuis nos alit ad magnim quate modolore vent lut luptat prat. Dui blaore min ea feuipit ing enit laore magnibh eniat wisissecte et, suscilla ad mincinci blam dolorpe rcilit irit, conse dolore dolore et, verci enis enit ip elesequisl ut ad esectem ing ea con eros autem diam nonullu tpatiss ismodignibh er. alit ad magnim quate modolore vent lut luptat prat. Dui blaore min ea feuipit ing enit laore magnibh eniat wisissecte et, suscilla ad mincinci blam dolorpe rcilit irit, conse dolore dolore et, verci enis enit ip elesequisl ut ad esectem ing ea con eros autem diam nonullu tpatiss ismodignib h er.
Patterns apparents
Elesequisl ut ad esectem ing ea con eros autem diam nonullu tpatiss ismodignib h er. alit ad magnim quate modolore vent lut luptat prat. Dui blaore min ea feuipit ing enit laore magnibh eniat wisissecte et, suscilla ad mincinci blam dolorpe rcilit irit, conse dolore dolore et, verci enis enit ip elesequisl ut ad esectem ing ea con eros autem diam nonullu tpatiss ismodignibh er.
La section Patterns apparents dcrit les relations que ce pattern entretient avec dautres.
vous tes ici
585
Q: R:
questions stupides
Il ny a pas de
Est-il possible de crer ses propres design patterns ou bien faut-il tre un gourou pour pouvoir le faire?
Tout dabord, souvenez-vous que les patterns sont dcouverts, pas crs. En consquence, nimporte qui peut dcouvrir un design pattern puis crire sa description. Mais ce nest pas facile, et ce nest ni rapide ni frquent. tre un auteur de patterns demande beaucoup dinvestissement. Demandez-vous dabord pourquoi vous en avez envie: la plupart des informaticiens ncrivent pas de patterns, ils se contentent de les utiliser. Mais vous travaillez peut-tre dans un domaine spcialis dans lequel vous pensez que de nouveaux patterns seraient utiles, ou peut-tre tes vous tomb par hasard sur une solution ce que vous considrez comme un problme rcurrent. Il se peut aussi que vous vouliez simplement vous impliquer dans la communaut des patterns et contribuer toffer le corpus existant.
Donc vous voulez tre une star des patterns? coutez dabord ce que je vous dis. Achetez un catalogue de patterns, Passez-y du temps, tudiez-le bien. Et quand votre description sra au point, Et qutrois dveloppeurs seront dvotre avis, Alors vous saurez que cest un pattern.
Sur lair de So you wanna be a Rockn Roll Star.
Comme dans toute discipline, plus vous en saurez mieux ce sera. Ltude des patterns existants, de ce quils font et de leurs relations avec dautres patterns, est cruciale. Non seulement elle vous permet de vous familiariser avec la faon dont les patterns sont forgs, mais elle vous vite de rinventer la roue. De l, vous voudrez sans doute commencer jeter vos propres patterns sur le papier afin de pouvoir en faire part dautres dveloppeurs; nous verrons comment procder pour communiquer vos patterns dans un instant. Si vous tes rellement intress, lisez la section qui suit ces questions-rponses.
Q: R:
Q: R:
Cest une trs bonne question: vous navez pas de pattern tant que dautres dveloppeurs ne lont pas utilis et constat quil fonctionnait. En gnral, un pattern nen est pas un tant quil na pas russi le test de la rgle des trois. Cette rgle nonce quon ne peut qualifier un pattern de pattern que sil a t appliqu au moins trois fois dans une situation du monde rel.
586
Chapitre 13
Utilisez lun des formats existants pour dfinir votre pattern. Ces formats ont t soigneusement penss et les autres utilisateurs de patterns les reconnatront.
587
Pattern
Dcorateur tat Itrateur Faade Stratgie Proxy Fabrication Adaptateur Observateur Patron de mthode Composite Singleton Fabrique Abstraite Commande
588
Chapitre 13
Enveloppe un objet et fournit une interface diffrente pour y accder Les sous-classes dcident de la faon dimplmenter les tapes dun algorithme Les sous-classes dcident quelles sont les classes concrtes crer Garantit quun objet et un seul est cr Encapsule des comportements interchangeables et utilise la dlgation pour dcider lequel utiliser Les clients traitent les collections dobjets et les ^ manire objets individuels de la mme Encapsule des comportements bass sur des tats et utilise la dlgation pour permuter ces comportements Fournit un moyen de parcourir une collection dobjets sans exposer son implmentation Simplifie linterface dun ensemble de classes Enveloppe un objet pour fournir un nouveau comportement Permet un client de crer des familles dobjets sans spcifier leurs classes concrtes. Permet de notifier des changements dtat des objets Enveloppe un objet et en contrle laccs Encapsule une requte sous forme dobjet
Description
Composite
Dcorateur
tat
Stratgie
Adaptateur
Observateur
Singleton
Patron de mthode
Lisez la description de chaque catgorie et voyez si vous pouvez placer ces patterns dans celle qui convient. Ce nest pas vident, mais faites de votre mieux puis vrifiez vos rponses page suivante.
Fabrication
Commande
Proxy
Itrateur
Faade
Les patterns crateurs concernent linstanciation des objets et fournissent tous un moyen de dcoupler un client des objets quil a besoin dinstancier.
Tout pattern qui est un pattern comportemental est en rapport avec les interactions des classes et des objets et la distribution des responsabilits.
Crateurs
Comportementaux
Structurels
Les patterns structurels permettent de composer des classes ou des objets pour former des structures plus vastes. vous tes ici
589
catgories de patterns
Les Patterns Crateurs concernent linstanciation des objets et fournissent tous un moyen de dcoupler un client des objets quil a besoin dinstancier.
Tout pattern qui est un Pattern Comportemental est en rapport avec les interactions des classes et des objets et la distribution des responsabilits.
Crateurs
Singleton Monteur Prototype Fabrique abstraite Fabrication
Comportementaux
Commande Itrateur Mdiateur Visiteur Chane de responsabilits tat Observateur Mmento Interprte Stratgie Patron de mthode
Structurels
Dcorateur Composite Poids-mouche Faade Pont
Proxy
Adaptateur
Certains patterns (en gris) nont pas t abords. Vous en trouverez un aperu dans lannexe.
Les Patterns Structurels permettent de composer des classes ou des objets pour former des structures plus vastes.
590
Chapitre 13
On applique souvent un autre critre de classification: un pattern donn sapplique-t-il des classes ou des objets?
Les patterns de classes dfinissent des relations entre classes et sont dfinis via lhritage. Dans ces patterns, les relations sont tablies au moment de la compilation. Les patterns dobjets dcrivent des relations entre objets et sont principalement dfinis par composition. Dans ces patterns, les relations sont gnralement dfinies au moment de lexcution et sont plus dynamiques et plus souples.
Visiteur Itrateur Commande Mmento Mdiateur Fabrique abstraite Observateur tat
Class e
Patron de mthode Adaptateur Fabrication Interprte
Objet
Composite Dcorateur Faade Pont Proxy Stratgie
Notez que les ts patterns dobjeplus up o sont beauc les nombreux queclasses patterns de
Q: R:
Non, dautres schmas ont t proposs. Certains commencent par les trois catgories puis ajoutent des sous-catgories, telles que patterns de dcouplage. Vous devez connatre les schmas dorganisation les plus courants, mais nhsitez pas crer les vtres si cela vous aide mieux comprendre les patterns.
En tout cas, cela vous fournit une structure qui permet de les comparer. Mais ces catgories crateur, structurel et comportemental droutent de nombreuses personnes parce que les patterns semblent souvent pouvoir appartenir plus dune catgorie. Le plus important est de connatre les patterns et les relations quils entretiennent. Si les catgories vous aident, utilisez-les!
R:
questions stupides
Il ny a pas de
Q:
Q:
Pourquoi le pattern Dcorateur est-il dans la catgorie Structurel? Jaurais cru que ctait un pattern comportemental: aprs tout, il permet dajouter des comportements!
Oui, cest lopinion de nombreux dveloppeurs! Mais voici lide qui soustend la classification de la Bande des quatre: les patterns structurels dcrivent la faon dont les objets sont composs pour crer de nouvelles structures ou de nouvelles fonctionnalits. Le pattern Dcorateur permet de composer des objets en enveloppant un objet dans un autre afin de fournir une nouvelle fonctionnalit. Laccent est donc mis sur la composition dynamique des objets pour obtenir une fonctionnalit plutt que sur la communication et linterconnexion entre objets, qui sont du ressort des patterns comportementaux. Cest une distinction subtile, surtout quand vous considrez les similitudes structurelles entre Dcorateur (un pattern structurel) et Proxy (un pattern comportemental). Mais noubliez pas que lintention de ces patterns est diffrente, et cest souvent la cl qui permet de comprendre pourquoi un pattern appartient telle ou telle catgorie. vous tes ici
R:
591
catgories de patterns
Matre et disciple...
Matre: Tu as lair troubl, Petit Scarabe. Disciple: Oui, je viens dtudier la classification des patterns et mes ides sont confuses. Matre: Continue, Scarabe... Disciple: Aprs avoir autant tudi les patterns, je viens dapprendre que chacun deux appartenait une catgorie: Structurel, Comportemental ou Crateur. Pourquoi avons-nous besoin de ces classifications? Matre: Scarabe, chaque fois que nous avons une importante collection dentits quelconques, nous trouvons naturellement des catgories dans lesquelles les faire entrer. Cela nous permet de les penser un niveau plus abstrait. Disciple: Matre Pouvez-vous me donner un exemple? Matre: Bien sr. Prends les automobiles. Il existe de nombreux modles dautomobiles diffrents et nous les classons naturellement dans des catgories, comme les voitures conomiques, les voitures de sport, les vhicules utilitaires, les camionnettes et les voitures de luxe. Disciple: ... Matre: Tu as lair choqu, Scarabe. Trouverais-tu cela illogique? Disciple: Cest trs logique, Matre, mais votre connaissance des voitures me stupfie! Matre: Scarabe, je ne peux pas tout comparer des fleurs de lotus ou des bols de riz. Puis-je continuer? Disciple: Oui, oui, je suis dsol. Continuez, Matre, je vous en prie. Matre: Une fois quon dispose de classifications ou de catgories, il est plus facile de parler des diffrents groupes: Si vous vous rendez de Sophia Antipolis Val-dIsre par les routes touristiques, une voiture sportive avec une bonne tenue de route est la meilleure option. Ou encore Avec la dgradation de la situation ptrolire, mieux vaut acheter une voiture conomique peu gourmande en carburant. Disciple: Les catgories nous permettent donc de parler dun ensemble de patterns en tant que groupe. Nous savons peut-tre quil nous faut un pattern comportemental sans savoir exactement lequel, mais nous pouvons toujours parler de patterns comportementaux. Matre: Oui, et elles nous offrent galement un moyen de comparer un membre au reste de la catgorie, par exemple, la Mini est
592
Chapitre 13
vraiment la voiture compacte la plus lgante qui soit ou de restreindre notre recherche Jai besoin dune voiture qui ne consomme pas trop. Disciple : Je vois Je pourrais donc galement dire que le pattern Adaptateur est le pattern structurel qui convient le mieux pour changer linterface dun objet. Matre: Oui. Nous pouvons galement employer des catgories pour une autre raison: pour nous lancer sur un nouveau march. Par exemple: Notre objectif est de proposer une voiture ayant les performances dune Ferrari pour le prix dune Logan. Disciple : Un pige mortel, somme toute. Matre: Dsol, je nai pas entendu, Scarabe. Disciple : Euh Jai dit Oui, Matre, jcoute. Matre : Mmmm Disciple : Et ainsi, les catgories nous fournissent un moyen de voir quels sont les rapports entre les groupes de patterns et comment les patterns au sein dun mme groupe sont relis. Elles nous permettent galement dextrapoler de nouveaux patterns. Mais pourquoi y a-t-il trois catgories, et pas quatre ou cinq? Matre: Ah, telles les toiles au firmament, il y a autant de catgories que tu veux en voir. Trois est un nombre pratique et de nombreuses personnes lont trouv commode pour grouper les patterns. Mais dautres en ont suggr quatre, ou cinq, voire plus.
593
Restez simple
En premier lieu, efforcez-vous de trouver la solution la plus naturelle possible. Vous devez viser la simplicit, non vous demander comment vous pourriez bien appliquer un pattern un problme donn. Nallez pas imaginer que vous ntes pas un dveloppeur raffin si vous nutilisez pas un pattern pour rsoudre un problme. Vos pairs apprcieront et ils admireront la simplicit de votre conception. Cela dit, il arrive que la meilleure faon de conserver simplicit et souplesse consiste employer un pattern.
Les design patterns ne sont pas une potion magique; en fait, ils ne sont mme pas une potion du tout!
Comme vous le savez, les patterns sont des solutions gnrales des problmes rcurrents. Ils prsentent galement lavantage davoir t tests par des myriades de dveloppeurs. Quand vous constatez quil vous en faut un, vous pouvez donc dormir sur vos deux oreilles en sachant que de nombreux informaticiens vous ont prcd et ont rsolu le problme en appliquant des techniques similaires. Mais les patterns ne sont pas une potion magique. Vous ne pouvez pas en insrer un, compiler et aller la pche. Pour bien utiliser les patterns, vous devez galement rflchir aux consquences quils auront sur le reste de votre conception.
semble convenir, vrifiez que ses consquences sont supportables et tudiez son effet sur le reste de votre conception. Si tout a lair correct, allez-y! Une seule situation impose de prfrer un pattern une solution plus simple, mme si vous savez quelle peut fonctionner: celle o vous vous attendez ce que certains aspects de votre systme varient. Comme nous lavons vu, lidentification de points de variation est gnralement un bon indicateur de la ncessit dun pattern. Vrifiez simplement que vous ajoutez des patterns pour traiter des changements probables qui sont susceptibles de se produire, non des changements hypothtiques qui pourraient ventuellement survenir. Lintroduction de patterns nest pas rserve la phase de conception: ils sont galement utiles au moment de la refactorisation.
liminez ce qui nest pas vraiment ncessaire. Nayez pas peur de supprimer un design pattern de votre conception.
Personne ne dit jamais quand supprimer un pattern, comme si ctait un blasphme! Nous sommes entre adultes ici, non? Nous pouvons assumer. Alors, quand supprimer un pattern? Quand votre systme est devenu complexe et que la souplesse que vous aviez voulu prserver nest pas ncessaire. Autrement dit, quand une solution plus simple serait prfrable au pattern.
Concentrez-vous sur la conception, pas sur les patterns. Employez des patterns quand ils correspondent un besoin naturel. Si une solution plus simple peut fonctionner, adoptez-la.
595
Matre et Disciple...
Matre: Ta formation initiale est presque termine, Petit scarabe. Quels sont tes projets? Disciple: Dabord, je vais aller Disneyland! Et puis je vais crire des tas de programmes avec des patterns! Matre: H, attends une minute. Inutile de sortir lartillerie lourde si tu nen as pas besoin. Disciple: Que voulez vous dire, Matre? Maintenant que jai appris les design patterns, est-ce que je ne dois pas les utiliser pour obtenir le maximum de puissance, de souplesse et de maintenabilit? Matre: Non. Les patterns sont un outil, et on ne doit utiliser un outil que lorsquon en a besoin. Tu as aussi pass beaucoup de temps apprendre les principes de conception. Pars toujours de ces principes, et cris toujours le code le plus simple possible pourvu quil remplisse sa fonction. Mais si la ncessit dun pattern se fait jour, emploie-le. Disciple: Alors, je ne dois pas construire mes conceptions sur des patterns? Matre: Cela ne doit pas tre ton objectif quand tu entames une conception. Laisse les patterns merger naturellement mesure que celle-ci progresse. Disciple: Si les patterns sont tellement gniaux, pourquoi tant de circonspection? Matre: Les patterns peuvent introduire de la complexit et nous prfrons toujours viter la complexit lorsquelle est inutile. Mais les patterns sont puissants quand on les utilise bon escient. Comme tu le sais, les patterns sont des solutions prouves issues de lexprience et lon peut les utiliser pour viter des erreurs courantes. Ils constituent galement un vocabulaire partag qui nous permet de communiquer notre conception dautres dveloppeurs. Disciple: Mais comment sait-on quil est judicieux dintroduire un pattern? Matre: Nintroduis un pattern que lorsque tu es sr quil est ncessaire pour rsoudre un problme dans ta conception ou pour faire face un changement futur dans les exigences de ton application. Disciple: Je crois que mon apprentissage va se poursuivre, mme si je comprends dj beaucoup de patterns. Matre: Oui, Scarabe. Apprendre grer la complexit et le changement dans le domaine logiciel est une poursuite sans fin. Mais maintenant que tu connais un bel assortiment de patterns, le temps est venu de les appliquer et de continuer en apprendre dautres. Disciple: Attendez une minute, vous voulez dire que je ne les connais pas TOUS? Matre: Scarabe Tu as appris les patterns fondamentaux, mais tu vas dcouvrir quil en existe beaucoup plus, notamment des patterns qui sappliquent uniquement des domaines spcialiss tels que les systmes concurrents et les systmes dentreprise. Mais maintenant que tu connais les bases, tu es prt les apprendre!
596
Chapitre 13
ESPRIT NEUF
Le dbutant voit des patterns partout. Cest bien: il pratique les patterns et accumule de lexprience. Le dbutant pense galement : Plus ma conception contiendra de patterns, meilleure elle sera. Il apprendra bientt quil nen est pas ainsi et que toute conception doit tre aussi simple que possible. La complexit et les patterns ne sont utiles que lorsquil existe un rel problme dextensibilit.
mesure que son apprentissage progresse, le dveloppeur commence voir quand un pattern est ncessaire ou non. Il continue essayer de faire entrer trop de patterns carrs dans des trous ronds, mais il commence galement se rendre compte que lon peut adapter les patterns pour grer des situations dans lesquelles les patterns canoniques ne conviennent pas.
STADE INTERMDIAIRE
ESPRIT ZEN
Lesprit Zen sait quand un pattern sapplique naturellement. Il nest pas obsd par lemploi des patterns mais recherche des solutions simples qui rsolvent au mieux le problme. Il pense en termes de principes objets et pse leurs avantages et leurs inconvnients. Quand un pattern semble simposer naturellement, lesprit Zen lapplique en sachant quil devra peut-tre ladapter. Il voit galement les relations quil entretient avec des patterns similaires et comprend les subtilits des diffrences dintention entre patterns apparents. Lesprit Zen est aussi un esprit neuf il ne laisse pas tout ce savoir sur les patterns exercer une influence exagre sur ses dcisions de conception. vous tes ici
597
ATTENTION: Labus de design patterns peut exagrment labor. Recherchez toujours la aboutir un code solution la plus simple et nintroduisez de patterns que lorsque le besoin sen fait ressentir.
Attendez une minute! Jai lu tout ce livre sur les patterns et maintenant vous me dites de ne PAS les utiliser?
Bien sr que nous voulons que vous utilisiez des design patterns!
Mais nous voulons encore plus que vous soyez un bon concepteur OO. Lorsquun problme de conception demande un pattern, vous tirez parti dune solution qui a t prouve par quantit de dveloppeurs. De plus, cette solution est bien documente et sera reconnue par vos pairs (vous savez toute cette histoire de vocabulaire partag). Toutefois, lapplication de design patterns peut prsenter un inconvnient. Les patterns introduisent souvent des classes et des objets supplmentaires, ce qui peut rendre vos conceptions plus complexes. Ils peuvent galement obliger crer des couches additionnelles, ce qui naugmente pas la complexit mais entrane des problmes defficacit. De plus, lemploi dun design pattern peut parfois relever du surarmement. Vous pouvez souvent revenir vos principes de conception et trouver une solution plus simple au mme problme. Si cela se produit, ne rsistez pas: choisissez la solution simple. Mais nous ne voulons pas vous dcourager. Si un design pattern savre tre le bon outil, les avantages seront lgion. 598
Chapitre 13
599
600
Chapitre 13
La Bande des quatre a lanc le mouvement des patterns logiciels, mais de nombreuses autres personnes ont apport une contribution significative, notamment Ward Cunningham, Kent Beck, Yann Coplien, Grady Booch, Bruce Anderson, Richard Gabriel, Doug Lea, Peter Coad et Doug Schmidt, pour nen citer que quelques-unes.
Ne vous emballez pas et recherchez la simplicit. Si vous trouvez une solution plus simple que lemploi dun pattern, nhsitez pas!
Richard Helm
John Vlissides
Ralph Johnson
Les patterns sont des outils, non des rgles. Rien nempche de les modifier et de les adapter votre problme.
Erich Gamma
601
u n Patterns ont re ig es D de s ur te au s Les ux de Bande degl ais, le surnom affectue an en r Fou of g an G ( re quat r, do lexpression de ge r ab ur po oF G ou Cest Christopher Alexander qui a patterns GoF)... invent la notion de patterns, qui a inspir lapplication de solutions similaires au dveloppement logiciel.
602
Chapitre 13
Le Hillside Group encourage les pratiques de programmation et de conception communes et centralise des ressources sur les patterns. Le site contient de nombreuses informations sur ces ressources, notamment les livres, les articles, les listes de diffusion et les outils.
http://hillside.net/patterns
Confrences et ateliers Et si vous voulez rencontrer en direct la communaut des utilisateurs de patterns, il existe quantit de confrences et dateliers. Le site de Hillside en maintient une liste complte. Consultez au moins la page de lOOPSLA, la confrence de lACM sur les systmes, les langages et les applications orients objet.
603
Les Patterns darchitecture servent crer larchitecture vivante des btiments, des villes et des cits. Cest de l que les patterns sont originaires.
Habitat: rencontr dans les constructions que vous aimez visiter, regarder et habiter.
des Habitat: vue dans le voisinage des s, che architectures trois cou systmes client-serveur et des applications web.
Les Patterns dapplication permettent de crer des architectures de systme. Nombre darchitecture en couches entrent dans cette catgorie...
Note de terrain: MVC est connu pour passer pour un pattern dapplication.
Les Patterns de domaine concernent des problmes spcifiques un domaine, comme les systmes concurrents ou les systmes temps-rel.
604
Chapitre 13
Les Patterns de processus mtier dcrivent les interactions entre les entreprises, les clients et les donnes, et sont applicables des problmes tels que comment prendre et communiquer efficacement des dcisions.
Les Patterns dorganisation dcrivent les structures et les pratiques des organisations humaines. Jusquici, les efforts se sont concentrs sur les organisations qui produisent du logiciel ou du support.
Les Patterns de conception dinterfaces utilisateur traitent des problmes de la conception de logiciels interactifs.
605
anti-patterns
Un Anti-Pattern vous dit comment partir dun problme et parvenir une MAUVAISE solution.
Vous vous demandez probablement: Pourquoi diable quelquun voudrait-il gaspiller son temps documenter de mauvaises solutions?. Vous pouvez voir les choses ainsi: sil existe une mauvaise solution rcurrente un problme courant, le fait de la documenter peut empcher dautres dveloppeurs de commettre la mme erreur. Aprs tout, viter de mauvaises solutions peut-tre tout aussi productif que den trouver de bonnes! Voyons quels sont les lments dun anti-pattern: Un anti-pattern vous dit pourquoi une mauvaise solution est attrayante. Regardons les choses en face, personne ne choisirait une mauvaise solution si elle ne prsentait pas dabord un aspect sduisant. Lune des fonctions les plus importantes de lanti-pattern consiste vous mettre en garde contre cet aspect sduisant. Un anti-pattern vous dit pourquoi cette solution est mauvaise long terme. Pour savoir pourquoi cest un antipattern, vous devez dabord comprendre en quoi il aura un effet ngatif en aval. Lanti-pattern dcrit les ennuis que vous allez vous attirer en adoptant cette solution. Un anti-pattern suggre dautres patterns applicables pouvant fournir de meilleures solutions. Pour tre vraiment utile, un anti-pattern doit vous montrer la bonne direction. Il doit indiquer dautres possibilits qui peuvent permettre de dboucher sur de bonnes solutions. Jetons un coup dil sur un anti-pattern.
Un anti-pattern ressemble toujours une bonne solution, ladite solution savrant mauvaise lorsquelle est applique. En documentant les antipatterns, nous aidons les autres dveloppeurs reconnatre les mauvaises solutions avant de commencer les mettre en uvre. linstar des patterns, il existe de nombreux types danti-patterns: anti-patterns de dveloppement, OO, organisationnels, spcifiques un domaine, etc.
606
Chapitre 13
Anti-Pattern
Nom: Marteau dor Problme: Vous devez choisir des technologies pour un dveloppement et vous croyez quune technologie et une seule doit dominer larchitecture. Contexte: Vous devez dvelopper un nouveau systme ou un logiciel qui ne colle pas avec la technologie dont lquipe de dveloppement a lhabitude. Forces: Lquipe de dveloppement est attache la technologie quelle connat. Lquipe de dveloppement connat mal les autres technologies. Les technologies mal connues sont considres comme risques. La planification et les estimations sont facilites si lon emploie la technologie dont lquipe est familire.
Le problme et son contexte, exactement comme dans une description de design pattern.
Explique lattrait de la solution
Solution suppose: Choisir la technologie familire malgr tout. Lappliquer de manire obsessionnelle tous les problmes, y compris dans les cas o elle est de toute vidence inadapte. Solution rvise: Permettre aux dveloppeurs dacqurir des connaissances en organisant des formations et des groupes de travail o ils rencontreront de nouvelles solutions. Exemples: Entreprises lectroniques qui continuent utiliser leurs propres systmes de mise en cache maison alors que des solutions open source sont disponibles.
607
POINTS DIMPACT
ce qui varie. Encapsulez lhritage. apsulation nc le ez r f Pr non des interfaces, es d ez m am Progr ions. implmentat aiblement de coupler f us vo z ce . nt Effor ui interagisse les objets q ouvertes oivent tre s la d s se as cl Les mais ferme lextensionn. modificatio ions. Ne . es abstract Dpendez d s des classes concrtes dpendez pa . u vos amis Ne parlez q vous lez pas, nous Ne nous appe appellerons. seule avoir quune t oi d ne se Une clas anger. raison de ch
Principes OO
Bases de lOO
Abstraction n Encapsulatio me Polymorphis Hritage
Le moment est venu pour vous de dcouvrir dautres patterns par vous-mme. Il y aura de nombreux patterns spcifiques un domaine que nous navons pas mentionns et des patterns fondamentaux que nous navons pas abords. Vous avez galement vos propres patterns crer.
uu f ee haca t q d st d ch n dF e cth e t ac u n r u a aly -rM to o nsatterns! r q h a up st e . it e t ie r t e ss s y r A t un ee f lt e ll s la t q o v a so a t b tg t c e e le c r s aA ,a s r o s ic la r a e su a e t t t ie p m s o y r a p su c t c il e a o a p b r t Str t t s la r n la m je je c r a c s o e p a y s. a b O n b c p su su t b r d je o e t m f o n le n p t e b o s la c b E a n g n t r o s, in o c c a a e c su o e in e e n n p V p g F m t je g u a E a w n h an n b da le d D c in t a o a o n n w ng.es. _ it e it t h r it b o as usu n a b ac E c r ll e oo lo r ta llle a e m y ch u r g r y le e a lgeopr c o c o o t cc t g ,lin m a g n y t h f t f et p in rA e t oa dad t in l d e sit o g e a a e d e d S C a t f n d w r ie in d t n st e a y c vi st e s e e it a le f o ss A x b t r c _ t il s a r r m t r a c la o y b e le n e p s h F r c f S g r r si t le je le n it r y n e d n h o in P e r b ib a t n o o t o e e t h ic x a e in r h sp c h lg t in n le e e e e t t s _ n e w x t f h r c t r c e l , u d n _ it n e t i e _e a u u es c ,e ngts_____ ee t je ncpid de hen _ q oa t p a b it o st t je _ r co e f o sd h d e b in e e ,d _ d p t vi w d je in if e g m f a t u n _ h e b n s. n r d r s e s r r n n _ s o if je e o e o a n it e d li o t p o b _ e p h n ss n ss vi o le d c t s _ a a a h e la la a x ie _ e o h w c d t o c s e d it r _ e o t d w g n a b s la e ie t w b r _ h e a t t u r t su o t . t li r a if s r ee nc c f sts, aannd t it toa e e ob nr p ga e M ec m le o ec n StrD n in li t m o yt e e a rp ss lla r e oa d ss iz ilc a e d________ t t n ir w s e c e iz cm a t r t p a e n t su F c s, t ee eeq f g o e a je d u_ st .h in a e___ rrn o p n b e in m oe u r y a t t pe q r ie e e ia if iv _ ar o h g c t p t _h n a vard T lo _ g n sp r r lo e io st e o _ t r in _ lt o ia _ a u t e e _ n u . u y _ s, a e q t ll u n a st st q in e t s, il ee a nns. uss m s. ye.r q f oc lut .st uic ee ttio tn liet r q io d uis a af la n r poss _________ ss c raa io m la o t o pee op c le a ooa u sr le n abb r d e uun d t r n ________ o t t _ a t _ P r p _ o p _ s. p su e p ss su subcla bine deux ou
Compos com Un Pattern ns pour rsoudre un ter plusieurs pat l ou rcurrent. ra n g problme
Construisez un vocabulaire
commun avec votre quipe. Cest lun des principaux avantages de lutilisation des patterns.
608
Chapitre 13
609
Pattern
Dcorateur tat Itrateur Faade Stratgie Proxy Fabrication Adaptateur Observateur Patron de mthode Composite Singleton Fabrique Abstraite Commande
610
Chapitre 13
Enveloppe un objet et fournit une interface diffrente pour y accder Les sous-classes dcident de la faon dimplmenter les tapes dun algorithme Les sous-classes dcident quelles sont les classes concrtes crer Garantit quun objet et un seul est cr Encapsule des comportements interchangeables et utilise la dlgation pour dcider lequel utiliser Les clients traitent les collections dobjets et les ^ manire objets individuels de la mme Encapsule des comportements bass sur des tats et utilise la dlgation pour permuter ces comportements Fournit un moyen de parcourir une collection dobjets sans exposer son implmentation Simplifie linterface dun ensemble de classes Enveloppe un objet pour fournir un nouveau comportement Permet un client de crer des familles dobjets sans spcifier leurs classes concrtes. Permet de notifier des changements dtat des objets Enveloppe un objet et en contrle laccs Encapsule une requte sous forme dobjet
Description
14
Annexe
annexe 611
le pattern pont
Pont
Utilisez le pattern Pont pour faire varier non seulement vos implmentations, mais aussi vos abstractions.
Un scnario
Imaginez que vous allez rvolutionner lextrme cocooning. Vous tes en train dcrire le code dune nouvelle tlcommande de tlviseur ergonomique et conviviale. Vous savez dj que vous allez devoir appliquer de bonnes techniquesOO, car si la commande est base sur la mme abstraction, il y aura de nombreuses implmentations une pour chaque modle de poste.
it tre Voici labstraction. Ce pourra straite. ab sse une interface ou une cla
Telecommande
allumer() eteindre() setChaine() // autres mthodes
CommandeRCA
CommandeSony
allumer() eteindre() setChaine() // autres mthodes
Votre dilemme
Vous savez que linterface utilisateur de la tlcommande ne sera pas parfaite du premier coup. En fait, vous vous attendez devoir affiner le produit plusieurs fois, mesure que vous recueillerez des donnes sur sa facilit dutilisation. Votre dilemme rside donc dans le fait que les tlcommandes vont changer et les tlviseurs aussi. Vous avez dj abstrait linterface utilisateur pour pouvoir faire varier limplmentation en fonction des diffrents postes que vos utilisateurs possderont. Mais vous allez galement devoir faire varier labstraction parce quelle va changer au fur et mesure que vous allez amliorer la tlcommande en tenant compte du feedback des utilisateurs. Comment donc allez-vous crer une conceptionOO qui vous permette de faire varier limplmentation et labstraction? 612
annexe
reglerChaine(chaine); }
Cette conception ne nous les permet de faire varier que implmentations, pas linterface utilisateur.
patterns restants
TV
allumer() eteindre() reglerChaine() // autres mthodes
implementeur.reglerChaine(chaine);
TelecommandeConcrete
stationCourante
straction Toutes les mthodes de lab termes les ns da sont mises en uvre . de limplmentation
setChaine(stationCourante + 1);
RCA
allumer() eteindre() reglerChaine() // autres mthodes
Sony
allumer() eteindre() reglerChaine() // autres mthodes
Les sous-classes concrtes sont implmentes dans les termes de labstraction, non de limplmentation.
Vous disposez maintenant de deux hirarchies spares: lune pour les tlcommandes et lautre pour les implmentations de tlviseurs spcifiques une plate-forme. Le pont vous permet de faire varier indpendamment chaque ct des deux hirarchies.
Avantages
Emplois et inconvnients
613
le pattern monteur
Monteur
Utilisez le pattern Monteur pour encapsuler la construction dun produit et permettre de le construire par tapes.
Un scnario
On vient de vous confier le dveloppement dun logiciel de planification de sjours pour Patternsland, un nouveau parc thme rcemment construit aux portes dObjectville. Les htes du parc peuvent choisir un htel et diffrents types de tickets dadmission, rserver des tables au restaurant et mme des places des vnements spciaux. Pour crire ce programme, vous devez pouvoir crer des structures telles que celles-ci: Chaque sjour est planifi sur
un
Sjour
Jo
u r n e U
urneTr
ois
Jo
urneDeu
Jo
Htel
Tic
kets
R epa s
Htel
Tickets
n em e
Repas
Tic
kets
Htel
v nemen
Dner
Pa tte s on rn
Dner
Ci rqu P e des
ne peut avoir u Chaque jour quelconque de combinaison dhtel, de tickets, rservations dvnements. de repas et
Il vous faut une conception souple
Le planning de chaque hte peut comprendre un nombre de journes et des types dactivits variables. Par exemple, un rsident local naura pas besoin dhtel mais pourra dsirer rserver pour dner et assister des vnements spciaux. Un autre arrivera en avion Objectville et aura besoin de rserver un htel, des repas et des tickets dentre. Il vous faut donc une structure de donnes souple et capable de reprsenter les plannings des htes avec toutes leurs variantes. Vous devez galement observer une squence dtapes potentiellement complexes pour crer le planning. Quel moyen avez-vous de crer cette structure complexe sans la mlanger avec les tapes ncessaires pour la crer? 614
annexe
att erns
Ice
patterns restants
Client construirePlanning()
monteur
MonteurAbstrait construireJournee() ajouterHotel() ajouterReservation() ajouterEvenement() ajouterTickets() getPlanningSejour()
monteur.construireJournee(date); monteur.ajouterHotel(date, La Grande Faade); monteur.ajouterTickets(Patterns on Ice); // planifier le reste du sjour Planning votrePlanning = monteur.getPlanningSejour(); MonteurSejour sejour construireJournee() ajouterHotel() ajouterReservation() ajouterEvenement() ajouterTickets() getPlanningSejour()
t Le monteur concre els r s uit cre les prod e un ns da et les stocke ur jo s structure de composite.
planning nteur de crer le pe lle la Le Client dit au mo ap re dtapes, puis cuprer en un certain nomb ejour() pour r mthode getPlanningS lobjet complet.
Avantages
Emplois et inconvnients
615
Chane de responsabilit
Utilisez le pattern Chane de responsabilit quand vous voulez donner plus dun objet une chance de traiter une requte.
Un scnario
Distribon reoit plus de courrier lectronique quil ne peut en grer depuis la sortie de leurs distributeurs de bonbons quips de Java. Selon leur propre analyse, ils reoivent quatre types de courrier: des lettres de fans qui adorent leur nouveau jeu, des rclamations de parents dont les gosses sont accros au jeu et des demandes de mise en place de nouveaux distributeurs. Ils reoivent galement pas mal de spams. Tout le courrier des admirateurs est transmis directement au P.D.G., les plaintes au service juridique et les demandes de nouveaux distributeurs au service dveloppement. Les messages indsirables doivent tre dtruits.
Votre tche
Distribon a dj crit des dtecteurs intelligents qui peuvent distinguer une lettre de fan dun spam, dune rclamation ou dune demande, mais ils ont besoin de vous pour crer une conception qui permette aux dtecteurs de grer le courrier entrant.
Vous devez nous aider trier tout ce courrier que nous recevons depuis la sortie des distributeurs Java.
616
annexe
patterns restants
Handler successeur
Chaque objet de la de chane joue le rle un gestionnaire et a il peut objet successeur. S il le traiter la requte,ansmet fait, sinon il la tr son successeur.
traiterRequete()
GestionnaireSpams traiterRequete()
GestionnaireFans traiterRequete()
GestionnairePlaintes traiterRequete()
GestionnaireDemandes traiterRequete()
Quand un message est reu, il est transmis au premier gestionnaire: GestionnaireSpams. Si GestionnaireSpams ne peut pas traiter la requte, il la transmet GestionnaireFans et ainsi de suite...
Le message nest pas trait sil tombe de la fin de la chane mais vous pouvez toujours implmenter un autre gestionnaire qui intercepte tout.
Gestionnaire Demandes
Avantages
Emplois et inconvnients
Dcouple lmetteur de la requte de ses rcepteurs. Simplifie votre objet, car il na pas besoin de
connatre la structure de la chane ni de conserver des rfrences directes ses membres. Permet dajouter ou de supprimer dynamiquement des responsabilits en changeant les membres ou lordre de la chane.
617
le pattern poids-mouche
Poids-mouche
Utilisez le pattern Poids-mouche quand une instance dune classe peut servir fournir plusieurs instances virtuelles.
Un scnario
Vous avez crit une super application de conception paysagre qui permet notamment dajouter des arbres autour dune maison. Dans cette application, les arbres ne font pas grand-chose: ils ont un emplacement X-Y et savent se dessiner eux-mmes dynamiquement en fonction de leur ge. Le problme est quun utilisateur peut vouloir dimportantes quantits darbres dans lun de ses plans paysagers. Celui-ci pourrait ressembler au schma ci-dessous:
Arbre
patterns restants
Tout ltat, pour TOUS vos objets Arbre virtuels, est stock dans ce tableau deux dimensions.
Avantages
Emplois et inconvnients
619
le pattern interprte
Interprte
Utilisez le pattern pour construire un interprteur pour un langage.
Un scnario
Vous souvenez-vous du Simulateur de mare aux canards? Vous avez lintuition quil pourrait devenir un outil pdagogique pour apprendre aux enfants programmer. Grce au simulateur, chaque enfant peut contrler un canard au moyen dun langage simple. Voici un exemple du langage:
ncessite Interprte Le pattern connaissance des ine s ne les une certa lles. Si vou d e rm fo s e an grammair s, lisez qu urs is tudie avez jama e: vous aurez toujo it su la e mm pattern. lesprit du
le canard
Vous puisez maintenant dans vos souvenirs de vos cours dintroduction la programmation et vous crivez la grammaire:
e expressionndes n u st e e m m a Un progr e squences de comma compose d itions (les instructions et de rpt ). tantque Une squence est un ensemble dexpressions expression ::= <commande> | <sequence> | <repetition> spares par des sequence ::= <expression> ; <expression> points-virgules. commande ::= droite | voler | cancaner repetition ::= tantque ( <variable> )<expression> Nous avons trois variable ::= [A-Z,a-z]+ commandes: droite, voler et cancaner. Une instruction tan tque est simplement comp os e variable conditionnelle dune et dune expression.
Et maintenant?
Vous avez une grammaire. Il ne vous manque plus quun moyen de reprsenter et dinterprter les phrases afin que vos lves voient les effets de leur programmation sur les canards virtuels.
620
annexe
patterns restants
interpreter(contexte)
Variable
CommandeCancaner
CommandeDroite
CommandeVoler
interpreter(contexte)
interpreter(contexte)
interpreter(contexte)
interpreter(contexte)
Pour interprter le langage, appeler la mthode interpreter() sur chaque type dexpression. Cette mthode reoit en argument un contexte qui contient le flot dentre que nous analysons, apparie lentre et lvalue.
Avantages
Emplois et inconvnients
621
le pattern mdiateur
Mdiateur
Utilisez le pattern Mdiateur pour centraliser le contrle et les communications complexes entre objets apparents.
Un scnario
Lo possde une maison entirement automatise laide de code Java grce aux bons services de MaisonsDuFutur. Tous ses appareils sont conus pour lui faciliter la vie. Quand Lo cesse de taper sur le bouton darrt de son rveil, ce dernier dit la cafetire de commencer faire le caf. Mais mme si Lo a la belle vie, lui et les autres clients ne cessent de rclamer de nouvelles fonctionnalits: pas de caf le week-end... arrter larrosage automatique si une averse est annonce... mettre le rveil plus tt les jours de ramassage des encombrants...
Reveil Cafetiere
Calendrier
Arrosage
Arrosage receptionEvenement() { verifierCalendrier() verifierTemperature() verifierPluie() // autres mthodes }
Le dilemme de MaisonsDuFutur
Il devient vraiment trs difficile de mmoriser dans quels objets se trouvent les rgles et comment les objets sont lis les uns aux autres. 622
annexe
patterns restants
Mdiateur en action...
Lajout dun Mdiateur au systme permet de simplifier grandement tous les appareils:
Quel soulagement de ne plus tre oblig de connatre toutes les rgles chipoteuses de ce Rveil!
Cafetire
Avant linsertion du Mdiateur, tous les appareils devaient se connatre... ils taient troitement coupls. Avec le Mdiateur en place, ces objets sont totalement dcoupls les uns des autres. Le Mdiateur contient toute la logique de contrle pour lensemble du systme. Quand un appareil existant a besoin dune nouvelle rgle ou quand un nouvel appareil est ajout au systme, vous savez que toute la logique ncessaire sera ajoute au Mdiateur.
Mdiateur
Calendrier
Arrosage
Avantages
Emplois et inconvnients
623
le pattern mmento
Memento
Utilisez le pattern Mmento quand vous avez besoin de restaurer lun des tats prcdents dun objet, par exemple si lutilisateur demande une annulation.
Un scnario
Votre jeu de rles interactif rencontre un immense succs et a cr des lgions de fans qui essaient tous datteindre le lgendaire niveau13. Plus les utilisateurs terminent des niveaux de jeu de difficult croissante, plus les chances de rencontrer une situation qui mettra fin au jeu augmentent. Les joueurs qui ont pass des jours et des jours parvenir un niveau avanc sont bien lgitimement froisss quand leur personnage se casse la pipe et quils doivent tout recommencer. Ils rclament cor et cri une commande de sauvegarde qui leur permette de mmoriser leur progression et de rcuprer la plus grande partie de leurs efforts quand leur personnage est dloyalement effac. La fonction de sauvegarde doit tre conue afin de permettre un joueur ressuscit de reprendre le jeu au dernier niveau termin avec succs.
Faites juste attention la faon dont vous sauvegardez ltat du jeu. Cest un programme plutt compliqu et je nai pas envie que nimporte qui vienne toucher mon code et le bousiller.
624
annexe
patterns restants
Mmento au travail
Le Mmento a deux objectifs:
Sauvegarder un tat important dun objet cl dun systme. Maintenir lencapsulation de lobjet cl.
Pour respecter le principe de responsabilit unique, il est galement judicieux de mmoriser ltat que vous sauvegardez dans un objet spar. Cest cet objet spar qui contient ltat qui est lobjet Mmento.
MementoJeu etatJeuSauvegarde
Client // quand un nouveau niveau est atteint Object sauve = (Object) ojp.getEtatCourant(); // quand la restauration est demande ojp.restaurerEtat(sauve);
ObjetJeuPrincipal etatJeu Object getEtatCourant() { // recueillir ltat return(etatJeu); } restaurerEtat(Object etatSauve) { // restaurer ltat } // reste du code
Si cette implmentation nest pas terriblement sophistique, remarquez que le Client na pas accs aux donnes du Mmento..
Avantages
Emplois et inconvnients
Le Mmento est utilis pour sauvegarder un tat. Linconvnient de lemploi de Mmento est que la
sauvegarde et la restauration dun tat peuvent tre trs longues. Dans les systmes Java, envisagez demployer la srialisation pour sauvegarder ltat dun systme.
625
le pattern prototype
Prototype
Utilisez le pattern Prototype quand la cration dune instance dune classe donne est coteuse ou complique.
Un scnario
Votre jeu de rles interactif est dot dun insatiable apptit pour les cratures monstrueuses. mesure que vos hros poursuivent leurs prgrinations dans un paysage cr dynamiquement, ils rencontrent une chane sans fin dennemis quils doivent anantir. Vous aimeriez que les caractristiques de vos monstres voluent avec les changements du paysage. Ce ne serait pas trs logique de laisser des dragons ails suivre vos personnages dans les royaumes sous-marins. Enfin, vous dsirez permettre aux joueurs avancs de crer leurs propres monstres personnaliss.
Ae! Le simple fait de crer tous ces diffrents types dinstances de monstres commence devenir problmatique... Placer toutes sortes de dtails sur les tats dans les constructeurs semble plutt nuire la cohsion. Ce serait gnial davoir un seul endroit o encapsuler tous les dtails des instanciations...
Ce serait beaucoup plus sain si nous pouvions dcoupler le code qui gre les dtails de la cration des monstres de celui qui doit crer les instances la vole.
626
annexe
patterns restants
Prototype la rescousse
Le pattern Prototype permet de crer de nouvelles instances en copiant des instances existantes. (En Java, cela signifie gnralement lemploi de la mthode clone(), ou de la dsrialisation quand on a besoin de copies en profondeur.) Un aspect essentiel de ce pattern est que le code client peut crer de nouvelles instances sans savoir quelle classe spcifique est instancie.
MonstreBienConnu <<interface>> Monstre
MonstreGnrDynamiquement
veau Le client a besoin dun nouuat ion sit la ri monstre approp l que pas ra sau actuelle. (Il ne .) genre de monstre il va obtenir
Avantages
Emplois et inconvnients
627
le pattern visiteur
Visiteur
Utilisez le pattern Visiteur quand vous voulez ajouter des capacits un ensemble composite dobjets et que lencapsulation nest pas importante.
Un scnario
Les clients qui frquentent la Cafeteria dObjectville et la Crperie dObjectville se proccupent de plus en plus de leur sant. Ils demandent des informations nutritionnelles avant de commander leur repas. Comme les deux tablissements acceptent de prendre des commandes spciales, certains clients rclament mme ces informations ingrdient par ingrdient.
Menu
// nouvelles mthodes getNoteGlobale getCalories getProteines getGlucides // nouvelles mthodes getNoteGlobale getCalories getProteines getGlucides
Plat
Plat
Ingredient
Ingredient
628
annexe
patterns restants
Voici le Visiteur
Le Visiteur doit parcourir chaque lment du Composite: cette fonctionnalit se trouve dans un objet Navigateur. Le Visiteur est guid par le Navigateur et recueille ltat de tous les objets du Composite. Une fois ltat recueilli, le Client peut demander au Visiteur dexcuter diffrentes oprations sur celui-ci. Quand une nouvelle fonctionnalit est requise, seul le Visiteur doit tre modifi.
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.
eler Le Visiteur doit pouvoir app et s, sse getEtat() entre les cla ut er ajo cest l que vous pouvez le client les nouvelles mthodes que utilisera.
te() getSta
Les classes du Composite nont rien dautre faire que dajouter une mthode getEtat() (et ne pas se soucier de sexposer ellesmmes).
Menu
Visiteur
Plat
e() tat tS ge
Client / Navigateur
Ingredient
Ingredient
Inconvnients
Lencapsulation des classes du Composite est brise. Comme une fonction de navigation est implique, les
modifications de la structure du Composite sont plus difficiles.
629
Index
h
g
Bote outils 32, 74, 105, 162, 186, 230, 270, 311, 380, 423, 491, 560, 608 Bonchoco, SARL 175
A
Adaptateur adaptateurs dobjet 244 adaptateurs de classe 244 avantages 242 combinaisons de patterns 504 dfinition 243 entre Enumeration et Iterator 248 exercice 251 explication 241 face--face 247, 252253 introduction 237 Alexander, Christopher 602 annihiler le mal 606 annulation doprations 216, 227 Anti-patterns 606607 A-UN (relation) 23 vos crayons 5, 42, 54, 61, 94, 97, 99, 124, 137, 148, 176, 183, 205, 225, 242, 268, 284, 322, 342, 396, 400, 406, 409, 421, 483, 511, 518, 520, 589
C
Cafeteria dObjectville 26, 197, 316, 628 catalogues de patterns 581, 583, 585 Chane de responsabilit 616617 changement 339 anticipation du 14 constante du dveloppement logiciel 8 identification 53 Choco BN 526 cohsion 339340 Comdie express 48, 478 Commande annulation de commandes 216, 220, 227 chargement de lInvocateur 201 dfinition 206207 diagramme de classes 207 introduction 196 journalisation des requtes 229 macro-commandes 224 mise en file des requtes 228 objet de commande 203 Objet nul 214 Composite comportement composite 363 comportement par dfaut 360
index 631
B
Bande des quatre 583, 601 Gamma, Erich 601 Helm, Richard 601 Johnson, Ralph 601 Vlissides, John 601
C-D
Composite (suite) dfinition 356 diagramme de classes 358 et Itrateur 368 interview 376377 patterns combins 513 scurit 367 scurit vs. transparence 515 transparence 367, 375 composition 23, 85, 93, 247, 309 vs. hritage 23, 75 contre-indications des patterns596598 contrle daccs 460. Voir aussi Proxy conversation dans un box 55, 93, 195, 208, 387, 397, 433, 583584 couplage faible 53 couplage fort 53 cration dobjets 134 Crperie dObjectville 316, 628
catgories 589, 592593 Chane de responsabilit 616617 Commande 206 Composite 356 Dcorateur 91 dcouvrez vos propres patterns 586587 dfinition 579, 581 tat 410 Fabrication 134 Fabrique abstraite 156 Fabrique simple 114 Faade 264 Interprte 620621 Itrateur 336 Mdiateur 622623 Mmento 624625 Monteur 614615 Objet nul 214 Observateur 51 organisation 589 Patron de mthode 289 patterns dobjets 591 patterns de classe 591 Poids-mouche 618619 Pont 612613 Prototype 626627 Proxy 460 Singleton 177 Stratgie 24 utilisation 29 Visiteur 628629 vs. bibliothques 29 vs. frameworks 29 diffusion-souscription 45 Distribon, Inc. 386 Distributeur de bonbons 431
D
Dcorateur conversation dans un box 93 dfinition 91 diagramme de classes 91 et E/S Java 100101 et Proxy 472473 face--face 252253 inconvnients 101, 104 interview 104 introduction 88 pattern structural 591 patterns combins 506 Design Patterns Adaptateur 243 avantages 599
632
index
index
E
Elvis 526 encapsulation de la construction des objets 614615 de la cration des objets 114, 136 des algorithmes 286, 289 des appels de mthode 206 des comportements 11 des tats 399 des itrations 323 des requtes 206 encapsulez ce qui varie 89, 75, 136, 397, 612 envelopper des objets 88, 242, 252, 260, 473, 508 Voir aussi Adaptateur, Dcorateur, Faade, Proxy EST-UN (relation) 23 tat dfinition 410 diagramme de classes 410 et Stratgie 411, 418419 inconvnients 412, 417 introduction 398 partage dtat 412 explosion combinatoire 81
dfinition 264 diagramme de classes 264 et Loi de Dmter 269 introduction 258 face--face 62, 247, 252, 308, 418, 472473 famille de produits 145 Voir aussi Stratgie forces 582 Friedman, Dan 171
G
Gamma, Erich 601 glouglou 239 GoF. Voir Bande des quatre guide pour mieux vivre avec les Design Patterns 578
H
Helm, Richard 601 hritage et rutilisation 56 inconvnients 5 vs. composition 23, 75, 93 hirarchie tout-parties 356. Voir aussi Composite Hillside Group 603 Home Cinema 255 Hook. Voir mthode adaptateur
F
Fabrication 134 Voir aussi Patterns de fabrique Fabrique abstraite 156 Voir aussi Patterns de fabrique Fabrique simple 117 fabrique statique 115 Faade avantages 260
I
instanciation au dmarrage 177 la demande 177 interface 12 Interprte 620621
633
I-O
Interviews 104, 158, 174, 377378 inversion 141142 Itrateur avantages 330 dfinition 336 diagramme de classes 337 et collections 347349 et Composite 368 et Enumeration 338 et Hashtable 343, 348 exercice 327 for/in 349 introduction 325 itrateur externe 338 itrateur interne 338 itrateur nul 372 itration polymorphe 338 java.util.Iterator 332 jeu du frigo 350 suppression dobjets 332
Mdiateur 622623 Mmento 624625 Mentions honorables 117, 214 Mto Express, SA, 38 Mthode adaptateur 292, 295 Model2549 Voir aussi Modle-Vue-Contrleur et design patterns 557558 Modle-Vue-Contrleur Adaptateur 546 la loupe 530 chanson 526 code prt lemploi 564576 Composite 532, 559 et design patterns 532 et le Web 549 introduction 529 Mdiateur 559 Observateur 532 Stratgie 532, 545 Monteur 614615 mots-croiss 33, 76, 163, 187, 231, 271, 310, 378, 490 MVC. Voir Modle-Vue-Contrleur
J
jeu du frigo 69, 179, 245, 350 Johnson, Ralph 601
L
Loi de Dmter. Voir Ne parlez pas aux inconnus
N
Ne parlez pas aux inconnus 265268 inconvnients 267
M
machines tats 388389 Maisons de rve, SARL 192 matre et disciple 23, 30, 85, 136, 592, 596 Marteau dor 607
O
Objet nul 214, 372 Observable 64, 71 Observateur Comdie express 48 conversation dans un box 55
634
index
index
dans Swing 7273 dfinition 5152 diagramme de classes 52 face--face 62 introduction 44 jeu du frigo 69 modle pull 63 modle push 63 Patterns combins 516 relation un--plusieurs 5152 support par Java 64 OOPSLA 603
Observateur 516 patterns composs 500, 522 patterns dapplications 604 patterns dans le monde rel 299, 488489 patterns dinterfaces utilisateur 605 Patterns de fabrique Fabrication la loupe 125 avantages 135 dfinition 134 diagramme de classes 134 et Fabrique abstraite 160161 interview 158159 introduction 120, 131132 Fabrique abstraite dfinition 156 diagramme de classes 156157 et Fabrication 158159, 160161 interview 158159 introduction 153 patterns combins 508 Fabrique simple dfinition 117 introduction 114 patterns de processus mtier 605 patterns organisationnels 605 patterns spcifiques un domaine 604 penser en termes de patterns 594595 Petit Lispien 171 Pizzeria dObjectville 112 Poids-mouche 618619 point daccs globa177 points dimpact 32, 74, 105, 162, 186, 230, 270, 311, 380, 423, 491, 560, 608 Pont 612613
vous tes ici
P
Patron de mthode la loupe 290291 avantages 288 dfinition 289 diagramme de classes 289 et Applet 307 et java.util.Arrays 300 et Principe dHollywood 297 et Stratgie 305, 308309 et Swing 306 face--face 308309 Mthode adaptateur 292, 295 introduction 286 patterns architecturaux 604 Patterns combins 500 Adaptateur 504 Composite 513 Dcorateur 506 diagramme de classes 524 Fabrique abstraite 508
635
P-S
Portland Patterns Repository 603 potion magique 594 prfrez la composition lhritage 23, 75 principe dHollywood 296 et Principe dinversion des dpendances 298 principe dinversion des dpendances 139143 et principe dHollywood 298 principe de responsabilit unique 339 Voir aussi Principes de conception orients objet: une classe, une responsabilit principe Ouvert-Ferm 8687 principes dapprentissage Tte la premire xxix principes de conception orients objet 9, 3031 principe dinversion des dpendances 139143 couplez faiblement les objets qui interagissent 53 encapsulez ce qui varie 9, 111 ne parlez pas aux inconnus 265 prfrez la composition lhritage 23, 243, 397 Principe dHollywood 296 Principe Ouvert-Ferm 8687, 407 programmez pour une interface, non une implmentation 11, 243, 335 une classe, une responsabilit 185, 336, 339, 367 problmes de conception 25, 133, 279, 395, 468, 542 programmer pour une implmentation 12, 17, 71 programmer pour une interface 12 non une implmentation 11, 75 Prototype 626627 Proxy code prt lemploi494 dfinition 460 diagramme de classes 461 et Adaptateur 471 et Dcorateur 471, 472473 et RMI 486 exercice 482
face--face 472473 java.lang.reflect.Proxy 474 proxy dimage 464 Proxy de mise en cache 471 Proxy de protection 474, 477 Proxy distant 434 Proxy dynamique 474, 479, 486 Proxy virtuel 462 variantes 471 Zoo 488489
Q
Qui fait quoi? 202, 254, 298, 379, 422, 487, 588
R
refactorisation (refactoring) 354, 595 Remote Method Invocation. Voir RMI rencontres Objectville 475 rutilisation 13, 23, 85 RMI 436
S
Sexy ou non 475 Simulateur de canards 2, 500 Singleton la loupe 173 avantages 170, 184 dfinition 177 diagramme de classes 177 et multithread 180182 et ramasse-miettes 184 et variables globales 185 inconvnients 184
636
index
index
interview 174 verrouillage double vrification 182 souche (RMI) 440 squelette (RMI) 440 Starbuzz Coffee 80, 276 Stratgie 24 et tat 411, 418419 et Patron de mthode 308309 encapsulation du comportement 22 famille dalgorithmes 22 face--face 308
Visiteur 628629 Visualiseur de CD 463 Vlissides, John 601 vocabulaire partag 2628, 599600 Vue DJ 534
637
Colophon
h
g
index
Toutes les maquettes intrieures ont t conues par Eric Freeman, Elisabeth Freeman, Kathy Sierra et Bert Bates. Kathy et Bert ont cr le look & feel de la collection Tte la premire. Ldition franaise de cet ouvrage a t
traduite par Marie-Ccile Baland. Marie-Ccile Baland est une traductrice passionne. Elle a traduit de nombreux ouvrages de rfrence dans le domaine des langages objets, dont le clbre Java, Tte la premire. Sa culture ne se limite pas aux domaines de linformatique: elle impressionne aussi ses amis qui sont souvent, est-ce un hasard, des informaticiens, par ses connaissances en littrature, en histoire, en botanique ou... en cuisine. Son humour subtil, toujours prsent, vous aura rendu, nous lesprons, la lecture en franais de cet ouvrage agrable. Le code a t localis par Frdric Laurent et MarieCcile; la traduction a t relue par Frdric et Michel Beteta. Frdric Laurent est spcialiste des architectures objet et des technologies J2EE et XML. Il sintresse lexploitation multisupport de contenu (grce, notament, aux technologies XML) ainsi quaux informations smantiques (web smantique et mtadonnes). Il a par ailleurs co-traduit Java et XSLT, Java plus rapide, plus lger et XML en concentr. Michel Beteta, consultant en nouvelles technologies et architectures Java, est impliqu dans toutes sortes de prestations de conseil autour des infrastructures J2EE. Ses heures de relecture au son dune musique lectro (merci Mylo!) et devant environ une tonne de chips lont confort dans sa vision dune approche didactique, pragmatique et claire de lapprentissage des bonnes pratiques de programmation et qui se retrouve dans la collection Tte la premire. vous tes ici
639
Tte la premire
Institut
index
641
Distribon
Sans votre aide, la prochaine gnration ne connatra peut-tre jamais les joies de notre distributeur de bonbons. Aujourdhui, un code mal conu et totalement dpourvu de souplesse met nos distributeurs Java en danger. Distribon ne laissera pas cette catastrophe se produire. Nous nous sommes dvous une cause: vous aider amliorer vos comptences en Java et en conception OO, afin que vous puissiez nous aider dvelopper notre prochaine gnration de machines Distribon. Allons, les grilles-pain Java font tellement annes 90. Rendez-nous visite sur http://www.distribon.com.
Distribon, SARL.
Le monde dans lequel les distributeurs ne sont jamais vides
Au catalogue OReilly
Java Tte la premire
Kathy Sierra et Bert Bates ISBN 2-84177-276-4 1re dition, septembre 2004 656 pages > Guide dapprentissage pour dbutants.
Avec Java tte la premire, apprenez enfin Java autrement mais assurment ! Laissez images, histoires, exercices, nigmes et jeux vous emmener dans lunivers Java sans pourautant vous ennuyer. crit par deux clbres gourous Java, cet ouvrage aux dehors amusants nen demeure pas moins srieux. Alors, si programmer en Java vous tente, plongez tte la premire !
Eclipse
Steve Holzner ISBN 2-84177-264-0 1re dition, dcembre 2004 350 pages > Guide dapprentissage pour dbutants et expriments.
Embarquement immdiat pour Eclipse, lEDI qui fait fureur chez les programmeurs Java. Tous les aspects de la plateforme sont soigneusement dcrits. Des thmes aussi varis que le JDT, le plan de travail, lintgration avec Ant, le travail collaboratif, le dbogage, le dveloppement dapplication Struts... Impossible de tout numrer, tant le contenu est riche.
Enterprise JavaBeans
Richard Monson-Haefel ISBN 2-84177-218-7 3e dition, septembre 2002 586 pages > Guide dapprentissage pour dbutants et expriments.
laide de nombreux exemples, vous apprendrez ce que sont rellement les EJB, et comment en tirer le meilleur parti. Cet ouvrage insiste galement sur les principaux piges viter lors du dveloppement dapplications.
Introduction Java
Pat Niemeyer et Jonathan Knudsen ISBN 2-84177-234-9 2e dition, dcembre 2002 910 pages > Guide dapprentissage pour dbutants et expriments.
maill dexemples concrets, cet ouvrage permettra aux dbutants en Java, comme aux programmeurs issus dautres environnements, de se familiariser rapidement et efficacement avec Java.
www.oreilly.fr
Au catalogue OReilly
JDBC et Java Guide du programmeur
George Reese ISBN 2-84177-136-9, 2e dition, mai 2001 326 pages > Guide dapprentissage pour expriments et spcialistes.
Lauteur montre comment transmettre des requtes SQL un gestionnaire de base de donnes via JDBC, il explique lemploi des procdures stockes, etc. Cet ouvrage est un vritable guide de programmation dapplications orientes bases de donnes.
Java en action
Ian F. Darwin ISBN : 2-84177-203-9 1re dition, avril 2002 864 pages > Solutions adaptables pour tous niveaux.
Conu comme un livre de recettes, cet ouvrage prsente une centaine de problmes et solutions immdiatemment applicables qui ne demandent quune connaissance de base de Java. Les problmes prsents vont du plus simple au plus complexe, couvrant ainsi la plupart des API.
Java et SOAP
Robert Englander ISBN 2-84177-230-6 1re dition, fvrier 2003 286 pages > Guide dapprentissage pour tous niveaux.
SOAP est un protocole indpendant et portable de transmission de messages bas sur XML. Java et SOAP aborde non seulement les bases de SOAP en prsentant son systme de messagerie, mais aussi lencodage des messages SOAP, comment travailler avec les API Java les plus standard et comment implmenter SOAP avec les principales plate-formes dont Microsoft .NET.
Java et XSLT
Eric M. Burke ISBN : 2-84177-205-5 1re dition, mars 2002 504 pages > Guide dapprentissage pour tous niveaux.
Java et XSLT a pour but daider le programmeur Java tirer parti de la puissance de XSLT pour des servlets ou des applications autonomes. De nombreux cas pratiques sont abords dans cet ouvrage : feuilles de style, transformations de documents, traitement de formulaires.
Java et XML
Brett McLaughlin ISBN 2-84177-204-7 2e dition, mars 2002 522 pages > Guide dapprentissage pour tous niveaux.
Java est aujourdhui le langage pour lequel le plus grand nombre dapplications XML ont t crites. Les servlets et les interfaces de programmation rseau y sont sans doute pour beaucoup. Lalliance Java/XML est idale pour construire des applications web, comme des plates-formes indpendantes, extensibles.
Au catalogue OReilly
Java in a Nutshell
David Flanagan ISBN 2-84177-216-0 4e dition, octobre 2002 1152 pages > Guide dapprentissage pour expriments et rfrence pour tous niveaux.
Cet ouvrage, crit dans un style concis et accompagn dexemples dutilisation, servira la fois de manuel de rfrence pour les vieux routards de Java, et de tutoriel pour les programmeurs avertis qui souhaitent dcouvrir ce langage rapidement. Lauteur passe en revue toutes les classes de paquetages fondamentaux pour les versions 1.0, 1.1, 1.2, 1.3 et 1.4.
Java threads
Scott Oaks & Henry Wong ISBN : 2-84177-079-6 2e dition, janvier 1999 334 pages > Guide dapprentissage pour tous niveaux.
Cet ouvrage explique clairement tout ce quil faut connatre sur les threads, et montre en dtails comment tirer pleinement parti des fonctionnalits de Java en la matire. Les plus ambitieux apprendront mme crire du code parallle pour machines multi-processeurs.
JavaServer Pages
Hans Bergsten ISBN 2-84177-145-8 1re dition, dcembre 2001 548 pages > Guide dapprentissage pour tous niveaux.
Utiliser les JSP dans des pages web afin dinteragir avec les composants serveur de lapplication et dvelopper les composants JSP et les combiner avec dautres technologies serveur : voil ce que vous propose cet ouvrage.
www.oreilly.fr
Au catalogue OReilly
Scurit en Java
Scott Oaks ISBN : 2-84177-063-X 1re dition, novembre 1999 438 pages > Guide dapprentissage pour dbutants et expriments.
Pour tout connatre des mcanismes de scurit propos par Java 2, et surtout apprendre sen servir dans ses applications. Cet ouvrage aborde notamment en dtail les chargeurs de classe, les gestionnaires de scurit, les listes daccs, ou encore les signatures numriques et lauthentification. Il montre en outre comment utiliser tous ces lments pour mettre en uvre sa propre politique de scurit.
Sax2
David Brownell ISBN : 2-84177-214-4 1re dition, septembre 2002 250 pages > Guide dapprentissage tous.
SAX2 est un analyseur syntaxique la fois rapide et lger pour le traitement de documents en XML ou non. Son usage est peu coteux en ressources mmoire et systme. Cet ouvrage vous permettra dapprendre vous servir de SAX2 avec Java et procder vos premires mises en uvre.
Au catalogue OReilly
JavaScript en action
Jerry Bradenbaugh ISBN 2-84177-135-0 1re dition, juillet 2001 528 pages > Solutions adaptables pour tous niveaux.
JavaScript est un langage performant de haut niveau, relativement facile apprendre qui permet de rendre des pages web dynamiques et interactives en agissant du ct client. Techniques et exemples de code JavaScript fourmillent sur le Web, mais ils ne sont jamais lis des cas concrets. Cet ouvrage propose donc des applications concrtes et fonctionnelles utilisables directement sur votre site. JavaScript en action est un livre de recettes qui enseigne lart de cuisiner des applications compltes pour le Web.
JavaScript - La rfrence
David Flanagan ISBN 2-84177-212-8 4e dition, septembre 2002 976 pages > Guide dapprentissage et rfrence pour tous niveaux.
JavaScript est lun des langages essentiels de la programmation Web. Il permet de crer des pages interactives et dynamiques. Cet ouvrage est une rfrence complte du langage qui couvre JavaScript 1.5. Ce guide dissque les caractristiques du noyau JavaScript, puis le ct client, ou le langage en situation. Les derniers chapitres documentent les objets dfinis par le noyau, ceux ct client et ceux dfinis par le DOM. Il sagit l dun ouvrage de rfrence qui conviendra tous les niveaux.
Introduction UML
Sinan Si Alhir ISBN : 2-84177-279-9 1re dition, dcembre 2003 232 pages > Guide dapprentissage.
Le lecteur qui ne connat ni UML ni mme la notion dobjet au sens informatique du terme trouvera dans cet ouvrage des explications limpides et structures et pourra les mettre en application sans tarder. Lauteur commence par une solide prsentation de la technologie objet avant daborder en dtail les diffrents types de diagrammes qui composent UML, indpendamment de tout langage dimplmentation. Une tude de cas constitue le fil rouge et permet dancrer dans la ralit des notions a priori abstraites.
www.oreilly.fr
Au catalogue OReilly
Introduction XML
Eric T. Ray ISBN 2-84177-142-3 1re dition, novembre 2001 384 pages > Guide dapprentissage pour dbutants.
Lauteur dmystifie le processus de cration et de transformation de documents XML au travers dexemples concrets, depuis llaboration de feuilles de style ncessaires la visualisation dun document XML dans un navigateur jusqu la programmation SAX et DOM, en passant par lcriture dune DTD.
XML en concentr,
W. Scott Means et Elliotte Rusty Harold ISBN 2-84177-353-1 3e dition, avril 2005 750 pages > Manuel de rfrence pour tous niveaux.
Cette troisime dition constitue une rfrence complte pour qui travaille avec des documents XML. Le webmaster tout comme le dveloppeur y trouveront une explication sur la manire dont ces technologies fonctionnent, ainsi quune rfrence sur leur syntaxe. De nouveaux chapitres sur XInclude, la grammaire de XML 1.1, CSS, les derniers jeux de caractres Unicode, etc., compltent cette mise jour.
XML Schma
Eric van der Vlist ISBN 2-84177-215-2 1re dition, juillet 2002 412 pages > Guide dapprentissage pour tous.
Cet ouvrage est non seulement un manuel de rfrence exhaustif de tous les lments qui constituent XML Schma, avec leurs attributs, leur valeurs possibles et leurs conditions dutilisation, mais aussi et surtout une analyse critique et approfondie des choix et compromis que devra effectuer tout architecte dun systme dinformation.
Comprendre XSLT
Philippe Rigaux et Bernd Amann ISBN 2-84177-148-2 1re dition, mars 2002 528 pages > Guide dapprentissage pour dbutants et expriments.
Les auteurs, soucieux de convaincre les nouveaux venus XSLT, introduisent progressivement des applications de plus en plus complexes, quil sagisse de produire la vole des documents XHTML ou PDF, ou maintenir un systme de News avec RSS, voir dobtenir automatiquement des fichiers vidos au format SMIL.
XSLT en action
Sal Mangano ISBN 2-84177-240-3 1re dition, juin 2003 694 pages > Solutions adaptables pour tous niveaux.
Ce recueil de recettes, qui pour chaque problme propose une ou plusieurs solutions, permettra au lecteur de rsoudre les diffrentes questions qui se posent lui au jour le jour. laide de recettes prtes lemploi, il pourra quel que soit son niveau, mettre immdiatement en application les solutions proposes dans cet ouvrage.
Au catalogue OReilly
Passez OpenOffice!
Franois Cerbelle ISBN: 2-84177-362-0 2e dition, octobre 2005 240 pages env. CD-Rom > Guide dapprentissage et rfrence pour tous niveaux.
OpenOffice est la suite bureautique du Libre. Elle sinstalle sur toutes les plateformes et rivalise dsormais avec Microsoft Office. Cet ouvrage est un guide complet pour tout utilisateur qui veut passer de MS Office OpenOffice. Il vise faciliter une transition sans heurt, toutes les cls sont donnes et les diffrences entre les deux suites sont dtaille. Le cdrom offert contient OpenOffice pour toutes les plateformes dans sa version 2.0.
Passez Thunderbird!
Georges Silva ISBN: 2-84177-363-9 1ere dition, octobre 2005 200 pages env. CD-Rom > Guide dapprentissage et rfrence pour tous niveaux.
Passez Thunderbird et grer enfin votre courrier lectronique! Cette application de courrier lectronique fonctionne sur toutes les plate-formes et prsente lavantage dviter bien des tracas quoccasionne Outlook Express. Cet ouvrage explique comment configurer Thunderbird, crer des comptes, rcuprer son courrier, en envoyer, grer le spam, lire des flux RSS, personnaliser Thunderbird, linstaller sur une cl USB...
Essayez linux!
Lautre systme dexploitation pour PC David Brickner ISBN: 2-84177-309-4 1ere dition, octobre 2005 300 pages env. CD-Rom > Guide dapprentissage et rfrence pour tous niveaux.
Ce guide permet tous les utilisateurs de Windows dessayer Linux tout de suite. Aucune installation ni configuration nest ncessaire (tout se fait par lentremise du CD) pour dcouvrir quoi ressemble un bureau Mandrake, une application Linux, etc. Cet ouvrage est conu comme une visite guide dans le monde Linux : ses bureaux, ses applications et son systme.