Vous êtes sur la page 1sur 73

Ministère de l’Enseignement Supérieur, Ecole Supérieure de Technologie

de la Recherche Scientifique et de la
Technologie et d’Informatique

Université du 7 Novembre à Carthage A.U. 2010/2011

Design Pattern

Afin d’améliorer l’architecture des


programmes

b "Une règle tripartite exprimant une relation entre un certain contexte, un


certain problème qui apparait répétiivement dans ce contexte et une certaine
configuration logiielle qui permet la résolution de ce problème"
Brad Appleton 1997

Département : Informatique Appliquée


Master Systèmes Informatiques et Logiciels
Enseignant : Mohamed KHADRAOUI
Table des matières
1. Problématique : ...................................................................... 3
2. Rappel POO ............................................................................ 3
3. Introduction ............................................................................ 4
1.1. Les Patterns en général ...................................................... 4
1.1. Les Design Pattern : la capitalisation de l’expérience ..... 5
1.2. Formalisme ......................................................................... 6
4. Classification ........................................................................... 7
5. Description .............................................................................. 8
1.3. Les pattern de création ...................................................... 8
1. Les prototypes .................................................................. 9
2. Le singleton .................................................................... 12
1.4. Les pattern de structure .................................................. 15
1. Le Adapter...................................................................... 15
2. Le Composite.................................................................. 18
3. Le Décorateur ‘Decorator’ ............................................ 24
4. Le Pont ‘Bridge’ ............................................................. 27
5. Le Proxy ......................................................................... 31
1.5. Les patterns de comportement ........................................ 34
1. Le ‘Command ‘ .............................................................. 35
2. Le Iterator ...................................................................... 38
3. Le ‘Observer ‘ ................................................................ 41
4. Le ‘State ‘ ....................................................................... 44
5. Le ‘Chain Of Responsability ‘ ...................................... 49
6. Le Visitor ........................................................................ 53
7. Le Memento.................................................................... 58
1.Problématique :
L'approche orientée objet tend à éclater les applications en composants plus simples et réutilisables.
Cependant, cette approche peut vite devenir un piège lorsque le découpage s'effectue sans règles
précises. Le concepteur finit par être saturé par la complexité du codage (effet spaghetti). Sans
architecture de base, telle application devient progressivement ingérable avec pour conséquence
l'émergence de bugs de plus en plus difficiles à corriger (effet dominos). Pour réduire les risques de
maintenance, il a fallu exploiter un niveau supplémentaire dans la conception objet : Les modèles
de conception ou design patterns.

2.Rappel POO
Contrairement aux langages de type procéduraux comme le C ou le Pascal, la conception objet ne
divise pas l'espace de données (attributs) et l'espace de traitements (méthodes). Cet espace commun
s'applique à une partie du problème à gérer sous la forme d'une classe. Une classe est une
représentation abstraite décrivant comment faire fonctionner des objets. Les objets sont construits à
partir de cette classe lors de -l'exécution par un processus d'instanciation (l'opérateur new). Chacune
des déclarations dans une classe peut être limitée dans sa portée (portée locale ou privée, portée de
groupe ou package, portée de descendance ou protégée, portée globale ou publique).

Une classe peut être associée à d’autres classes pour faciliter la réutilisation. L'association la plus
commune est l'héritage. L'héritage sert à spécialiser une classe existante (lien de
généralisation/spécialisation) par la modification/l'ajout de nouvelles méthodes et de nouvelles
données. Cette spécialisation conduit à la construction de nouvelles classes (appelées aussi sous-
classes). Le concept d'héritage peut être vu comme la conception d'un élément "est une sorte de".
Par exemple, une Voiture peut être vue comme une sous-classe de la classe Véhicule, une Voiture
hérite donc de la classe Véhicule, elle étend les caractéristiques du véhicule par des données et des
méthodes supplémentaires (la vitesse de pointe, le moteur...).

Lorsqu'une méthode d'une classe est redéfinie par une sous-classe, on dit qu'il y a surcharge.
Comme nous le verrons dans la suite cette possibilité donne une grande souplesse à l'introduction de
classes génériques déléguant certains comportements aux sous-classes.
Certaines classes ne sont pas complètement construites afin d'obliger les sous-classes à effectuer
une surcharge. On parle alors de classes abstraites ou d'interfaces. L'interface est employée en Java
pour compenser l'interdiction d'utiliser l'héritage multiple (héritage de plusieurs classes),
interdiction qui n'apparaît pas en C++. Une interface est associée à une ou plusieurs classes
d'implémentation. Une classe d'implémentation contient donc le code de réalisation de l'interface.
Alors que l'héritage concerne les classes (et leur hiérarchie), le polymorphisme est relatif aux
méthodes des objets. 3 différents types de polymorphisme existent :
polymorphisme ad hoc : permet d'avoir des fonctions de même nom, avec des fonctionnalités
similaires, dans des classes sans aucun rapport entre elles (surcharger l'opérateur + pour lui faire
réaliser des actions différentes suivant e type de l’objet)
Polymorphisme paramétrique : appelé généricité, représente la possibilité de définir plusieurs
fonctions de même nom mais possédant des paramètres différents (en nombre et/ou en type). Dans
ce cas, c'est la signature de la méthode qui détermine laquelle sera appelée.
Polymorphisme d’héritage : La possibilité de redéfinir une méthode dans des classes héritant
d'une classe de base s'appelle la spécialisation. Il est alors possible d'appeler une telle méthode d'un
objet sans se soucier de son type intrinsèque.

3.Introduction
1.1. Les Patterns en général
Un pattern (modèle) est une façon de faire, une manière d’arriver à l’objectif fixé. Les patterns
permettent d’utiliser des principes basés sur l’habileté des précurseurs en la matière. Les modèles
sont une façon excellente de capturer et transporter l’habileté. Lorsque votre habileté à employer
certaines méthodes communes seront maîtrisées, on se rend compte que ces méthodes pourraient
être réutilisées dans plusieurs contextes. Les Patterns, sont apparus bien avant l’apparition de
l’informatique, étaient utilisés en architecture. Les modèles architecturaux peuvent servir et inspirer
les personnes qui occuperont les bâtiments. Les Patterns font référence aux modèles standardisés
d’implémentation de certaines taches à réaliser… Les écrivains documentent ces modèles, en aidant
à standardiser ces modèles. Ils assurent aussi que la sagesse accumulée d'une habileté est disponible
aux futures générations des praticiens.

Remember, knowing concepts like abstraction, inheritance, and


polymorphism do not make you a good object oriented designer.

A design guru thinks about how to create flexible designs that are
maintainable and that can cope with change.

Patterns provide proven solutions to A design pattern describes how to


recurring design problems in OOP. structure classes to meet requirement.

Make code more readable to other Learn from community wisdom &
programmers -> Vocabulary to Implement proved solutions faster
communicate in programming community
1.1. Les Design Pattern : la capitalisation de l’expérience
Lorsqu’on parle de librairies de fonctions : toolkit nous nous retrouvons devant une multitude de
fonctions génériques réutilisables que nous prouvons appelé dans notre code : exemple : io, net,
awt,…
Dans un cadre relativement plus évolué : les Framework nous nous retrouvons entourés d’une
multitude de classes qui coopèrent pour fournir un modèle d’application pour une catégorie
d’application : MFC, WPF,…

Design Patterns (DP) Frameworks


Les DP sont des solutions récurrentes pour des problèmes qui Un Framework est un goupe de composants qui coopérent les
pourront apparaitre durant le cycle de développement d’une uns avec les autres pour fournir une architecture d’une
application application dans un domaine donné
L’objectif principal est de : L’objectif principal est de :
- Aider à améliorer la qualité des logiciels à produire - Aider à améliorer la qualité des logiciels à produire en
en termes de réutilisabilité, maintenanbilité, etc,… termes de réutilisabilité, maintenanbilité, etc,…
- Améliorer la productivité - Améliorer la productivité
Les DP sont des concepts logiques par nature Les frameworks sont plus physiques puisqu ’elles existent sous
une forme donnée dans l’application produite
Les DP sont générique et peuvent être utilisé dans plusieurs Les frameworks fournissent des fonctionnalités spécifiques à un
domaines d’application domaine donnée
Un DP n’existe pas sous forme de composant logiciel, il à Un Framework ne fournit pas un application complète, on a
besoin d’être implémenté chaque fois l’occasion se présente besoin de compléter et détendre les composants qui existent
DP sont utilisés dans la conception et l’implémenation des Les Frameworks englobent des composants qui répondent aux
frameworks. desigtn patterns existants

Nous parlons de Design pattern quand il s’agit d’une problématique récurrente dans différents
domaines de développement.
Quand les patterns sont appliqués dans le domaine de développement logiciel ils prennent la
dénomination de Design Pattern.
Un Design Pattern est un modèle de conception, il utilise des classes et leurs méthodes dans un
langage orientée objet. Les Design Patterns permettent de mettre en application des concepts
spécifiques sans pour autant avoir à passer beaucoup de temps sur la méthode de développement à
employer pour arriver à un objectif. Ils permettent donc de simplifier votre réflexion en réutilisant
des principes de base de la conception, cela aura donc pour effet de rendre votre programme plus
lisible lors de phase de maintenance par exemple. Les Design Patterns sont tout simplement des
architectures de classes permettant d'apporter une solution à des problèmes fréquemment rencontrés
lors des phases d'analyse et de conception d'applications. Ces solutions sont facilement adaptables
(donc réutilisables), elles sont utilisables sans aucun risques dans la grande majorité des langages de
programmation orientée objet. Les Design Patterns sont fiables. Ils ont une architecture facilement
compréhensible et identifiable pour les développeurs. Cela améliore donc la communication et la
compréhension entre développeurs.

Ce concept à été introduit par : Erich Gamma, Richard Helm, Ralph Johnson et John Vlissides « Gang Of
Four » suivant les travaux publiés en 1995 intitulé « Design Patterns -- Elements of Reusable Object-
Oriented Software ».
1.2. Formalisme
Un design pattern est en général composé de 4 parties essentielles à savoir :

Le nom du pattern : Point d’accroche qui permettra facilement de référer le problème et sa solution.
Ce nom doit être choisit avec soin

Le problème lui même : décrivant le plus systématiquement le contexte dans lequel s’appliquent
cette design pattern

La solution : qui décrit l’ensemble des éléments en jeu, leurs relations et leurs responsabilités. La
solution est exprimée d’une façon générique et ne repose sur aucun contexte pour être facilement
transportable. La solution doit être suffisamment abstraite pour mieux être concrétisée.

Les conséquences qu’entraînent l’application de la solution en matière de développement, cela peut


concerné le temps de calcul, la mémoire engagée, les contraintes particulières du langage utilisé.

Les diagrammes UML : Les deux points centraux (description du problème et solution) justifient
généralement l’usage de UML pour illustrer le propos et le rendre universel.

Class name
Attributs
Operations

*Nota :
Les principes de représentation UML : Interface, héritage, réalisation, dépendance,
association, instanciation, composition et aggrégation doivent être acquise.
4.Classification
Les design pattern sont généralement classifiés en trois catégories suivant la disposition suivante :
Purpose

Creational Structural Behavioral

Scope Class Factory Method Adapter (class) Interpreter

Template Method

Object Abstract Factory Adapter (object) Chain of Responsibility

Builder Bridge Command

Prototype Composite Iterator

Singleton Decorator Medaitor

Façade Momento

Flyweight Observer

Proxy State

Strategy

Visitor

Les “Creational Patterns” s’occupent de la création de classes et d’objets, les “ Structural


Patterns » s’occupent de leur composition. Les « Behavioral Patterns” concernent l’interaction des
objets et la distribution des responsabilités.

Sous la rubrique « Scope » la classification se fait au niveau des relations qui existent entre les
classes à leurs niveaux statiques d’un coté et l’interaction des objets dynamiques entre eux de
l’autre coté.
5.Description
1.3. Les pattern de création
Ce genre de pattern gère les différentes formes de création:

 Abstraire le processus d'instanciation.


 Rendre indépendant de la façon dont les objets sont créés, composés, assemblés, représentés.
 Encapsuler la connaissance de la classe concrète qui instancie.
 Cacher ce qui est créé, qui crée, comment et quand.

Pattern description
AbstractFactory on passe un paramètre à la création qui définit ce qu'on va
créer
Builder on passe en paramètre un objet qui sait construire l'objet à
partir d'une description
FactoryMethod la classe sollicitée appelle des méthodes abstraites ...il suffit
de sous-classer
Prototype des prototypes variés existent qui sont copiés et assemblés
Singleton unique instance
1. Les prototypes

Le Design Pattern Prototype est utilisé lorsqu’un système doit être indépendant de la façon dont ses
produits sont créés, assemblés, représentés ou quand la classe n'est connue qu'à l'exécution et pour
éviter une hiérarchie trop redondante.
Prototype permet donc de créer de nouveau objets en copiant une instance.
Ex: drag-and-drop de composants inconnus avec touche ctrl enfoncée.

Les classes à utiliser pour le Pattern Prototype sont (également appelées : les participants) :

• Prototype (c’est la super classe à partir de laquelle on appellera la méthode clone() qui
remplira les fonctionnalités recherchées)
• ConcretePrototype (l’héritage redéfinissant la méthode clone () permettra de retourner la
copie de l’instance à cloner)
• Client (c’est à partir de l’Opération qu’une nouvelle instance pourra être crée)

Le langage Java permet, grâce à l’interface Clonable, de pouvoir simplifier ce principe de recopie d’un objet.

package prototype;

public class Prototype implements Cloneable {


String a;
String b;
public Prototype(String c, String d) {
a = c; b = d;
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
A gérer l’exception « CloneNotSupportedException » qui peut être levée lors de l’appel de la méthode
clone() :

package prototype;

public class Client {

public static void main(String[] args) {


try {
Prototype p1 = new Prototype("Labo", "Sun");
Prototype p2 = (Prototype) p1.clone();
System.out.println(p2.a + ":" + p2.b);
} catch (CloneNotSupportedException e) {}
}
}

L’instance p2 est bien une copie de p1, cela signifie que lorsque la copie est faite, on peut modifier p1 sans
que cela affecte p2.

Delegation
The client replaces all references to the
new operator with calls to prototype
Before After
// The architect has done an admirable job of // A clone() method has been added to the
// decoupling the client from Stooge concrete Stooge hierarchy. Each derived class
// derived classes, and, exercising implements that
polymorphism. // method by returning an instance of itself
// But there remains coupling where instances //A Factory class has been introduced that
are actually created. maintains a suite of "breeder" objects (aka
proto types), and knows how to delegate to
class Stooge { the correct prototype.
public:
virtual void slap_stick() = 0; class Stooge {
}; public:
virtual Stooge* clone() = 0;
class Larry : public Stooge { virtual void slap_stick() = 0;
public: };
void slap_stick() {
cout << "Larry: poke eyes\n"; } class Factory {
}; public:
class Moe : public Stooge { static Stooge* make_stooge( int choice );
public: private:
void slap_stick() { static Stooge* s_prototypes[4];
cout << "Moe: slap head\n"; } };
}; Stooge* Factory::s_prototypes[] = {6
class Curly : public Stooge { 0, new Larry, new Moe, new Curly
public: };
void slap_stick() { Stooge* Factory::make_stooge( int choice ) {
cout << "Curly: suffer abuse\n"; } return s_prototypes[choice]->clone();
}; }

int main( void ) { int main( void ) {


vector roles; vector roles;
int choice; int choice;

while (true) { while (true) {


cout << "Larry(1) Moe(2) Curly(3) Go(0): cout << "Larry(1) Moe(2) Curly(3)
"; Go(0):";
cin >> choice; cin >> choice;
if (choice == 0) if (choice == 0)
break; break;
else if (choice == 1) roles.push_back(
roles.push_back( new Larry ); Factory::make_stooge( choice ) );
else if (choice == 2) }
roles.push_back( new Moe );
else for (int i=0; i < roles.size(); ++i)
roles.push_back( new Curly ); roles[i]->slap_stick();
} for (int i=0; i < roles.size(); ++i)
for (int i=0; i < roles.size(); i++) delete roles[i];
roles[i]->slap_stick(); }
for (int i=0; i < roles.size(); i++)
delete roles[i]; class Larry : public Stooge {
} public:
Stooge* clone() { return new Larry; }
// Larry(1) Moe(2) Curly(3) Go(0): 2 void slap_stick() {
// Larry(1) Moe(2) Curly(3) Go(0): 1 cout << "Larry: poke eyes\n"; }
// Larry(1) Moe(2) Curly(3) Go(0): 3 };
// Larry(1) Moe(2) Curly(3) Go(0): 0 class Moe : public Stooge {
// Moe: slap head public:
// Larry: poke eyes Stooge* clone() { return new Moe; }
// Curly: suffer abuse void slap_stick() {
cout << "Moe: slap head\n"; }
};
class Curly : public Stooge {
public:
Stooge* clone() { return new Curly; }
void slap_stick() {
cout << "Curly: suffer abuse\n"; }
};
1. Add a clone() method to the existing "product" hierarchy.
2. Design a "registry" that maintains a cache of prototypical objects. The
registry could be encapsulated in a new Factory class, or in the base
class of the "product" hierarchy.
3. Design a factory method that: may (or may not) accept arguments, finds
the correct prototype object, calls clone() on that object, and returns
the result.
4. The client replaces all references to the new operator with calls to the
factory method.

2. Le singleton

Le singleton est une classe dont on veut s’assurer qu’une seule et unique instance de celle-ci est
créée pendant toute la durée d’exécution de votre application.
Ce Design Pattern est très facile à utiliser et souvent utilisé, cependant beaucoup de personnes
l’utilisent mal. Exemple :
• système de log pour une application,
• pool de connection à la base de données,
• classe dérivant de CWinApp dans MFC (afin de s’assurer qu’il n’existe qu’une seule
instance de l’objet Application accessible via la méthode AfxGetApp() )
S’il est facile de concevoir une variable globale pouvant régler le problème de l’accès à l’instance
d’un objet dans une application, cette solution n’est malheureusement pas acceptable en termes
POO et n’est même pas en mesure de garantir que l’application puisse créer une autre instance
quelque part dans son code.
Ex : spooler d’impression, …

Pour s’assurer de l’unicité de l’instance du singleton, la responsabilité doit être déplacée à la classe
elle-même. Il faut tout d’abord penser à limiter les accès aux constructeurs (le plus souvent
« private » ou « protected », cependant si on utilise ce deuxième cela ne certifie pas l’unicité de
l’instance si on hérite votre singleton).
Si on n’a pas de constructeur, il faut donc un autre moyen pour retourner une instance de votre
classe. Pour cela on doit implémenter une méthode « static » qui, par convention, s’appellera :
getInstance().

Le singleton contrôle sa pro


propre insance, il peut contrôler
l’application quand et comment
appelle la méthode exposée
/**
* Exemple d'implémentation d'un singleton.<p>
* Cet exemple ne fait rien.
*/

public class Singleton {


/** L'instance statique */
private static Singleton instance;

/**
* Récupère l'instance unique de la class Singleton.
* Remarque : le constructeur est rendu inaccessible
*/
public static Singleton getInstance() {
if (null == instance) { // Premier appel
instance = new Singleton();
}
return instance;
}

/**
* Constructeur redéfini comme étant privé pour interdire son appel et forcer à passer par la méthode
getInstance()
private Singleton() {
}
}

The Singleton pattern ensures that a class has only one


instance and provides a global point of access to that instance

En C++ plusieurs façons sont disponibles pour en créer des classes singleton :

1- Utilisation d’une déclaration globale d’une variable


2- Utilisation de ‘static’ dans une classe implémentant un message heap pour une application :
Dans l’exemple suivant l’objet est crérer au premier appel . donc jamais créer si l’appel n’est pas
effectué :
class CMsgHndlr
{
public :
static CMsgHndlr * GetMsgHndlr();

protected :
CMsgHndlr(){}
virtual ~CMsgHndlr(){}
static CMsgHndlr m_MsgHndlr; // Static instance of Message handler class.
};

CMsgHndlr CMsgHndlr::m_MsgHndlr;
CMsgHndlr * CMsgHndlr::GetMsgHndlr()
{
return & m_MsgHndlr;
}
Before After
// A global variable is default initialized // Make the class responsible for its own
// when it is declared - but it is not ini global pointer and "initialization on first
tialized in earnest until its first use. use" (by using a private static
// This requires that the initialization pointer and a public static accessor method).
// code be replicated throughout the
application. The client uses only the public accessor
method.
class GlobalClass {
int m_value; class GlobalClass {
public: int m_value;
GlobalClass( int v=0 ) { m_value = v; } static GlobalClass* s_instance;
int get_value() { return m_value; GlobalClass( int v=0 )
} { m_value = v; }
void set_value( int v ) { m_value = v; } public:
}; int get_value()
{ return m_value; }
// Default initialization void set_value( int v )
GlobalClass* global_ptr = 0; { m_value = v; }
static GlobalClass* instance() {
void foo( void ) { if ( ! s_instance )
// Initialization on first use s_instance = new GlobalClass;
if ( ! global_ptr ) return s_instance;
global_ptr = new GlobalClass; }
global_ptr->set_value( 1 ); };
cout << "foo: global_ptr is "
<< global_ptr->get_value() << '\n'; // Allocating and initializing GlobalClass's
} static data member. The pointer is being

void bar( void ) { // allocated - not the object itself.


if ( ! global_ptr ) GlobalClass* GlobalClass::s_instance =0;
global_ptr = new GlobalClass;
global_ptr->set_value( 2 ); void foo( void ) {
cout << "bar: global_ptr is " GlobalClass::instance()->set_value( 1 );
<< global_ptr->get_value() << '\n'; cout << "foo: global_ptr is "
} << GlobalClass::instance()->get_value()
<< '\n';
int main( void ) { }
if ( ! global_ptr ) void bar( void ) {
global_ptr = new GlobalClass; GlobalClass::instance()->set_value(2);
cout << "main: global_ptr is " cout << "bar: global_ptr is "
<< global_ptr->get_value() << '\n'; << GlobalClass::instance()->get_value()
foo(); = << '\n';
bar(); }
} int main( void ) {
cout << "main: global_ptr is "
// main: global_ptr is 0 << GlobalClass::instance()->get_value()
// foo: global_ptr is 1 << '\n';
// bar: global_ptr is 2 //begin manipulation of the global pointer
foo();
bar();
}
// main: global_ptr is 0
// foo: global_ptr is 1
// bar: global_ptr is 2

1. Define a private static attribute in the "single instance" class.


1.4.
2. Define a public static accessor function in the class.
3. Do "lazy initialization" (creation on first use) in the accessor function.
4. Define all constructors to be protected or private.
5. Clients may only use the public accessor method to manipulate the Singleton.
Les pattern de structure
Ces patterns gèrent les formes de structure :

 Comment les objets sont assemblés


 Les patterns sont complémentaires les uns des autres

Pattern Description
Adapter rendre un objet conformant à un autre
Bridge pour lier une abstraction à une implantation
Composite basé sur des objets primitifs et composants
Decorator ajoute des services à un objet

Facade cache une structure complexe


Flyweight petits objets destinés à être partagés
Proxy un objet en masque un autre

1. Le Adapter

Ce pattern est très utilisé pour encapsuler un ensemble de fonctions sous la même implémentation
(Wrapping). L’utilisation de plusieurs APIs pour une question de portabilité est un très bon exemple
d’utilisation de l’adapteur. En effet, comme son nom l’indique il permet d’adapter une
implémentation à une autre afin de généraliser les APIs. Il permet de mettre en conformité de
composants d’origines diverses.

Une autre problématique traité au niveau de ce pattern est quand on veut utiliser plusieurs sous-
classes mais il est coûteux de redéfinir l'interface de chaque sous-classe en les sous-classant. Un
adapter peut adapter l'interface au niveau du parent.

La structure est assez simple, elle redéfinit les accès aux méthodes de l’API. L’interface
« Adapteur » permet de définir l’ensemble des méthodes qui seront communes à tous les
adaptateurs. La classe « AdapteurImpl » est l’implémentation de l’adaptateur qui permet de faire
le lien entre l’API et la normalisation qu’on souhaite mettre en place.
La/les classe(s) cible(s) représentent l’ensemble des fonctions de l’API utilisé.
Ce pattern permet donc de rajouter une couche logicielle afin de normaliser des implémentations
par exemple. Plusieurs raisons s’offrent à nous pour l’utilisation de ce pattern :

• Le besoin de ne pas dépendre d’une API particulière ou de pouvoir en changer rapidement.


• Une portabilité encore plus forte.

Le seul cas où il est difficile de partir dans ce genre de concept est lorsque le temps pour le
développement de votre application est très limité. Car ceci demande beaucoup de patience lorsque
les APIs à uniformiser sont importantes (exemple API Javamail et Lotus pour l’envoi des mails).
Voici un exemple très simple d’utilisation de ce pattern. Dans celui-ci nous « émulons » le fait que
nous ayons nos 2 APIs d’envoi de mail (ApiMail1 et ApiMail2). Notre adapter, ici, nous permettra
d’utiliser l’API que l’on veut, très facilement (nous spécifions une méthode mail() dans notre
interface de normalisation).

L’Adapter est un moyen commode de faire


fonctionner un objet avec une interface qu’il ne
possède pas !

• Hiérarchie des packages :

Les packages adapter.apimail1 et adapter.apimail2 représentent nos deux apis de mails


complètement différentes l’une de l’autre. Cependant elles ont le point commun de pouvoir envoyer
des mails.

• L’API mail1 • L’API mail2

package adapter.apimail1; package adapter.apimail2 ;

public class Base {


public class ApiMailBase { public Base() {
public ApiMailBase() { }
}
public static boolean sendMail() {
public static boolean envoiMail() { System.out.println("Envoi de mail avec ApiMail2");
System.out.println("Envoi mail avec ApiMail1"); return true;
return true; }
} }
}

• L’interface ApiMailAdapter permet d’indiquer les fonctionnalités communes que les apis
sont capables d’exécuter.
package adapter;
public interface ApiMailAdapter {
// Méthode commune à toutes les apis
public boolean mail();
}
• Implémentation de l’adapter pour • Implémentation de l’adapter pour l’API mail2 :
l’API mail1 :
package adapter; package adapter;
import adapter.apimail1.ApiMailBase; import adapter.apimail2.Base;

public class ApiMail1AdapterImpl implements


ApiMailAdapter { public class ApiMail2AdapterImpl implements
ApiMailAdapter {
public boolean mail() {
return ApiMailBase.envoiMail(); public ApiMail2AdapterImpl() {
} }
}
public boolean mail() {
return Base.sendMail();
}
}

• Le client (qui permet ici de tester l’application) :

package adapter;
public class Client {
protected ApiMailAdapter api;

//constructeur
public Client(ApiMailAdapter api) {
this.api = api;
}
public ApiMailAdapter getApi(){
return api;
}
public static void main(String []args){
Client clientAvecApiMail1 = new Client(new ApiMail1AdapterImpl());
Client clientAvecApiMail2 = new Client(new ApiMail2AdapterImpl());

// Envoi mail usage d’une méthode commune qq soit l’api

clientAvecApiMail1.getApi().mail();

clientAvecApiMail2.getApi().mail();
}
}

1. Identify the players: the component(s) that want to be accommodated , and the
component that needs to adapt.
2. Identify the interface that the client requires.
3. Design a "wrapper" class that can "impedance match" the adaptee to the client.
4. The adapter/wrapper class "has a" instance of the adaptee class.
5. The adapter/wrapper class "maps" the client interface to the adaptee interface.
6. The client uses (is coupled to) the new interface
2. Le Composite

Le design pattern composite compose les objets dans les structures correspondent à une structure
récursive ; de type arbre ou arborescence. Il permet alors d’afficher une partie ou l’intégralité d’une
hiérarchie.
La structure est la même que celle d’un arbre. Celle-ci se compose :

• Classe abstraite « Component » qui définit les attributs et méthodes communes (feuilles/nœuds).
• Une classe d’implémentation : « Leaf » qui représente une feuille.
• Une classe d’implémentation : « Composite » qui représente le nœud de l’arbre.

Ce pattern est surtout utilisé dans les applications utilisant des hiérarchies (arbres). Pour cela nous allons
simplement créer une application qui simule une hiérarchie d’éléments (chaîne de caractère) et les afficher.
Voici le résultat que nous allons produire :
+ Racine
+ RacineElement1
+ SousRoot1
-+ SousSousRoot
--+ SSElement1
--+ SSElement2
--+ SSElement3
+ RacineElement2
+ SousRoot2
-+ SElement1
-+ SElement2
-+ SElement3
Cette application est composée de 4 classes dont 3 principales (qui représentent le pattern) :
• La classe abstract AbstractElement (Component) : permet de définir le type d’élément à
hiérarchiser.

package composite;

public abstract class AbstractElement {


protected String name;

public AbstractElement(String name) {


this.name = name;
}

public abstract boolean add(AbstractElement e);


public abstract boolean remove(AbstractElement e);

public boolean display(int indentation) {


StringBuffer strBuf = new StringBuffer();
for (int i = 0; i < indentation; i++)
strBuf.append("-");
System.out.println(strBuf.toString() + "+ " + this.name);
return true;
}
}

• La classe Leaf : représente une feuille dans votre arbre. Elle implémente AbstractElement.

package composite;

public class Leaf extends AbstractElement {

public Leaf(String name) {


super(name);
}

public boolean add(AbstractElement e) {


System.err.println("Impossible de rajouter des éléments dans une feuille");
return false;
}

public boolean remove(AbstractElement e) {


System.err.println("Impossible de supprimer des éléments dans une feuille");
return false;
}
}

• La classe Node : représente un nœud dans votre arbre. Cette classe implémente AbstractElement.
On peut y ajouter des feuilles ou d’autres nœuds.

package composite;

import java.util.ArrayList;

public class Node extends AbstractElement {


private ArrayList elements;

public Node(String name) {


super(name);
elements = new ArrayList();
}

public boolean add(AbstractElement e) {


elements.add(e);
return true;
}

public boolean remove(AbstractElement e) {


elements.remove(e);
return true;
}

public boolean display(int indentation) {


super.display(indentation);
for (int i = 0; i < elements.size(); i++)
((AbstractElement) elements.get(i)).display(indentation + 1);
return true;
}
}

• La classe Client n’est autre que la classe de lancement de l’application :

package composite;
public class Client {

public static void main(String[] args) {


Node root = new Node("Racine");
Node sousRoot1 = new Node("SousRoot1");
Node sousSousRoot = new Node("SousSousRoot");
Node sousRoot2 = new Node("SousRoot2");

// sousSousRoot
sousSousRoot.add(new Leaf("SSElement1"));
sousSousRoot.add(new Leaf("SSElement2"));
sousSousRoot.add(new Leaf("SSElement3"));

// sousRoot1
sousRoot1.add(sousSousRoot);

// sousRoot2
sousRoot2.add(new Leaf("SElement1"));
sousRoot2.add(new Leaf("SElement2"));
sousRoot2.add(new Leaf("SElement3"));

// Racine
root.add(new Leaf("RacineElement1"));
root.add(sousRoot1);
root.add(new Leaf("RacineElement2"));
root.add(sousRoot2);

root.display(-1);
}
}
Before After
class File { // ***** Define a "lowest common denominator"
public File( String name ) { *****
m_name = name; interface AbstractFile {
} public void ls();
public void ls() { }
System.out.println( Composite.g_indent +
m_name ); // * File implements the "lowest common
} denominator"
private String m_name; class File implements AbstractFile {
} public File( String name ) {
class Directory { m_name = name;
public Directory( String name ) { }
m_name = name; public void ls() {
} System.out.println( CompositeDemo.g_indent
public void add( Object obj ) { + m_name );
m_files.add( obj ); }
} private String m_name;
public void ls() { }
System.out.println( Composite.g_indent +
m_name ); // **Directory implements the "lowest common
Composite.g_indent.append( " " ); denominator"
for (int i=0; i < m_files.size(); ++i) { class Directory implements AbstractFile {
Object obj = m_files.get(i); public Directory( String name ) {
// ***** Recover the type of this object *** m_name = name;
if (obj.getClass().getName().equals( }
"Directory" )) public void add( AbstractFile obj ) {
((Directory) obj).ls(); m_files.add( obj );
else }
((File) obj).ls(); public void ls() {
} System.out.println( CompositeDemo.g_indent
Composite.g_indent.setLength( + m_name );
CompositeDemo.g_indent.length() - 3 ); CompositeDemo.g_indent.append( " " );
} for (int i=0; i < m_files.size(); ++i) {
private String m_name; //**Leverage the "lowest common denominator
private ArrayList m_files = new ArrayList(); AbstractFile obj = (AbstractFile)
} m_files.get(i);
obj.ls();
public class CompositeDemo { }
public static StringBuffer g_indent = new CompositeDemo.g_indent.setLength(
StringBuffer(); CompositeDemo.g_indent.length() - 3 );
}
public static void main( String[] args ) {
Directory one = new Directory("dir111"), private String m_name;
two = new Directory("dir222"), private ArrayList m_files = new ArrayList();
thr = new Directory("dir333"); }
File a = new File("a"), b = new File("b"),
c = new File("c"), d = new File("d"), public class CompositeDemo {
e = new File("e"); public static StringBuffer g_indent = new
one.add( a ); one.add(two); one.add(b); StringBuffer();
two.add(c); two.add(d); two.add(thr );
thr.add(e); public static void main( String[] args ) {
one.ls(); Directory one = new Directory("dir111"),
} two = new Directory("dir222"),
} thr = new Directory("dir333");
// dir111 File a = new File("a"), b = new File("b"),
// a c = new File("c"), d = new File("d"),
// dir222 e = new File("e");
// c one.add( a ); one.add( two ); one.add( b );
two.add( c ); two.add( d ); two.add( thr);
// d thr.add( e );
// dir333 one.ls();
// e }
// b }
C++ example
Before After
class File { // ***** Define a "lowest common denominator"
public File( String name ) { *****
m_name = name; interface AbstractFile {
} public void ls();
public void ls() { }
System.out.println( Composite.g_indent +
m_name ); // * File implements the "lowest common
} denominator"
private String m_name; class File implements AbstractFile {
} public File( String name ) {
class Directory { m_name = name;
public Directory( String name ) { }
m_name = name; public void ls() {
} System.out.println( CompositeDemo.g_indent
public void add( Object obj ) { + m_name );
m_files.add( obj ); }
} private String m_name;
public void ls() { }
System.out.println( Composite.g_indent +
m_name ); // **Directory implements the "lowest common
Composite.g_indent.append( " " ); denominator"
for (int i=0; i < m_files.size(); ++i) { class Directory implements AbstractFile {
Object obj = m_files.get(i); public Directory( String name ) {
// ***** Recover the type of this object *** m_name = name;
if (obj.getClass().getName().equals( }
"Directory" )) public void add( AbstractFile obj ) {
((Directory) obj).ls(); m_files.add( obj );
else }
((File) obj).ls(); public void ls() {
} System.out.println( CompositeDemo.g_indent
Composite.g_indent.setLength( + m_name );
CompositeDemo.g_indent.length() - 3 ); CompositeDemo.g_indent.append( " " );
} for (int i=0; i < m_files.size(); ++i) {
private String m_name; //**Leverage the "lowest common denominator
private ArrayList m_files = new ArrayList(); AbstractFile obj = (AbstractFile)
} m_files.get(i);
obj.ls();
public class CompositeDemo { }
public static StringBuffer g_indent = new CompositeDemo.g_indent.setLength(
StringBuffer(); CompositeDemo.g_indent.length() - 3 );
}
public static void main( String[] args ) {
Directory one = new Directory("dir111"), private String m_name;
two = new Directory("dir222"), private ArrayList m_files = new ArrayList();
thr = new Directory("dir333"); }
File a = new File("a"), b = new File("b"),
c = new File("c"), d = new File("d"), public class CompositeDemo {
e = new File("e"); public static StringBuffer g_indent = new
one.add( a ); one.add(two); one.add(b); StringBuffer();
two.add(c); two.add(d); two.add(thr );
thr.add(e); public static void main( String[] args ) {
one.ls(); Directory one = new Directory("dir111"),
} two = new Directory("dir222"),
} thr = new Directory("dir333");
// dir111 File a = new File("a"), b = new File("b"),
// a c = new File("c"), d = new File("d"),
// dir222 e = new File("e");
// c one.add( a ); one.add( two ); one.add( b );
two.add( c ); two.add( d ); two.add( thr);
// d thr.add( e );
// dir333 one.ls();
// e }
// b }
1. Ensure that your problem is about representing "whole-part" hierarchical
relationships.
2. Consider the heuristic, "Containers that contain containees, each of which could be a
container." For example, "Assemblies that contain components, each of which could be
an assembly." Divide your domain concepts into container classes, and containee
classes.
3. Create a "lowest common denominator" interface that makes your containers and
containees interchangeable. It should specify the behavior that needs to be exercised
uniformly across all containee and container objects.
4. All container and containee classes declare an "is a" relationship to the interface.
5. All container classes declare a one-to-many "has a" relationship to the interface.
6. Container classes leverage polymorphism to delegate to their containee objects.
7. Child management methods [e.g. addChild(), removeChild()] should normally be
defined in the Composite class. Unfortunately, the desire to treat Leaf and Composite
objects uniformly may require that these methods be promoted to the abstract
Component class.

« Indépendamment du Design Pattern Iterator qui peut être utilisé dans ce contexte. Le présent
Design Pattern est utilisé chaque fois qu’un client veut exécuter des commandes sur des objets
abstraction faite si ces objets contiennent eux-mêmes d’autres objets. »
Don Robert
3. Le Décorateur ‘Decorator’
Le pattern Décorateur (Decorator) attache dynamiquement des responsabilités supplémentaires à un
objet. Il fournit une alternative souple à l’héritage, pour étendre des fonctionnalités à l’exécution.
Le souci de l’héritage est l’ajout de fonctionnalités de façon statique. En effet, l’héritage de classe
se définit lors de l’écriture du programme et ne peut être modifié après la compilation. Or, dans
certains cas, on peut vouloir rajouter des fonctionnalités de façon dynamique.
Exemple :
Dans le cas où on est demandé de fournir une interface graphique supportant des fenêtres avec ou
sans bordure, avec ou sans scroll bar. Nous pouvons définir une hiérarchie de classe comme suit :

Fenêtre
. Dessiner ()

Fenêtre avec scroll bar vertical Fenêtre avec bordure

Fenêtre avec scroll bar horizental

Fenêtre avec scroll bar vertical & horizontal

Fenêtre avec scroll bar vertical & horizontal


avec bordure
Cette structure s’avère figé et difficile »ment maintenable quan’aux évolution d’ergonomie. Le
décorateur permet une composition dynamique de possibilités :
Widget* aWidget = new BorderDecorator( new HorizontalScrollBarDecorator(
New VerticalScrollBarDecorator( new Window( 80, 24 ))));
aWidget->Dessiner();

Cette flexibilité est assurée par une encapsulation récursive. Suivant le schéma suivant :

1
<<LCD Interface >>
Dessiner ()

Fenêtre Décorateur
Dessiner () Dessiner ()

bordure Scroll bar horizentale Scroll bar verticale


La solution consiste à encapsuler l’objet d’origine (fenêtre) derrière une interface abstraite. Le
décorateur ainsi que l’objet d’origine héritent de cette interface. L’interface utilise une composition
récursive pour permettre un nombre illimité de couche de décoration rajouté à l’objet d’origine.

Before After
// Inheritance run amok // Replacing inheritance with wrapping-
class A { delegation
public: // Discussion. Use aggregation instead of
virtual void do_it() { cout << 'A'; } inheritance to implement embellishments to
}; a "core" object. Client can dynamically
class AwithX : public A { compose permutations, instead of the
public: architect statically wielding multiple
/*virtual*/ void do_it() { inheritance.
A::do_it(); class I {
do_X(); public:
}; virtual ~I() { }
private: virtual void do_it() = 0;
void do_X() { cout << 'X'; } };
}; class A : public I { // Core Class
public:
class AwithY : public A { ~A() { cout << "A dtor" << '\n'; }
public: /*virtual*/ void do_it() { cout << 'A';
/*virtual*/ void do_it() { }
A::do_it(); };
do_Y(); class D : public I { // Decorator Class
} public:
protected: D( I* inner ) { m_wrappee = inner; }
void do_Y() { cout << 'Y'; } ~D() { delete m_wrappee; }
}; /*virtual*/void do_it(){
m_wrappee->do_it(); }
class AwithZ : public A { private: I* m_wrappee;
public: };
/*virtual*/ void do_it() { class X : public D {
A::do_it(); public:
do_Z(); X( I* core ) : D( core ) { }
} ~X() { cout << "X dtor" << " "; }
protected: /*virtual*/ void do_it() {
void do_Z() { cout << 'Z'; } D::do_it();
}; cout << 'X'; }
};
class AwithXY :public AwithX, public AwithY class Y : public D {
{ public:
public: Y( I* core ) : D( core ) { }
/*virtual*/ void do_it() { ~Y() { cout << "Y dtor" << " "; }
AwithX::do_it(); /*virtual*/ void do_it() {
AwithY::do_Y(); D::do_it();
} cout << 'Y';
}; }
};
class AwithXYZ:public AwithX, class Z : public D {
public AwithY,public AwithZ{ public:
public: Z( I* core ) : D( core ) { }
/*virtual*/ void do_it() { ~Z() { cout << "Z dtor" << " "; }
AwithX::do_it(); /*virtual*/ void do_it() {
AwithY::do_Y(); D::do_it();
AwithZ::do_Z(); cout << 'Z'; }
} };
}; int main( void ) {
int main( void ) { I* anX = new X( new A );
AwithX anX; I* anXY = new Y( new X( new A ) );
AwithXY anXY; I* anXYZ = new Z(new Y(new X(new A )));
AwithXYZ anXYZ; anX->do_it(); cout << '\n';
anX.do_it(); cout << '\n'; anXY->do_it(); cout << '\n';
anXY.do_it(); cout << '\n'; anXYZ->do_it(); cout << '\n';
anXYZ.do_it(); cout << '\n'; delete anX; delete anXY; delete
} anXYZ;
// AX }
// AXY // AX
// AXYZ // AXY
// AXYZ
// X dtor A dtor
// Y dtor X dtor A dtor
// Z dtor Y dtor X dtor A dtor

1. Ensure the context is: a single core (or non-optional) component, several optional
embellishments or wrappers, and an interface that is common to all.
2. Create a "Lowest Common Denominator" interface that makes all classes
interchangeable.
3. Create a second level base class (Decorator) to support the optional wrapper classes.
4. The Core class and Decorator class inherit from the LCD interface.
5. The Decorator class declares a composition relationship to the LCD interface, and this
data member is initialized in its constructor.
6. The Decorator class delegates to the LCD object.
7. Define a Decorator derived class for each optional embellishment.
8. Decorator derived classes implement their wrapper functionality - and - delegate to the
Decorator base class.
9. The client configures the type and ordering of Core and Decorator objects.
4. Le Pont ‘Bridge’
Ce pattern permet d’éviter d’avoir un lien permanent entre l'abstraction et l'implantation.
L'implantation est choisie à l'exécution par exemple.
L'abstraction et l'implantation sont toutes les deux susceptibles d'être raffinées, étoffées, ….
Cependant, toutes les modifications éventuellement subies par l'implantation ou l'abstraction ne
doivent pas avoir d'impacts sur le client (pas de recompilation)

Impl1

Impl2
Client Bridge

Impl3

Intermediary defines the interface but not the


implementation.

E.g., Motif/Mac/Windows look and feel.

Ce diagramme de classe montre à la fois les concepts d’héritage multiple ainsi que le lien de
composition entre la classe « Abstraction » et « Implementor ». C’est ce lien qui donne toute la
particularité de ce Design Pattern.
Bridge Pattern is all about decoupling an abstraction (interface) from its implementation so
that the two can vary independently. In MFC, the process of storing/retrieving an object
to/from a persistence mechanism (like a file) is called Serialization. MFC uses the Bridge
Pattern to implement Serialization. CArchive and CFile classes implement object
Serialization. CArchive class provides the interface for writing/reading an object to/from a
persistence mechanism whereas the CFile and its sub classes provides implementation for
different persistence mechanisms such as memory, disk file, sockets etc.

A CArchive object is configured with an object of class CFile (or a derived class) during its
construction, from which it obtains the necessary information for serialization, including the
filename and type of the requested operation (a read or write). Client performing the
Serialization operation can use CArchive object without regarding the persistence
mechanism implemented by CFile classes.

• La classe Client permet d’instancier et d’appeler la méthode operation().

package bridge;

public class Client {

public static void main(String[] args) {

Abstraction abstraction = new RefinedAbstraction();

//abstraction.implementor = new ConcreteImplementorA();


setImplementor (new ConcreteImplementorA());
abstraction.operation();

//abstraction.implementor = new ConcreteImplementorB();


setImplementor (new ConcreteImplementorB());
abstraction.operation();
}
}

• La classe Implementor est la super classe qui s’occupe de la partie implémentation, on retrouvera
la méthode operation() qui sera redéfinie dans les sous classes.

package bridge;

abstract class Implementor {


abstract public void operation();
}

• La classe ConcreteImplementorA qui hérite de la classe Implementor et redéfini la méthode


operation().

public class ConcreteImplementorA extends Implementor {

public void operation() {


System.out.println("ConcreteImplementorA Operation");
}
}
• La classe ConcreteImplementorB qui hérite de la classe Implementor et redéfini la méthode
operation(). La méthode operation() n’affichera pas la même ligne que la classe précédante.

public class ConcreteImplementorB extends Implementor {

public void operation() {


System.out.println("ConcreteImplementorB Operation");
}
}

• La classe Abstraction permet de simuler la présence de la méthode operation() au niveau abstrait.

package bridge;

public class Abstraction {


protected Implementor implementor;

public void operation() {


implementor.operation();
}

public void setImplementor(Implementor impl) { //pour le choix du client


this.implementor = impl;
}
}

• La classe RefinedAbstraction hérite de la classe ci–dessus et n’a que pour but de lier la partie
d’abstraction d’implémentation.

package bridge;

public class RefinedAbstraction extends Abstraction {


public void operation() {
implementor.operation();
}
}

1. Decide if two orthogonal dimensions exist in the domain. These independent


concepts could be: abstraction<>platform, or domain<>infrastructure, or
front-end<>back-end, or interface<>implementation.
2. Design the separation of concerns: what does the client want, and what do
the platforms provide.
3. Design a platform-oriented interface that is minimal, necessary, and
sufficient. Its goal is to decouple the abstraction from the platform.
4. Define a derived class of that interface for each platform.
5. Create the abstraction base class that "has a" platform object and delegates
the platform-oriented functionality to it.
6. Define specializations of the abstraction class if desired.
Exemple: Thread scheduling
Thread scheduler

Premptive thread schedluer Time sliced thread scheduler

Unix PTS Windows PTS Unix TSTS Windows TSTS

Il y’a deux types de threads et deux types de système d’exploitation (platformes). Suivant
l’approche de spécialisation, nous avons à définir une permutation pour chaque diemnsion. Sinous
rajoutons une nouvelle platformes la hierarchie devient :

Thread scheduler

Premptive thread schedluer Time sliced thread scheduler

Unix PTS Windows PTS Unix TSTS Windows TSTS

JVM TSTS
JVM PTS

Suivant cette approche d’héritage, le nombre de classe qui resulte sera le produit du nombre de
scheduling avec le nombre de platforme
Ce pattern propose une factorisation de cette explosion de hiérarchie en deux hiérarchies
orthogonales :
 Abstarction de la plateforme (indépendance de la plateforme)
 Implementation dépendante de la plateforme

Le client pourra choir dynamiquement (au runtime) l’implémentation qu’il utilise (via le membre
Impl)

Thread scheduler Impl Thread scheduler Implmentor

Premptive TS Time sliced TS Unix JVM Win


5. Le Proxy

Dans ce cas le client interagit avec un intermédiaire. La requête effective sera emballée et redirigée
vers le serveur. C’est le cas de la gestion de connexion, de persistance, contrôle d’accès, de
comptage de références…
Un cas courant d’utilisation est lorsqu’on veut pousser la construction de l’objet que lors de la
première utilisation. Le proxy se place alors entre le client et l’objet réel, et qui n’effectue la
construction ‘coûteuse’ que lorsque nécessaire.

Adapter provides a different interface to its subject. Proxy provides the


same interface. [GoF. p216]
Before After
// Direct coupling, lots of start-up and shut- //create on first Use
down overhead
class RealImage {
class Image { int m_id;
int m_id; public:
static int s_next; RealImage( int i ) { m_id = i;
public: cout << " $$ ctor: "<< m_id << '\n'; }
Image() { m_id = s_next++; ~RealImage(){cout <<"dtor: "<< m_id << '\n';}
cout << " $$ ctor: "<< m_id << '\n'; } void draw() {
~Image() { cout << "drawing image " << m_id << '\n'; }
cout << " dtor: " << m_id << '\n'; } };
void draw() {
cout << " drawing image"<< m_id <<'\n'; } // 1. Design an "extra level of indirection"
}; wrapper class
int Image::s_next = 1; class Image {
// 2. The wrapper class holds a pointer to the
int main( void ) { real class
Image images[5]; RealImage* m_the_real_thing;
int m_id;
for (int i; true; ) { static int s_next;
cout << "Exit [0], Image [1-5] : "; public:
cin >> i; Image() {
if (i == 0) m_id = s_next++;
break; // 3. Initialized to null
images[i-1].draw(); m_the_real_thing = 0;
} } }
~Image() { delete m_the_real_thing; }
// $$ ctor: 1 void draw() {
// $$ ctor: 2 // 4. When a request comes in, the real object
// $$ ctor: 3 is created "on first use"
// $$ ctor: 4 if ( ! m_the_real_thing)
// $$ ctor: 5 m_the_real_thing = new RealImage( m_id );
// Exit [0], Image [1-5] : 2 // 5. The request is always delegated
// drawing image 2 m_the_real_thing->draw();
// Exit [0], Image [1-5] : 4 }
// drawing image 4 };
// Exit [0], Image [1-5] : 2 int Image::s_next = 1;
// drawing image 2
// Exit [0], Image [1-5] : 0 int main( void ) {
// dtor: 5 Image images[5];
// dtor: 4 for (int i; true; ) {
// dtor: 3 cout << "Exit[0], Image[1-5]: ";
// dtor: 2 cin >> i;
// dtor: 1 if (i == 0)
break;
images[i-1].draw();
} }
// Exit [0], Image [1-5] : 2
// $$ ctor: 2
// drawing image 2
// Exit [0], Image [1-5] : 4
// $$ ctor: 4
// drawing image 4
// Exit [0], Image [1-5] : 2
// drawing image 2
// Exit [0], Image [1-5] : 0
// dtor: 4
// dtor: 2
Exemple C#
using System;
namespace
ConsoleApplicationTest.FundamentalPatterns.ProtectionProxyPattern
{
public interface IClient {
string GetAccountNo();
}

public class RealClient : IClient {


private string accountNo = "12345";
public RealClient() {
Console.WriteLine("RealClient: Initialized");
}
public string GetAccountNo() {
Console.WriteLine("RealClient's AccountNo: " + accountNo);
return accountNo;
}
}

public class ProtectionProxy : IClient


{
private string password; //password to get secret
RealClient client;

public ProtectionProxy(string pwd) {


Console.WriteLine("ProtectionProxy: Initialized");
password = pwd;
client = new RealClient();
}

// Authenticate the user and return the Account Number


public String GetAccountNo() {
Console.Write("Password: ");
string tmpPwd = Console.ReadLine();

if (tmpPwd == password) {
return client.GetAccountNo();
} else {
Console.WriteLine("ProtectionProxy: Illegal password!");
return "";
}
}
}

class ProtectionProxyExample
{
[STAThread]
public static void Main(string[] args) {
IClient client = new ProtectionProxy("thePassword");
Console.WriteLine();
Console.WriteLine("main received: " + client.GetAccountNo());
Console.WriteLine("\nPress any key to continue . . .");
Console.Read();
}
}
}
1. Identify the leverage or “aspect” that is best implemented as a wrapper or surrogate.
2. Define an interface that will make the proxy and the original component interchangeable.
3. Consider defining a Factory that can encapsulate the decision of whether a proxy or original
object is desirable.
4. The wrapper class holds a pointer to the real class and implements the interface.
5. The pointer may be initialized at construction, or on first use.
6. Each wrapper method contributes its leverage, and delegates to the wrappee object
1.5. Les patterns de comportement
Ces pattern décrivent les Formes de comportement des :

 algorithmes
 comportements entre objets
 formes de communication entre objet

Pattern Description
Command • spécifier, stocker et exécuter des actions à des
moments différents.
• on veut pouvoir "défaire". Les commandes exécutées
peuvent être stockées ainsi que les états des objets
affectés...
• on veut implanter des transactions ; actions de "hautniveau".
Chain of • plus d'un objet peut traiter une requète, et il n'est pas
responsability connu a priori
• l'ensemble des objets pouvant traiter une requète est
construit dynamiquement
State • Le comportement d'un objet dépend de son état, qui
change à l'exécution
• Les opérations sont constituées de partie
conditionnelles de grande taille (case)
Iterator • pour accéder à un objet composé dont on ne veut
pas exposer la structure interne
• pour offrir plusieurs manières de parcourir une
structure composée
• pour offrir une interface uniforme pour parcourir
différentes structures
Observer • Une abstraction a plusieurs aspects, dépendant l’un
de l’autre. Encapsuler ces aspects indépendament
permet de les réutiliser séparément.
• Quand le changement d’un objet se répercute vers
d’autres.
• Quand un objet doit prévenir d’autres objets sans
pour autant les connaitre.
Memento • on veut sauvegarder tout ou partie de l’état d’un objet
pour éventuellement pouvoir le restaurer, et
• une interface directe pour obtenir l’état de l’objet
briserait l’encapsulation
Visitor • une structure d'objets contient de nombreuses
classes avec des interfaces différentes et on veut
appliquer des operations diverses sur ces objets.
• les structures sont assez stables, et les opération sur
leurs objets évolutives.
1. Le ‘Command ‘
Le principal objectif de ce Design Pattern est d’encapsuler une requête sous forme d’objet. Ce
pattern est utilisé lorsque :

• on veut paramétrer des objets pour réaliser certaines actions avec différentes requêtes
• pour spécifier, mettre en file, et exécuter des requêtes à des moments différents
• pour implémenter des opérations réversibles
• pour structurer un système à l’aide d’opérations de haut niveau construites à partir
d’opérations primitives.

Ce design pattern est représenté par :

• une interface CallbackInterface qui définit les méthodes de la commande qui devront être
implémentées.
• Plusieurs classes CallbackOne et CallbackTwo qui implémente l’interface
CallbackInterface servant à définir la jonction entre l’objet « Receiver » et les actions à
effectuer.
• une classe « Client » qui génère les commandes voulues « CallbackInterface ». Ces
commandes passent à une autre classe qui possède les ressources pour les exécuter

Ce design pattern est très utilisé dans la programmation objet. En effet il se retrouve dans les
applications utilisant les files d’attentes, les logs, les supports d’opérations réversibles (“undo”)…
Before After
//the client has to query the "type" of each //the desired method is encapsulated in each
object, and manually invoke the desired method. Command object.
class Giant { class Giant { //receiver object
public: public:
enum Type { Fee, Phi, Pheaux }; Giant() { m_id = s_next++; }
Giant() { void fee(){ cout << m_id << "-fee ";}
m_id = s_next++;
m_type = (Type) (m_id % 3);
void phi(){ cout << m_id << "-phi ";}
} void pheaux(){cout << m_id <<"-pheaux";}
Type get_type() { return m_type; }
void fee() { cout << m_id << "-fee "; } private:
void phi() { cout << m_id << "-phi "; } int m_id; No arguments
void pheaux() { cout << m_id << "-pheaux "; static int s_next;
} }; to pass
private:
Type m_type; int Giant::s_next = 0;
int m_id;
static int s_next;
}; typedef void (Giant::*Action)();
int Giant::s_next = 0; class Command {
public:
template <typename T> Command( Giant* object, Action method){
class Queue { m_object = object;
public: m_method = method;
Queue() { m_add = m_remove = 0; } }
void enque( T* c ) { void execute() {
m_array[m_add] = c;
m_add = (m_add + 1) % SIZE;
(m_object->*m_method)();
} }
T* deque() { private:
int temp = m_remove; Giant* m_object;
m_remove = (m_remove + 1) % SIZE; Action m_method;
return m_array[temp]; };
}
private: template <typename T>
enum { SIZE = 8 }; class Queue {
T* m_array[SIZE];
int m_add, m_remove;
public:
}; Queue() { m_add = m_remove = 0; }
void enque( T* c ) {
int main( void ) { m_array[m_add] = c;
Queue<Giant> que; m_add = (m_add + 1) % SIZE;
Giant input[6], *bad_guy; }
T* deque() {
for (int i=0; i < 6; i++) int temp = m_remove;
que.enque( &input[i] ); m_remove = (m_remove + 1) % SIZE;
for (int i=0; i < 6; i++) {
return m_array[temp];
bad_guy = que.deque(); }
if (bad_guy->get_type() == Giant::Fee) private:
bad_guy->fee(); enum { SIZE = 8 };
else if (bad_guy->get_type() == Giant::Phi) T* m_array[SIZE];
bad_guy->phi(); int m_add, m_remove;
else if(bad_guy->get_type()==Giant::Pheaux) };
bad_guy->pheaux();
} int main( void ) {
cout << '\n';
}
Queue<Giant> que;
Command* input[] = {
// 0-fee 1-phi 2-pheaux 3-fee 4-phi 5- new Command( new Giant, &Giant::fee ),
pheaux new Command( new Giant, &Giant::phi ),
new Command( new Giant,&Giant::pheaux),
new Command( new Giant, &Giant::fee ),
new Command( new Giant, &Giant::phi ),
new Command( new Giant, &Giant::pheaux)
};

for (int i=0; i < 6; i++)


que.enque( input[i] );

for (int i=0; i < 6; i++)


que.deque()->execute();
cout << '\n';
}

// 0-fee 1-phi 2-pheaux 3-fee 4-phi


5-pheaux

1. Define a Command interface with a method signature like execute().


2. Create one or more derived classes that encapsulate some subset of the
following: a "receiver" object, the method to invoke, the arguments to pass.
3. Instantiate a Command object for each deferred execution request.
4. Pass the Command object from the creator (sender) to the invoker (receiver).
5. The invoker decides when to execute().
2. Le Iterator

Ce pattern est très utilisé et permet d’accéder à des éléments d’un agrégat séquentiellement. Il a
pour but premier de donner un moyen de parcourir et d’accéder aux éléments sans pour autant
exposer sa structure interne. Il peut également avoir différents moyens de lister le contenu (ordre
croissant/décroissant, insertion…) et permettre (si on le souhaite) d’accéder simultanément à la liste
avec différents moyens (croissant et décroissant en même temps par exemple).

Visit every element in an aggregate structure in a well-defined order and perform an action
on each element.

=> Defines a mechanism for enumerating elements of an aggregate without exposing the
representation (by supporting first(), next(), item(), isDone(), etc.)
E.g., pre/in/post-order traversals of tree.

L’iterator peut être utilisé pour accéder à une liste d’éléments suivant un moyen précis :

• Croissant
• Décroissant
• Insertion
• …

Il ser t aussi à appliquer une condition de parcours permettant d’apliquer un filtre sur la liste
Before After
//The class SomeClassWithData provides // Take traversal-of-a-collection
access to its internal data structure. functionality out of the collection and
Clients can accidently or maliciously trash promote it to "full object status". This
that data structure. simplifies the collection, allows many
traversals to be active simultaneously,
class SomeClassWithData { and decouples collection algorithms from
private TreeSet<Integer> m_data = collection data structures.
new TreeSet<Integer>();
class SomeClassWithData {
public void add( int in ) { private TreeSet<Integer> m_data =
m_data.add( in ); } new TreeSet<Integer>();
public Collection get_data() {
return m_data; } public class Iterator {
} private SomeClassWithData m_collection;
private java.util.Iterator m_it;
class IteratorDemo { private int m_current;
public static void main( String[] args ) public Iterator( SomeClassWithData in){
{ m_collection = in;
SomeClassWithData some_object = }
new SomeClassWithData(); public void first() {
for (int i=9; i > 0; --i) m_it =m_collection.m_data.iterator();
some_object.add( i ); next();
Collection data=some_object.get_data(); }
for(java.util.Iterator it= public void next() {
data.iterator(); it.hasNext(); ) try {
System.out.print( it.next() + " "); m_current = m_it.next();
System.out.println(); } catch (NoSuchElementException ex) {
m_current = -999;
// Do we really want a client to be able to }
// trash encapsulated state? }
data.clear(); public boolean is_done() {
data = some_object.get_data(); return m_current == -999;
System.out.println( "size of data is " }
+ data.size() ); public int current_item() {
} return m_current; }
} }

// 1 2 3 4 5 6 7 8 9 public void add( int in ) {


// size of data is 0 m_data.add( in );
SomeClassWithData
}
}//end of Iterator inner class

public Iterator create_iterator() {


return new Iterator( this );
}
}//end of SomeClassWithData class

class IteratorDemo {
public static void main(String[] args){
SomeClassWithData some_object = new
SomeClassWithData();
for (int i=9; i > 0; --i)
some_object.add( i );

// get_data() has been removed.


// Client has to use Iterator.
SomeClassWithData.Iterator it1 =
some_object.create_iterator();
SomeClassWithData.Iterator it2 =
some_object.create_iterator();
for(it1.first();!it1.is_done();
it1.next())
System.out.print(it1.current_item()
+ " " );
System.out.println();

// Two simultaneous iterations


for (it1.first(), it2.first();
!it1.is_done();
it1.next(), it2.next())
System.out.print(it1.current_item()
+ " " + it2.current_item() + " " );
System.out.println();
}
}

// 1 2 3 4 5 6 7 8 9
// 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8
9 9

1. Add a create_iterator() method to the "collection" class, and grant the


"iterator" class privileged access.
2. Design an "iterator" class that can encapsulate traversal of the "collection" class.
3. Clients ask the collection object to create an iterator object.
4. Clients use the first(), is_done(), next(), and current_item() protocol to
access the elements of the collection class.
3. Le ‘Observer ‘

Dans le cas générale, un Objet possède un état interne et fournit un ensemble de services en tant que
des méthodes. L’état de l’objet est décrit par des données membres.

Ce design pattern met en jeu deux Objets en intéractions : un objet ‘A’ possède une référence à un
Objet B pour utiliser ses services, ‘A’ doit être linker à l’objet ‘B’ (incude B.h) et rester coupler à
cet objet. La réponse du ‘Observer’ est effectivement comment découpler les deux classes ‘A’ et
‘B’ et surtout pouvoir réutiliser ‘A’ sans ramener l’objet ‘B’,

Sometimes, the state of the object 'A' may depend on the state of object 'B'. Whenever 'B'
changes, 'A' should recompute its state to remain in Sync with 'B'. The situation becomes more
complicated when a new Object 'C', which is dependent on the state of 'B' is brought into the
application. In short, the dependency and the coupling between the objects are increased and the
reusability is reduced. How can we make two or more independent objects work together without
knowing each other’s existence? The answer is The Observer Pattern.

• Define a one-to-many dependency between objects so that when one object changes state,
all its dependents are notified and updated automatically. [GoF, p293]
• Encapsulate the core (or common or engine) components in a Subject abstraction, and the
variable (or optional or user interface) components in an Observer hierarchy.
• The "View" part of Model-View-Controller.
GoF classifies the Observer Pattern as an Object Behavioral Pattern. The Observer Pattern is
intended to "Define a one-to-many dependency between objects so that when one object changes
state, all its dependents are notified and updated automatically". An object that is subjected to
change is called a Subject and an object that depends on the Subject's state is called an
Observer. In the above example, 'B' can be a Subject and Objects A and C can be Observers. A
Subject can have any number of Observers. When the Subject changes its state, it Notifies all the
Observers and the Observers query the Subject to maintain state consistency with the Subject.

Before After
// The number and type of "user interface" (or // The Subject class is now decoupled from the
dependent) objects is hard-wired in the number and type of Observer objects. The
Subject class. The user has no ability to client has asked for two DivObserver delegates
Affect this configuration. (each configured differently) and one
ModObserver delegate

class DivObserver { class Observer {


int m_div; public:
public: virtual void update( int value ) = 0;
DivObserver( int div ) { m_div = div; } };
void update( int val ) {
cout << val << " div " << m_div << " is " class Subject {
<< val / m_div << '\n'; int m_value;
} }; vector<Observer*> m_views;
public:
class ModObserver { void attach( Observer* obs ) {
int m_mod; m_views.push_back( obs );
public: }
ModObserver( int mod ) { m_mod = mod; } void set_val( int value ) {
void update( int val ) { m_value = value;
cout << val << " mod " << m_mod << " is " notify();
<< val % m_mod << '\n'; }
} }; void notify() {
for (int i=0; i < m_views.size();++i)
class Subject { m_views[i]->update( m_value );
int m_value; }
DivObserver m_div_obj; };
ModObserver m_mod_obj;
public: class DivObserver : public Observer {
Subject() : m_div_obj(4), m_mod_obj(3) { } int m_div;
void set_value( int value ) { Subject *m_pDocument;
m_value = value; public:
notify(); DivObserver( Subject* model, int div ) {
} model->attach( this );
void notify() { m_pDocument = model;
m_div_obj.update( m_value ); m_div = div;
m_mod_obj.update( m_value ); }
} }; /* virtual */ void update( int v ) {
cout << v << " div " << m_div << " is "
int main( void ) { << v / m_div << '\n';
Subject subj; }
subj.set_value( 14 ); };
}
class ModObserver : public Observer {
// 14 div 4 is 3 int m_mod;
// 14 mod 3 is 2 Subject *m_pDocument;
public:
ModObserver( Subject* model, int mod ) {
model->attach( this );
m_pDocument = model;
m_mod = mod;
}
/* virtual */ void update( int v ) {
cout << v << " mod " << m_mod << " is "
<< v % m_mod << '\n';
}
};

int main( void ) {


Subject subj;
DivObserver divObs1( &subj, 4 );
DivObserver divObs2( &subj, 3 );
ModObserver modObs3( &subj, 3 );
subj.set_val( 14 );
}

// 14 div 4 is 3
// 14 div 3 is 4
// 14 mod 3 is 2

1. Differentiate between the core (or independent) functionality and the optional (or
dependent) functionality.
2. Model the independent functionality with a "subject" abstraction.
3. Model the dependent functionality with an "observer" hierarchy.
4. The Subject is coupled only to the Observer base class.
5. The client configures the number and type of Observers.
6. Observers register themselves with the Subject.
7. The Subject broadcasts events to all registered Observers.
8. The Subject may "push" information at the Observers, or, the Observers may
"pull" the information they need from the Subject.

MFC uses a Document/View variant of the Observer Pattern. MFC's famous Document/View
architecture uses this variant. A document contains the data object and acts as a Subject. A
view is a window object through which the user updates the document and it acts as an
Observer. A document can have multiple views. Whenever the data in the document is
changed by one of the views, it updates the document by calling UpdateAllViews method,
with optional hint about the modification. To inform about the change to other views, the
document object calls OnUpdate method for each view attached to it (except the view that
called UpdateAllViews). Derived view classes can override the OnUpdate method and
update themselves by querying the data from the document.
4. Le ‘State ‘
Le Pattern State permet, lorsqu’un objet est altéré de changer son comportement. Le changement
d’état peut parfois poser des problèmes dans leurs gestions (interminables if – elseif, switch-case),
le Pattern State permet de pallier à ce problème de manière simple et rapide en associant a chaque
état élémentaire une classe qui sera responsable du traitement et de la transition.

Cependant il n’est pas toujours obligatoire de l’utiliser dans toutes les situations. Il faudra identifier
s’il sagit réellement d’un changement d’état qui doit être gérer.

State doit comporter des classes précises participantes à ce Pattern :

• Context
• State
• ConcreteState

On utilise State lorsque :


• Le comportement d'un objet dépend de son état, qui change à l'exécution.
• Les opérations sont constituées de parties conditionnelles de grande taille (case).

• La classe Client permet d’instancier un objet Context et d’en afficher son état suite à des
changements liés à l’appel de la méthode request() :

package state;

public class Client {

public static void main(String[] args) {


Context c = new Context(new ConcreteStateA() );
c.show();

c.request();
c.show();
c.request();
c.show();
}
}

• La classe Context comporte un attribut de type State et une méthode Request() qui change l’état.

package state;

public class Context {


private State state;
public Context(State state) {
this.state = state;
}
public void request() {
state.handle(this);
}
public void show() {
System.out.println("State: " + state );
}
public void setState(State state) {
this.state = state;
}
}

• La classe State est abstraite afin de forcer les classes qui la dérive de redéfinir la méthode
abstraite handle().

package state;

abstract class State {


abstract public void handle(Context context);
}

• La classe ConcreteStateA hérite de la classe State et redéfini la méthode handle().

package state;

public class ConcreteStateA extends State {


public void handle(Context context) {
context.setState(new ConcreteStateB());
}
}

• La classe ConcreteStateB procède selon le même procédé que la classe précédente mais effectue
le changement d’état dans le sens inverse.

package state;
public class ConcreteStateB extends State {
public void handle(Context context) {
context.setState(new ConcreteStateA());
}
}
Exemple d’editeur de texte

1. Identify an existing class, or create a new class, that will serve as the "state machine" from the
client's perspective. That class is the "wrapper" class.
2. Create a State base class that replicates the methods of the state machine interface. Each method
takes one additional parameter: an instance of the wrapper class. The State base class specifies
any useful "default" behavior.
3. Create a State derived class for each domain state. These derived classes only override the
methods they need to override.
4. The wrapper class maintains a "current" State object.
5. All client requests to the wrapper class are simply delegated to the current State object, and the
wrapper object's this pointer is passed.
6. The State methods change the "current" state in the wrapper object as appropriate.

Before After
// 3-speed ceiling fan state machine //The CeilingFanPullChain class is now a
wrapper that delegates to its
// Not good: unwieldy "case" statement current_state reference. Each clause from
the "before" case statement is now
class CeilingFanPullChain { captured in a State derived class.
private int m_current_state;
public m_value; // For this simple domain, the State
pattern is probably over-kill.
public CeilingFanPullChain() {
m_current_state = 0; class CeilingFanPullChain {
} private State m_current_state;
public void pull(char c,
CeilingFanPullChain pContext) { public CeilingFanPullChain() {
if (m_current_state == 0) { m_current_state = new Off();
m_current_state = 1; }
System.out.println( " low speed ",c); public void set_state( State s ) {
m_current_state = s;
}else if (m_current_state == 1) { }
m_current_state = 2; public void pull(char ev) {
System.out.println(" medium speed"); m_current_state.pull( ev, this );
}
} else if (m_current_state == 2) { }
m_current_state = 3;
System.out.println(" high speed" ); interface State {
void pull(char ev, CeilingFanPullChain
} else { wrapper);
m_current_state = 0; }
System.out.println(" turning off");
} class Off implements State {
} public void pull(char ev,
} CeilingFanPullChain wrapper )
{
public class StateDemo { wrapper.set_state( new Low() );
public static void main( String[] args ){ System.out.println(" low speed",ev
CeilingFanPullChain chain = new );
CeilingFanPullChain(); }
while (true) { }
System.out.print( "Press " );
Char c = get_line(); class Low implements State {
chain.pull(c); public void pull(char ev,
} CeilingFanPullChain wrapper )
} {
static String get_line() { wrapper.set_state( new Medium() );
BufferedReader in = new BufferedReader( System.out.println(" medium
new InputStreamReader( System.in )); speed",ev);
String line = null; }
try { }
line = in.readLine(); class Medium implements State {
} catch (IOException ex) { public void pull(char ev,
ex.printStackTrace(); CeilingFanPullChain wrapper ) {
} wrapper.set_state( new High() );
return line; System.out.println( " high speed",ev
} );
} }
}
// Press class High implements State {
// low speed public void pull(char ev,
// Press CeilingFanPullChain wrapper ) {
// medium speed wrapper.set_state(new Off() );
// Press System.out.println(" turning
// high speed off",ev);
// Press }
// turning off }
// Press
// low speed public class StateDemo {
// Press public static void main( String[] args )
// medium speed {
// Press CeilingFanPullChain chain = new
// high speed CeilingFanPullChain();
// Press while (true) {
// turning off System.out.print( "Press " );
Char c = get_line();
chain.pull(c);
}
}
static String get_line()
{
BufferedReader in= new BufferedReader(
new InputStreamReader(System.in);
String line = null;
try {
line = in.readLine();
} catch (IOException ex) {
ex.printStackTrace();
}
return line;
}
}
5. Le ‘Chain Of Responsability ‘

• Le principe de ce pattern est de découpler celui qui envoie la requête et celui qui la tarite.
Les entités de traitement auront la chance de se mettre en chaine pour que chacune soit
responsable d’un bout de traitement sur la requête. Le data en transit entre les entités est le
seul lien qui existe. Le flus de la requête passera d’un handler ason suivant un ordre
séquentiel choisit par l’application dynamiquement.
• L’application peut même choisr plusieurs chaines différentes mettant en jeu les m^mes
entités ou des entités différentes

Request
Client

Processing Processing Processing Processing


Element Element Element Element

Request
Client Processing Processing Processing
Element Element Element
import java.util.*;
abstract class Logger
{
public static int ERR = 3;
public static int NOTICE = 5;
public static int DEBUG = 7;
protected int mask;

// The next element in the chain of responsibility


protected Logger next;
public Logger setNext( Logger l)
{
next = l;
return l;
}
public void message( String msg, int priority )
{
if ( priority <= mask )
{
writeMessage( msg );
}
if ( next != null )
{
next.message( msg, priority );
}
}
abstract protected void writeMessage( String msg );
}

class StdoutLogger extends Logger


{
public StdoutLogger( int mask ) { this.mask = mask; }

protected void writeMessage( String msg )


{
System.out.println( "Writing to stdout: " + msg );
}
}
class EmailLogger extends Logger
{

public EmailLogger( int mask ) { this.mask = mask; }

protected void writeMessage( String msg )


{
System.out.println( "Sending via email: " + msg );
}
}
class StderrLogger extends Logger
{
public StderrLogger( int mask ) { this.mask = mask; }
protected void writeMessage( String msg )
{
System.err.println( "Sending to stderr: " + msg );
}
}
public class ChainOfResponsibilityExample
{
public static void main( String[] args )
{
// Build the chain of responsibility
Logger l,l1;
l1 = l = new StdoutLogger( Logger.DEBUG );
l1 = l1.setNext(new EmailLogger( Logger.NOTICE ));
l1 = l1.setNext(new StderrLogger( Logger.ERR ));

// Handled by StdoutLogger
l.message( "Entering function y.", Logger.DEBUG );

// Handled by StdoutLogger and EmailLogger


l.message( "Step1 completed.", Logger.NOTICE );

// Handled by all three loggers


l.message( "An error has occurred.", Logger.ERR );
}
}
Before After
// Before - the client is responsible for // After - the client submits each request to
stepping the
// through the "list" of Handler objects, and // "chain" abstraction and is decoupled from
deter- all sub-sequent processing.
// mining when the request has been handled.
// class Handler {
class Handler { private static java.util.Random s_rn =
private static java.util.Random s_rn = new java.util.Random();
new java.util.Random(); private static int s_next = 1;
private static int s_next = 1; private int m_id = s_next++;
private int m_id = s_next++; private Handler m_next;

public boolean handle( int num ) { public void add( Handler next ) {
if (s_rn.nextInt(4) != 0) { if (m_next == null)
System.out.print( m_id + "-busy " ); m_next = next;
return false; else
} m_next.add( next );
System.out.println( m_id + "-handled-" + }
num ); public void wrap_around( Handler root ) {
return true; if (m_next == null)
} } m_next = root;
else
public class ChainDemo { m_next.wrap_around( root );
public static void main( String[] args ) { }
Handler[] nodes = { new Handler(), new public void handle( int num ) {
Handler(),new Handler(), new Handler() }; if (s_rn.nextInt(4) != 0) {
for (int i=1, j; i < 10; i++) { System.out.print( m_id + "-busy " );
j = 0; m_next.handle( num );
while ( ! nodes[j].handle( i )) } else
j = (j+1) % nodes.length; System.out.println( m_id + "-handled-
} } } " + num );
} }
// 1-busy 2-busy 3-busy 4-busy 1-busy 2-
handled-1 public class ChainDemo {
// 1-busy 2-busy 3-handled-2 public static void main( String[] args ) {
// 1-busy 2-busy 3-busy 4-handled-3 Handler chain_root = new Handler();
// 1-busy 2-busy 3-busy 4-busy 1-busy 2- chain_root.add( new Handler() );
busy 3-handled-4 chain_root.add( new Handler() );
// 1-busy 2-busy 3-handled-5 chain_root.add( new Handler() );
// 1-handled-6
// 1-busy 2-handled-7 chain_root.wrap_around( chain_root );
// 1-busy 2-busy 3-busy 4-busy 1-busy 2- for (int i=1; i < 10; i++)
busy 3-busy 4-handled-8 chain_root.handle( i );
// 1-busy 2-handled-9 } }

// 1-busy 2-busy 3-handled-1


// 1-busy 2-busy 3-busy 4-busy 1-handled-2
// 1-busy 2-busy 3-busy 4-busy 1-busy 2-
busy
// 3-busy 4-busy 1-handled-3
// 1-busy 2-handled-4
// 1-busy 2-busy 3-busy 4-handled-5
// 1-busy 2-busy 3-busy 4-busy 1-busy 2-
handled-6
// 1-busy 2-handled-7
// 1-handled-8
// 1-busy 2-busy 3-handled-9
6. Le Visitor

Le modèle ‘Visitor’ est utile dans la conception d’une opération à travers une collection hétérogène
d'objets disposés en hiérarchie de classe. Le modèle de ‘Visitor’ permet à l'opération d’être défini
sans changer la classe de n'importe quel des objets dans la collection. Pour accomplir ceci, le
modèle de Visitor suggère la définition de l'opération dans une classe séparée référée comme une
classe de visiteur. Ceci sépare l'opération de la collection d'objet sur laquelle il fonctionne.
Pour chaque nouvelle opération à définir, une nouvelle classe de visiteur est créée. Puisque
l'opération est exécutée à travers une série d'objets, le visiteur a besoin d'une façon d' accéder aux
membres publics de ces objets.

Visitor's primary purpose is to abstract functionality that can be applied to an aggregate hierarchy of
"element" objects. The approach encourages designing lightweight Element classes - because
processing functionality is removed from their list of responsibilities. New functionality can easily
be added to the original inheritance hierarchy by creating a new Visitor subclass.

Visitor implements "double dispatch". OO messages routinely manifest "single dispatch" - the
operation that is executed depends on: the name of the request, and the type of the receiver. In
"double dispatch", the operation executed depends on: the name of the request, and the type of
TWO receivers (the type of the Visitor and the type of the element it visits).

The implementation proceeds as follows. Create a Visitor class hierarchy that defines a pure virtual
visit() method in the abstract base class for each concrete derived class in the aggregate node
hierarchy. Each visit() method accepts a single argument - a pointer or reference to an original
Element derived class.

Each operation to be supported is modelled with a concrete derived class of the Visitor hierarchy.
The visit() methods declared in the Visitor base class are now defined in each derived subclass by
allocating the "type query and cast" code in the original implementation to the appropriate
overloaded visit() method.

Add a single pure virtual accept() method to the base class of the Element hierarchy. accept() is
defined to receive a single argument - a pointer or reference to the abstract base class of the Visitor
hierarchy.

Each concrete derived class of the Element hierarchy implements the accept() method by simply
calling the visit() method on the concrete derived instance of the Visitor hierarchy that it was
passed, passing its "this" pointer as the sole argument.

Everything for "elements" and "visitors" is now set-up. When the client needs an operation to be
performed, he creates an instance of the Vistor object, calls the accept() method on each Element
object, and passes the Visitor object. The accept() method causes flow of control to find the correct
Element subclass. Then when the visit() method is invoked, flow of control is vectored to the
correct Visitor subclass. accept() dispatch plus visit() dispatch equals double dispatch.
The Visitor pattern makes adding new operations (or utilities) easy - simply add a new Visitor
derived class. But, if the subclasses in the aggregate node hierarchy are not stable, keeping the
Visitor subclasses in sync requires a prohibitive amount of effort.

An acknowledged objection to the Visitor pattern is that is represents a regression to functional


decomposition - separate the algorithms from the data structures. While this is a legitimate
interpretation, perhaps a better perspective/rationale is the goal of promoting non-traditional
behavior to full object status.

Client Visitor

VisitConcreteElementA(ConcreteElementA)
VisitConcreteElementB(ConcreteElementB)

ConcreteVisitor1 ConcreteVisitor2

VisitConcreteElementA(ConcreteElementA) VisitConcreteElementA(ConcreteElementA)
VisitConcreteElementB(ConcreteElementB) VisitConcreteElementB(ConcreteElementB)

ObjectStructure
Element

Accept(Visitor)

ConcreteElementA ConcreteElementB

Accept(Visitor V) Accept(Visitor V)
OperationA() OperationB()

v->VisitConcreteElementA(this) v->VisitConcreteElementB(this)
Before After
// The interface for "operations" are //The Color hierarchy specifies a single
specified in the Color base class and "accept()" method, and then the previous
implemented in the Color derived classes. "count()" and "call()" methods are
done based on the type of both objects. implemented as Visitor derived classes.
When accept() is called on a Color
class Color { object, that is the first dispatch. When
public: visit() is called on a Visitor object,
virtual void count() = 0; that is the second dispatch; and the
virtual void call() = 0; "right thing" can be
static void report_num() { class Color {
cout << "Reds " << s_num_red public:
<< ", Blus " << s_num_blu << virtual void accept( class Visitor* )
'\n'; = 0;
} };
protected:
static int s_num_red, s_num_blu; class Red : public Color {
}; public:
int Color::s_num_red = 0; /*virtual*/ void accept( Visitor* );
int Color::s_num_blu = 0; void eye() { cout << "Red::eye\n"; }
};
class Red : public Color { class Blue : public Color {
public: public:
void count() { ++s_num_red; } /*virtual*/ void accept( Visitor* );
void call() { eye(); } void sky() { cout << "Blu::sky\n"; }
void eye() { cout << "Red::eye\n"; } };
};
class Blu : public Color { void Red::accept( Visitor* v )
public: { v->visit( this ); }
void count() { ++s_num_blu; } void Blue::accept( Visitor* v )
void call() { sky(); } { v->visit( this ); }
void sky() { cout << "Blu::sky\n"; }
}; class Visitor {
public:
int main( void ) { virtual void visit( Red* ) = 0;
Color* set[] = { new Red, new Blu, new virtual void visit( Blue* ) = 0;
Blu, };
new Red, new Red, 0 };
for (int i=0; set[i]; ++i) { class CountVisitor : public Visitor {
set[i]->count(); public:
set[i]->call(); CountVisitor()
} { m_num_red = m_num_blu = 0; }
Color::report_num(); /*virtual*/
} void visit( Red* ) { ++m_num_red; }
/*virtual*/
// Red::eye void visit( Blu* ) { ++m_num_blu; }
// Blu::sky void report_num() {
// Blu::sky cout << "Reds " << m_num_red
// Red::eye << ", Blus " << m_num_blu << '\n';
// Red::eye }
// Reds 3, Blus 2 private:
int m_num_red, m_num_blu;
};

class CallVisitor : public Visitor {


public:
/*virtual*/
void visit( Red* r ) { r->eye(); }
/*virtual*/
void visit( Blu* b ) { b->sky(); }
};

int main( void ) {


Color* set[] = {new Red, new Blu, new
Blu, new Red, new Red, 0 };

CountVisitor count_operation;
CallVisitor call_operation;
for (int i=0; set[i]; i++) {
set[i]->accept( &count_operation );
set[i]->accept( &call_operation );
}
count_operation.report_num();
}

// Red::eye
// Blu::sky
// Blu::sky
// Red::eye
// Red::eye
// Reds 3, Blus 2
1. Confirm that the current hierarchy (known as the Element hierarchy) will be
fairly stable and that the public interface of these classes is sufficient for the
access the Visitor classes will require.
2. Create a Visitor base class with a visit(ElementXxx) method for each
Element derived type.
3. Add an accept(Visitor) method to the Element hierarchy. The
implementation in each Element derived class is always the same – accept(
Visitor v ) { v.visit( this ); }. Because of cyclic dependencies, the
declaration of the Element and Visitor classes will need to be interleaved.
4. The Element hierarchy is coupled only to the Visitor base class, but the
Visitor hierarchy is coupled to each Element derived class. If the stability of
the Element hierarchy is low, and the stability of the Visitor hierarchy is
high; consider swapping the “roles” of the two hierarchies.
5. Create a Visitor derived class for each "operation" to be performed on
Element objects. visit() implementations will rely on the Element's public
interface.
6. The client creates Visitor objects and passes each to Element objects by
calling accept().
7. Le Memento

Le patron memento est un patron qui fournit la manière de renvoyer un objet à un état précédent
(retour arrière) sans violer le principe d'encapsulation.

Le mémento est utilisé par deux objets: le créateur (originator) et le gardien (care Taker). Le
créateur est un objet ayant un état interne (état à sauvegarder). Le gardien agira sur le créateur, tout
en conservant la possibilité de revenir en arrière. Le gardien demande alors au créateur l'objet
mémento. Il effectue l'opération (ou séquence d'opérations) souhaitée. Afin de permettre le retour
arrière avant les opérations, le mémento est retourné au créateur. L'objet mémento est opaque :le
gardien ne peut, ou ne devrait pas, le modifier. Lors d'utilisation de ce patron, une attention toute
particulière doit être prise de vérifier si le créateur modifie d'autres objets ou ressources - le patron
mémento doit opérer sur un seul objet.

The Memento captures and externalizes an object's internal state so that


the object can later be restored to that state. This pattern is common
among do-it-yourself mechanics restoring
Exemple Java

import java.util.*;
class Caretaker {
class Originator { private ArrayList savedStates = new
private String state; ArrayList();

public void set(String state) { public void addMemento(Object m) {


System.out.println("Originator: etat savedStates.add(m); }
affecte a: "+state); public Object getMemento(int index) {
this.state = state; return savedStates.get(index); }
} }

public Object saveToMemento() { class MementoExample {


System.out.println("Originator: public static void main(String[] args) {
sauvegarde dans le memento."); Caretaker caretaker = new Caretaker();
return new Memento(state);
} Originator originator = new
public void restoreFromMemento(Object m) { Originator();
if (m instanceof Memento) { originator.set("State1");
Memento memento = (Memento)m; originator.set("State2");
state = memento.getSavedState(); caretaker.addMemento(
System.out.println("Originator: Etat originator.saveToMemento() );
après restauration: "+state); originator.set("State3");
} caretaker.addMemento(
} originator.saveToMemento() );
originator.set("State4");
private static class Memento {
private String state; originator.restoreFromMemento(
caretaker.getMemento(1) );
public Memento(String stateToSave) {
state = stateToSave; }
public String getSavedState() { return
state; }
}
}
Exemple C++

// "check point" and "roll back" a Stack

class Memento {
friend class Stack;
int *m_items, m_num;
Memento( int* arr, int num ) {
m_items = new int[m_num = num];
for (int i=0; i < m_num; i++)
m_items[i] = arr[i];
}
public:
~Memento() { delete m_items; }
};

class Stack { //originator


int m_items[10], m_sp;
public:
Stack() { m_sp = -1; }
void push( int in ) { m_items[++m_sp] = in; }
int pop() { return m_items[m_sp--]; }
bool is_empty() { return (m_sp == -1); }
Memento* check_point() {
return new Memento( m_items, m_sp+1 );
}
void roll_back( Memento* mem ) {
m_sp = mem->m_num-1;
for (int i=0; i < mem->m_num; ++i)
m_items[i] = mem->m_items[i];
}
friend ostream& operator<< ( ostream& os, const Stack& s ) {
string buf( "[ " );
for (int i=0; i < s.m_sp+1; i++) {
buf += s.m_items[i]+48;
buf += ' ';
}
buf += ']';
return os << buf;
}
};

int main( void ) { // 1. main() is the "caretaker"

Stack s;
for (int i=0; i < 5; i++)
s.push( i );
cout << "stack is " << s << '\n';
Memento* first = s.check_point();
for (int i=5; i < 10; i++)
s.push( i );
cout << "stack is " << s << '\n';
Memento* second = s.check_point();
cout << "popping stack: ";
while ( ! s.is_empty())
cout << s.pop() << ' ';
cout << '\n';
cout << "stack is " << s << '\n';
s.roll_back( second );
cout << "second is " << s << '\n';
s.roll_back( first );
cout << "first is " << s << '\n';
cout << "popping stack: ";
while ( ! s.is_empty())
cout << s.pop() << ' ';
cout << '\n';
delete first; delete second;
}

// stack is [ 0 1 2 3 4 ]
// stack is [ 0 1 2 3 4 5 6 7 8 9 ]
// popping stack: 9 8 7 6 5 4 3 2 1 0
// stack is [ ]
// second is [ 0 1 2 3 4 5 6 7 8 9 ]
// first is [ 0 1 2 3 4 ]
// popping stack: 4 3 2 1 0
1. Identify the roles of “caretaker” and “originator”.
2. Create a Memento class and declare the originator a friend.
3. Caretaker knows when to "check point" the originator.
4. Originator creates a Memento and copies its state to that Memento.
5. Caretaker holds on to (but cannot peek into) the Memento.
6. Caretaker knows when to "roll back" the originator.
7. Originator reinstates itself using the saved state in the Memento

Command and Memento act as magic tokens to be passed around and


invoked at a later time. In Command, the token represents a request; in
Memento, it represents the internal state of an object at a particular time.
Polymorphism is important to Command, but not to Memento because its
interface is so narrow that a memento can only be passed as a value.
[GoF, p346]
ANNEXE
Gestion d’achat dans une structure hiérarchique
Table 21.1 Levels of PR Authorization
Management Level Authorization Limit
Branch Manager $25,000
Regional Director $100,000
Vice President $200,000
President and COO $400,000

class BranchManager {
static double LIMIT = 25000;


}//End of class
class RegionalDirector {
static double LIMIT = 100000;


}//End of class
class VicePresident {
static double LIMIT = 200000;


}//End of class
class PresidentCOO {
static double LIMIT = 400000;


}//End of class

PurchaseRequest

ID:int
description:String
amount:double
getAmount():double

class PurchaseRequest {
private int ID;
private String description;
private double amount;
public PurchaseRequest(int id, String desc, double amt) {
ID = id;
description = desc;
amount = amt;
}
public double getAmount() {
return amount;
}
public String toString() {
return ID + ":" + description;
}
}
public abstract class PRHandler {
private PRHandler nextHandler;
private String handlerName;
public PRHandler(String name) {
handlerName = name;
}
public String getName() {
return handlerName;
}
public abstract boolean authorize(PurchaseRequest request);

public PRHandler getNextHandler() {


return nextHandler;
}
public void setNextHandler(PRHandler handler) {
nextHandler = handler;
};
}
class BranchManager extends PRHandler {
static double LIMIT = 25000;
public BranchManager(String name) {
super(name);
}
public boolean authorize(PurchaseRequest request) {
double amount = request.getAmount();
if (amount <= LIMIT) {
System.out.println(" Branch Manager " + getName() +
" has authorized the PR - " + request);
return true;
} else {
//forward the request to the next handler
return getNextHandler().authorize(request);
}
}
}//End of class
class RegionalDirector extends PRHandler {
static double LIMIT = 100000;
public RegionalDirector(String name) {
super(name);
}
public boolean authorize(PurchaseRequest request) {
double amount = request.getAmount();
if (amount <= LIMIT) {
System.out.println(" Regional Director " + getName() +
" has authorized the PR - " +request);
return true;
} else {
//forward the request to the next handler
return getNextHandler().authorize(request);
}
}
}//End of class
class VicePresident extends PRHandler {
static double LIMIT = 200000;
public VicePresident(String name) {
super(name);
}
public boolean authorize(PurchaseRequest request) {
double amount = request.getAmount();
if (amount <= LIMIT) {
System.out.println(" V.P. " + getName() +
" has authorized the PR - " + request);
return true;
} else {
//forward the request to the next handler
return getNextHandler().authorize(request);
}
}
}//End of class
class PresidentCOO extends PRHandler {
static double LIMIT = 400000;
public PresidentCOO(String name) {
super(name);
}
public boolean authorize(PurchaseRequest request) {
double amount = request.getAmount();
if (amount <= LIMIT) {
System.out.println(" President & COO " + getName() +
" has authorized the PR - " + request);
return true;
} else {
System.out.println("PR - " + request +" couldn't be
authorized.\n " +
"Executive Board needs to be " +
"consulted for approval \n" +
"reason: Amount too large");
return false;
}
}
}//End of class
public class PRManager {
private BranchManager branchManager;
private RegionalDirector regionalDirector;
private VicePresident vicePresident;
private PresidentCOO coo;
public static void main(String[] args) {
PRManager manager = new PRManager();
manager.createAuthorizationFlow();

PurchaseRequest request = new PurchaseRequest(1, "Office


Supplies”,10000);
manager.branchManager.authorize(request);

request = new PurchaseRequest(2, "HardWare Procurement”,175000);


manager.branchManager.authorize(request);

request = new PurchaseRequest(3, "AD Campaign”,800000);


manager.branchManager.authorize(request);
}
public void createAuthorizationFlow() {
branchManager = new BranchManager("Robin");
regionalDirector = new RegionalDirector("Oscar");
vicePresident = new VicePresident("Kate");
coo = new PresidentCOO("Drew");

branchManager.setNextHandler(regionalDirector);
regionalDirector.setNextHandler(vicePresident);
vicePresident.setNextHandler(coo);
}
}//End of class
Output:

Branch Manager Robin has authorized the PR - 1:Office Supplies


V.P. Kate has authorized the PR - 2:HardWare Procurement
PR - 3:AD Campaign couldn't be authorized.
Executive Board needs to be consulted for approval
reason: Amount too large
Study case
File system with 7 GoF patterns

// Composite --------------- directories that contain files, each of which could


// be a directory
// Proxy ------------------- Unix logical link
// Chain of Responsibility - pointer back to the parent directory to create
// "fully qualified path name"
// Iterator ---------------- use iterator to control the traversal of a Composite
// Visitor ----------------- create a recursive Unix "du" utility without
// modifying the original Composite hierarchy
// Observer ---------------- register one-to-many "listeners" for File write()
// events
// Command ----------------- register one-to-one callback object for File write()
// events

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <ctime>
using namespace std;

class AbsFile {
string name;
class Directory* parent;
protected:
Directory* getParent() { return parent; }
public:
AbsFile( string n, Directory* p ) : name( n ) { parent = p; }
virtual ~AbsFile() { }
string getName() { return name; }
virtual string getFullName();
virtual void ls( string = "" ) = 0;
virtual void populate( vector<AbsFile*>& list ) { list.push_back( this ); }
virtual void streamOut( ostream& = cout ) = 0;
virtual void accept( class Visitor& ) = 0;
};

class Observer {
public:
virtual void fileChanged( class File* ) = 0;
};

class Command {
public:
virtual void execute( File* ) = 0;
};
class NullCommand : public Command {
public:
static Command& inst() { static NullCommand nc; return nc; }
/*virtual*/ void execute(File*) { }
};

class File : public AbsFile {


stringstream contents;
static vector<Observer*> observers;
Command& cmd;
public:
File( string n, Directory* p, Command& c = NullCommand::inst() )
: AbsFile( n,p ), cmd( c ) { }
~File() { cout << 'X' << getName() << ' '; }
/*virtual*/ void ls( string s = "" ) {
if (s == "") cout << getName() << ' ';
else cout << getFullName() << '\n';
}
void write( string str ) {
contents << str;
notify();
cmd.execute( this );
}
/*virtual*/ void streamOut( ostream& os = cout ) {
os << contents.str() << '\n';
}
int getSize() { return contents.str().size(); }
/*virtual*/ void accept( Visitor& v );
static void registerObserver( class Observer* o ) { observers.push_back( o );
}
void notify() {
for (int i=0; i < observers.size(); i++)
observers[i]->fileChanged( this );
} };
vector<Observer*> File::observers;

class Directory : public AbsFile {


vector<AbsFile*> children;
string name;
public:
Directory( string n, Directory* p=0 ) : AbsFile( n,p ) { }
~Directory() {
cout << 'X' << getName() << ' ';
for (int i=0; i < children.size(); i++)
delete children[i];
}
void add( AbsFile* ele ) { children.push_back( ele ); }
/*virtual*/ void ls( string s = "" ) {
if (s == "") cout << getName() << "/ ";
else cout << getFullName() << '\n';
for (int i=0; i < children.size(); i++)
children[i]->ls( s );
}
/*virtual*/ string getFullName() { return AbsFile::getFullName() + '/'; }
class Iterator* createIterator();
/*virtual*/ void populate( vector<AbsFile*>& list ) {
AbsFile::populate( list );
for (int i=0; i < children.size(); i++)
children[i]->populate( list );
}
/*virtual*/ void streamOut( ostream& os = cout ) {
os << "directory - " << getName() << '\n';
}
/*virtual*/ void accept( Visitor& v );
};

/*virtual*/ string AbsFile::getFullName() {


string str;
Directory* p;
if (p = getParent()) str = p->getFullName();
return str + getName();
}

class Link : public AbsFile {


AbsFile* wrapped;
public:
Link( string n, AbsFile* w, Directory* p ) : AbsFile(n,p) { wrapped = w; }
~Link() { cout << 'X' << getName() << ' '; }
/*virtual*/ void ls( string s = "" ) {
if (s == "") {
cout << getName() << "* ";
wrapped->ls();
} else {
cout << getFullName() << " => ";
cout << wrapped->getFullName() << '\n';
} }
/*virtual*/ string getFullName() { return AbsFile::getFullName() + '*'; }
/*virtual*/ void streamOut( ostream& os = cout ) {
os << "link - " << getName() << '\n';
}
/*virtual*/ void accept( Visitor& v );
};

class Iterator {
Directory* container;
vector<AbsFile*> list;
int index;
public:
Iterator( Directory* c ) { container = c; }
void first() {
list.resize( 0 );
container->populate( list );
index = 0;
}
void next() { index++; }
bool isDone() { return index == list.size(); }
AbsFile* currentItem() { return list[index]; }
};

Iterator* Directory::createIterator() { return new Iterator( this ); }

class Visitor { public:


virtual void visit( File* ) = 0;
virtual void visit( Directory* ) = 0;
virtual void visit( Link* ) = 0;
};

/*virtual*/ void File::accept( Visitor& v ) { v.visit( this ); }


/*virtual*/ void Directory::accept( Visitor& v ) { v.visit( this ); }
/*virtual*/ void Link::accept( Visitor& v ) { v.visit( this ); }

class DU : public Visitor {


int numDir, numLink, numFile, sizeFile;
public:
DU() { numDir = numLink = numFile = sizeFile = 0; }
/*virtual*/ void visit( class File* f ) { numFile++;
sizeFile += f->getSize(); }
/*virtual*/ void visit( class Directory* ) { numDir++; }
/*virtual*/ void visit( class Link* ) { numLink++; }
void report() {
cout << "du: directories-" << numDir << ", links-" << numLink;
cout << ", files-" << numFile << ", size-" << sizeFile << '\n';
} };

class NameObserver : public Observer {


public:
/*virtual*/ void fileChanged( File* f ) {
time_t t;
time( &t );
string str( asctime(localtime(&t)) );
str.resize( str.size()-1 );
cout << f->getName() << " changed - " << str << '\n';
} };

class ContentObserver : public Observer {


public:
/*virtual*/ void fileChanged( File* f ) {
stringstream ss;
f->streamOut( ss );
cout << f->getName() << " content - " << ss.str();
} };

class ContentCommand : public Command {


public:
static Command& inst() { static ContentCommand cc; return cc; }
/*virtual*/ void execute( File* f ) {
stringstream ss;
f->streamOut( ss );
cout << "ContentCommand - " << f->getName() << " - " << ss.str();
} };

Directory* initialize() {
Directory* root = new Directory( "root" );
root->add( new File( "core", root ) );
Directory* usr = new Directory( "usr", root );
root->add( usr );
File* adm = new File( "adm", usr );
usr->add( adm );
Directory* local = new Directory( "local", usr );
usr->add( local );
local->add( new File( "bin", local ) );
usr->add( new File( "var", usr ) );
Directory* home = new Directory( "home", root );
root->add( home );
home->add( new Link( "alias", adm, home ) );
File* sam = new File( "sam", home, ContentCommand::inst() );
home->add( sam );
home->add( new File( "sue", home ) );
root->add( new Link( "aka", local, root ) );
root->add( new File( "end", root ) );
return root;
}

void main( void ) {


////////// Composite, Proxy //////////
Directory* root = initialize();
root->ls(); cout << '\n';

////////// Chain //////////


root->ls( "-l" ); cout << '\n';

////////// Iterator //////////


Iterator* it = root->createIterator();
for (it->first(); ! it->isDone(); it->next())
cout << it->currentItem()->getName() << ' ';
delete it; cout << "\n\n";
it = root->createIterator();
for (it->first(); ! it->isDone(); it->next())
cout << it->currentItem()->getFullName() << '\n';
delete it; cout << '\n';

////////// Observer //////////


File::registerObserver( new NameObserver() );
File::registerObserver( new ContentObserver() );

////////// Observer //////////


it = root->createIterator();
for (it->first(); ! it->isDone(); it->next())
if (it->currentItem()->getName() == "adm")
break;
File* f = dynamic_cast<File*>( it->currentItem() );
f->write( "adm line 1\nadm line 2" );
f->streamOut(); cout << '\n';

////////// Observer, Command //////////


for ( ; ! it->isDone(); it->next())
if (it->currentItem()->getName() == "sam")
break;
f = dynamic_cast<File*>( it->currentItem() );
f->write( "sam single line" );
f->streamOut();
delete it; cout << '\n';

////////// Visitor //////////


DU duObject;
it = root->createIterator();
for (it->first(); ! it->isDone(); it->next())
it->currentItem()->accept( duObject );
duObject.report();
delete it; cout << '\n';

delete root; cout << '\n';


}

// root/ core usr/ adm local/ bin var home/ alias* adm sam sue aka* local/ bin
end
//
// root/
// root/core
// root/usr/
// root/usr/adm
// root/usr/local/
// root/usr/local/bin
// root/usr/var
// root/home/
// root/home/alias* => root/usr/adm
// root/home/sam
// root/home/sue
// root/aka* => root/usr/local/
// root/end
//
// root core usr adm local bin var home alias sam sue aka end
//
// root/
// root/core
// root/usr/
// root/usr/adm
// root/usr/local/
// root/usr/local/bin
// root/usr/var
// root/home/
// root/home/alias*
// root/home/sam
// root/home/sue
// root/aka*
// root/end
//
// adm changed - Thu Dec 28 11:20:26 2000
// adm content - adm line 1
// adm line 2
// adm line 1
// adm line 2
//
// sam changed - Thu Dec 28 11:20:26 2000
// sam content - sam single line
// ContentCommand - sam - sam single line
// sam single line
//
// du: directories-4, links-2, files-7, size-36
//
// Xroot Xcore Xusr Xadm Xlocal Xbin Xvar Xhome Xalias Xsam Xsue Xaka Xend