Cette page liste les exemples impliquant plusieurs patterns dans le livre "Head First Design Patterns".
Qui fait quoi ?
Chapter 9 - The Iterator and Composite Patterns: Well-Managed Collections
p 315 de la version papierExemple non traité ; table des matières :
Breaking News: Objectville Diner and Objectville Pancake House Merge Check out the Menu Items Lou and Mel’s Menu implementations What’s the problem with having two different menu representations? What now? Can we encapsulate the iteration? Meet the Iterator Pattern Adding an Iterator to DinerMenu Reworking the Diner Menu with Iterator Fixing up the Waitress code Testing our code What have we done so far? What we have so far... Making some improvements... Cleaning things up with java.util.Iterator We are almost there... What does this get us? Iterator Pattern defined Single Responsibility Taking a look at the Café Menu Reworking the Café Menu code Adding the Café Menu to the Waitress Breakfast, lunch AND dinner What did we do? We decoupled the Waitress.... ... and we made the Waitress more extensible But there’s more! Iterators and Collections Is the Waitress ready for prime time? Just when we thought it was safe... What do we need? The Composite Pattern defined Designing Menus with Composite Implementing the Menu Component Implementing the Menu Item Implementing the Composite Menu Getting ready for a test drive... Now for the test drive... Getting ready for a test drive... Flashback to Iterator The Composite Iterator The Null Iterator Give me the vegetarian menu The magic of Iterator & Composite together... Tools for your Design Toolbox
Simulateur de canards
p 499 de la version papier (chapitre 12)On réalise un simulateur de canards pour un parc naturel.
Le client est un quackologue, qui voudrait pouvoir simuler et suivre des populations de canards.
On va répondre aux différentes demandes en utilisant des patterns, mais il ne s'agit pas de patterns composés, au sens MVC par exemple.
Ici, il s'agit plutôt de patterns qui fonctionnent ensemble.
1 - Au départ
On démarre l'application avec :-
Les classes représentant différentes sortes de canards :
DuckCall MallardDuck RedheadDuck RubberDuck
Les canards ont tous une méthodequack()
(= coincoin) -
Une interface
Quackable
("qui fait coin coin") que tous les canards implémentent. -
Un début de simulateur,
DuckSimulator
. -
Une fonction
main()
Récupérer, lire, compiler et exécuter le code de départ dans ce répertoire.
Dans la classe
Dans la classe
DuckSimulator
, à quel endroit le polymorphisme est-il utilisé ? De quel type de polymorphisme s'agit-il ?
2 - Et les oies
On veut pouvoir gérer les oies (= goose en anglais) dans notre simulateur ; on nous fournit une classe :public class Goose { public void honk() { System.out.println("Honk"); } }La demande est de pouvoir utiliser des oies partout où on utilise des canards.
Y a-t-il un pattern adapté à cette demande ?
Ecrivez le code permettant de rajouter les oies dans notre simulateur.
Ecrivez le code permettant de rajouter les oies dans notre simulateur.
3 - Comptage
Le client souhaite pouvoir compter le nombre de quacks effectués par l'ensemble des canards observés.Mais il ne veut pas compter les quacks des oies.
L'output souhaité est le suivant :
=== Duck Simulator === Mallard Quack Redhead Quack DuckCall quack RubberDuck quack Honk Number of quacks : 4
Comment rajouter ce comportement sans avoir à modifier toutes les classes de canards ?
Modifier le code pour obtenir l'output souhaité.
Modifier le code pour obtenir l'output souhaité.
4 - Avec ou sans comptage ?
Cette solution fonctionne, mais les risques d'erreurs sont importants, car il faut décorer chaque canard créé.De plus, le client veut parfois utiliser le simulateur avec comptage, et parfois sans le comptage.
Ecrire une solution permettant facilement soit de créer des canards avec comptage, soit sans comptage.
Tous les canards créés doivent avoir le même comportement : avec comptage ou sans comptage.
Les quacks des oies ne sont toujours pas comptés.
Modifier la fonction
Tous les canards créés doivent avoir le même comportement : avec comptage ou sans comptage.
Les quacks des oies ne sont toujours pas comptés.
Modifier la fonction
main()
de manière à pouvoir lui passer un paramètre qui peut prendre les valeurs "count" ou "normal".
java Main normal === Duck Simulator === Mallard Quack Redhead Quack DuckCall quack RubberDuck quack Honk
java Main count === Duck Simulator === Mallard Quack Redhead Quack DuckCall quack RubberDuck quack Honk Number of quacks : 4
5 - Flock
flock = troupe, fouleMaintenant qu'on sait observer les canards, on aimerait bien gérer les différents groupes de canards qui peuplent le parc.
Ce qu'il nous faudrait, c'est la possibilité de gérer des collections et sous-collections de canards ; pouvoir appliquer des opérations sur des groupes entiers.
On créé une classe
Flock
, représentant un groupe de canards.
On va faire un choix d'implémentation : les méthodes concernant la gestion des enfants ne seront présentes que dans
Flock
.
Dans
DuckSimulator::simulate()
, on crée 3 groupes :
- un groupe contenant les 5 canards et oies déjà créés.
- un groupe contenant 4 nouveaux canards mallards.
- Un groupe contenant tous les canards (contenant donc les deux groupes précédemment créés).
Flock f1 = new Flock(); f1.addChild(mallardDuck); f1.addChild(redheadDuck); f1.addChild(duckCall); f1.addChild(rubberDuck); f1.addChild(goose); Quackable m1 = factory.createMallardDuck(); Quackable m2 = factory.createMallardDuck(); Quackable m3 = factory.createMallardDuck(); Quackable m4 = factory.createMallardDuck(); Flock f2 = new Flock(); f2.addChild(m1); f2.addChild(m2); f2.addChild(m3); f2.addChild(m4); Flock f3 = new Flock(); f3.addChild(f1); f3.addChild(f2);
Complétez le code pour obtenir cet output :
java Main count === Duck Simulator === Duck Simulator: Whole flock simulation Mallard Quack Redhead Quack DuckCall quack RubberDuck quack Honk Mallard Quack Mallard Quack Mallard Quack Mallard Quack Duck Simulator: Mallard flock simulation Mallard Quack Mallard Quack Mallard Quack Mallard Quack The ducks quacked : 12 times
6 - Tracking individual ducks
Maintenant, le quackologiste veut pouvoir suivre certains individus en particulier (afficher leurs quacks).En d'autres termes, il veut pouvoir observer certains canards, ce qui mène au pattern Observer.
Les canards sont les observables. Pour que le pattern Observer fonctionne, ils doivent tous dériver d'une abstraction qui a besoin de 2 méthodes :
registerObserver()
et notifyObservers()
.
Les default methods permettent de mettre ces 2 méthodes directement dans
Quackable
.
Pour que chaque canard implémente cette interface, il faut que chaque classe implémente la gestion d'une liste d'observateurs et de notification, ce qui entraîne des répétitions de code.
Pour limiter ces répétitions, on a plusieurs solutions :
-
On peut écrire une classe
ObservableHelper
implémentant ce mécanisme, et utiliser la composition dans les classes de canards :import java.util.Iterator; import java.util.ArrayList; public class ObservableHelper implements Quackable { ArrayList<Observer> observers = new ArrayList<Observer>(); Quackable duck; public ObservableHelper(Quackable duck) { this.duck = duck; } public void registerObserver(Observer observer) { observers.add(observer); } public void notifyObservers() { Iterator<Observer> iterator = observers.iterator(); while (iterator.hasNext()) { Observer observer = iterator.next(); observer.update(duck); } } public Iterator<Observer> getObservers() { return observers.iterator(); } }
On est toujours obligés de modifier chaque classe de canard :public class RubberDuck implements Quackable { ObservableHelper observable; public RubberDuck() { observable = new ObservableHelper(this); } public void quack() { System.out.println("Squeak"); notifyObservers(); } public void registerObserver(Observer observer) { observable.registerObserver(observer); } public void notifyObservers() { observable.notifyObservers(); } public String toString() { return "Rubber Duck"; } }
-
On peut utiliser l'héritage, en écrivant une classe
Observable
, qui implémenteQuackable
et qui contient le mécanisme. Les classes de canards dériveront deObservable
.import java.util.Iterator; import java.util.List; import java.util.ArrayList; public abstract class Observable implements Quackable{ ArrayList
Et il faut adapter les classes de canards :observers = new ArrayList (); public void quack(){ notifyObservers(); } public void registerObserver(Observer observer) { observers.add(observer); } public void notifyObservers() { Iterator iterator = observers.iterator(); while (iterator.hasNext()) { Observer observer = iterator.next(); observer.update(this); } } public Iterator getObservers() { return observers.iterator(); } } public class RedheadDuck extends Observable { public void quack() { System.out.println("Redhead Quack"); super.quack(); } }
Flock
.
On crée aussi une classe d'observateur que l'on appelle
Quackologist
, qui n'a besoin que d'une méthode update()
.
Dans
main()
, on a juste à ajouter :
Quackologist quackologist = new Quackologist(); f3.registerObserver(quackologist);
Implémenter de manière à avoir l'output suivant :
java Main count === Duck Simulator === Mallard Quack Quackologist: Mallard Duck just quacked. Redhead Quack Quackologist: Redhead Duck just quacked. DuckCall quack Quackologist: Duck Call just quacked. RubberDuck quack Quackologist: Rubber Duck just quacked. Honk Quackologist: Goose pretending to be a Duck just quacked. Mallard Quack Quackologist: Mallard Duck just quacked. Mallard Quack Quackologist: Mallard Duck just quacked. Mallard Quack Quackologist: Mallard Duck just quacked. Mallard Quack Quackologist: Mallard Duck just quacked. The ducks quacked : 8 times
7 - Représenter les canards
(pas présent dans le livre Head First)Voir la page sur Flyweight.