Les designs patterns présentés autrement. Du besoin vers le pattern pour avoir un cas concret d'application et pour mieux comprendre l'application.
Les étudiants ont aimé la démarche et leurs retours étaient positifs. Donc j'ai décidé de le partager avec vous.
J'attends vos retours aussi ;)
Enjoy :)
5. Design pattern - Définition
• Modèle de conception
• Décrit les cas d’utilisation
• Sert d’abstraction au modèle d’implémentation
• Intension, motivation, solution
• Une solution à un problème générique
6. Premier exemple
• Projet à récupérer de GitHub
• https://github.com/iAmDorra/DesignPatterns
Folder
+ Name : String
File
+ Name : String
contains
contains
7. Solution & Projet
• Définir le projet (ou les) de
démarrage
• Références d’un projet
• Un autre projet
• Une librairie du Framework
• Un composant COM
• …
Project 1
Project 2
References
Solution
Project 3
References
Project 4
2
1
1'
3
References
x Build order
8. Premier exemple
• Contexte :
• Un dossier peut contenir des fichiers et des dossiers
Folder
+ Name : String
File
+ Name : String
contains
contains
9. Premier exemple
• Inconvénients :
• Pour connaitre le contenu de Folder, il faut parcourir les deux listes
Folder
+ Name : String
File
+ Name : String
contains
contains
10. Premier exemple
• Pour connaitre le contenu de Folder, il faut parcourir une seule
collection
Folder
+ Name : String
File
+ Name : String
<<Interface>>
IElement
+ Name : String
11. Premier exemple
• Caractéristiques
• Eviter la duplication
• Extensible
=> Pattern « Composite »
Folder
+ Name : String
File
+ Name : String
contains
contains
Folder
+ Name : String
File
+ Name : String
<<Interface>>
IElement
+ Name : String
12. Exercice
• Ajouter un lien (Link)
• Un lien peut référencer un fichier ou un dossier (pas les deux)
• Un dossier peut contenir des liens
Folder
+ Name : String
File
+ Name : String
<<Interface>>
IElement
+ Name : String
14. SOLID
• S RP : Single Responsibility Principle
• O CP : Open Close Principle
• L SP : Liskov Substitution Principle
• I SP : Interface Separation Principle
• D IP : Dependency Inversion Principle
15. Classification des patterns
• GoF (Gang of four) classification
• Creational patterns (de construction)
• Behavior patterns (de comportement)
• Structural patterns (de structuration)
17. Composite
• Permettre à un client d’interagir avec un objet sans connaitre la
profondeur de sa composition
• Limiter les dépendances entre les classes concrètes
• Eviter la duplication
• Extensible
Folder
+ Name : String
File
+ Name : String
<<Interface>>
IElement
+ Name : String
18. Composite – use cases
• Il est nécessaire de représenter des hiérarchies de composition
• Les clients de la composition doivent ignorer s’ils communiquent avec
des objets composés ou non
Folder
+ Name : String
File
+ Name : String
<<Interface>>
IElement
+ Name : String
20. Le système solaire
• Il est composé de :
• Le soleil
• Des planètes
• Autres étoiles et objets célestes
21. Le système solaire
Sun
- instance : Sun
+ GetInstance() : Sun
private static Sun instance = new Sun();
private Sun()
{
}
public static Sun GetInstance()
{
return instance;
}
22. Singleton
• Assurer qu’une classe ne possède qu’une seule instance et de fournir
une méthode de classe unique retournant cette instance
Singleton
- instance : Singleton
+ GetInstance() : Singleton
private static Singleton instance = new Singleton();
private Singleton()
{
}
public static Singleton GetInstance()
{
return instance;
}
23. Singleton – use cases
• Il ne doit y avoir qu’une seule instance
• Cette instance n’est accessible qu’à travers une méthode de classe
• Eviter une variable globale
Singleton
- instance : Singleton
+ GetInstance() : Singleton
private static Singleton instance = new Singleton();
private Singleton()
{
}
public static Singleton GetInstance()
{
return instance;
}
24. Singleton – use cases
On peut avoir une version lazy du singleton
Singleton
- instance : Singleton
+ GetInstance() : Singleton
private static Singleton instance;
public static Singleton GetInstance()
{
if(instance == null)
instance = new Singleton();
return instance
}
25. Singleton – attention
• Si on est en multithreading, ajout un lock pour l’instance
• Eviter les méthodes qui set l’instance à null
Singleton
- instance : Singleton
+ GetInstance() : Singleton
+ Reset()
public static Singleton GetInstance()
{
if(instance == null)
instance = new Singleton();
return instance
}
public void Reset()
{
instance = null;
}
var first = Singleton.GetInstance();
// first pointe donc sur un objet en mémoire
first .Reset();
var second = Singleton.GetInstance();
// => on a donc deux instances en mémoire
27. Appel au service client
• Un client achète un article sur un site et paye par carte bancaire
• Il veut changer l’adresse de livraison et rajouter une option dans
l’article
• Le changement d’adresse est au service de facturation
• L’ajout de l’option est au service de commande
• Il appelle le service client qui se charge de tout
33. Compresser des données
• On a besoin de compresser des données
• Codage par répétition
• AAA0000111FF => 3A40312F
• Codage entropique (de Huffman ou arithmétique)
• Pattern => 000 001 11 11 010 011 10
34. Compresser des données
Compressor
+ string Compress(string data)
<<Interface>>
ICompressor
+ string Compress(string data)
public string Compress(string data)
{
switch (this.compressionType)
{
case CompressionType.Repetition:
return CompressWithRepetitionAlgo(data);
case CompressionType.Entropic:
return CompressWithEntropicAlgo(data);
case CompressionType.ByDictionary:
return CompressByDictionaryAlgo(data);
default:
return data;
}
}
36. Strategy
• Adapter le comportement d’un objet en fonction d’un besoin sans
changer les interactions de cet objet avec les clients
• Représenter les comportements variables sous forme de classes
• Les comportements/algorithmes sont interchangeables
ConcreteStrategyA
+ Calculate()
<<Interface>>
Strategy
+ Calculate()
ConcreteStrategyB
+ Calculate()
Context
+ Execute()
void Execute()
{
strategy.Calculate();
}
37. Strategy – use cases
• Besoin d’implémenter plusieurs comportements pour un traitement
• Protéger les variations en séparant les parties variantes et invariantes
ConcreteStrategyA
+ Calculate()
<<Interface>>
Strategy
+ Calculate()
ConcreteStrategyB
+ Calculate()
Context
+ Execute()
void Execute()
{
strategy.Calculate();
}
38. Strategy – attention
• Quand les contextes sont responsables du choix de la stratégie
ConcreteStrategyA
+ Calculate()
<<Interface>>
Strategy
+ Calculate()
ConcreteStrategyB
+ Calculate()
Context
+ Execute()
void Execute()
{
strategy.Calculate();
}
40. Chez le concessionnaire
• Le concessionnaire gère les commandes issues de client de la France
et du Luxembourg.
• La différence entre les commandes concerne le calcul de la TVA
• France : TVA = 19,6%
• Luxembourg : TVA = 12% pour les prestations et 15% pour le matériel
41. Chez le concessionnaire
• Montant = HT + TVA
<<Interface>>
IOrder
+CalculateAmount()
FrenchOrder
-preTaxAmount
+CalculateAmount()
LuxembourgishOrder
-preTaxServiceAmount
-preTaxMaterialAmount
+CalculateAmount()
public double CalculateAmount()
{
var vatAmount = preTaxAmount * 0.196;
var amount = preTaxAmount + vatAmount;
return amount;
}
public double CalculateAmount()
{
var vatAmount = preTaxServiceAmount * 0.12 +
preTaxMaterialAmount * 0.15;
var preTaxAmount = preTaxServiceAmount +
preTaxMaterialAmount;
var amount = preTaxAmount +
vatAmount;
return amount;
}
43. Chez le concessionnaire
• Montant = HT + TVA
• Factoriser le comportement
dans la classe de base
• Implémenter les spécificités
dans les classes filles
<<Interface>>
IOrder
+CalculateAmount()
FrenchOrder
-preTaxAmount
#CalculateVat()
#CalculatePreTaxAmount()
LuxembourgishOrder
-preTaxServiceAmount
-preTaxMaterialAmount
#CalculateVat()
#CalculatePreTaxAmount()
<<Abstract>>
BaseOrder
+CalculateAmount()
#CalculateVat()
#CalculatePreTaxAmount()
protected override double CalculateVat()
{
return preTaxAmount * 0.196;
}
protected override double CalculatePreTaxAmount()
{
return preTaxAmount;
}
protected override double CalculateVat()
{
return preTaxServiceAmount * 0.12 +
preTaxMaterialAmount * 0.15;
}
protected override double CalculatePreTaxAmount()
{
return preTaxServiceAmount +
preTaxMaterialAmount;
}
public double CalculateAmount()
{
return CalculateVat() + CalculatePreTaxAmount();
}
protected abstract double CalculateVat();
protected abstract double CalculatePreTaxAmount();
44. Template method
• Reporter aux filles
certaines étapes d’un
traitement
• Les étapes sont
implémentées dans
les classes filles
ConcreteClass
#Operation1()
#Operation2()
<<Abstract>>
BaseClass
+TemplateMethod()
#Operation1()
#Operation2()
public void TemplateMethod()
{
...
Operation1();
...
Operation2();
...
}
protected abstract void Operation1();
protected abstract void Operation2();
protected override void Operation1()
{
...
}
protected override void Operation2()
{
...
}
45. Template method – use cases
• Deux classes ou plusieurs
partagent du code
identique
• Les parties spécifiques
déplacées dans les filles
• Un algorithme avec une
partie invariable et des
parties spécifiques à
différents objets ConcreteClass
#Operation1()
#Operation2()
<<Abstract>>
BaseClass
+TemplateMethod()
#Operation1()
#Operation2()
public void TemplateMethod()
{
...
Operation1();
...
Operation2();
...
}
protected abstract void Operation1();
protected abstract void Operation2();
protected override void Operation1()
{
...
}
protected override void Operation2()
{
...
}
47. L’usine à chaussures
• L’usine crée
• Des chaussures de ville, des sandales
• En cuir ou synthétiques
ShoeCreator
+ CreateLeatherShoe()
+ CreateLeatherSandal()
+ CreateSyntheticShoe()
+ CreateSyntheticSandal()
IShoe
-type
Client
Sandal
-type
StreetShoe
-type
public StreetShoe CreateLeatherShoe()
{
return new StreetShoe(ShoeType.Leather);
}
public Sandal CreateSyntheticSandal()
{
return new Sandal(ShoeType.Synthetic);
}
48. L’usine à chaussures
ShoeCreator
+ CreateShoe()
+ CreateSandal()
IShoe
Client
Sandal
StreetShoe
LeatherFabric
+ CreateShoe()
+ CreateSandal()
SyntheticFabric
+ CreateShoe()
+ CreateSandal()
LeatherSandal SyntheticSandal
LeatherStreetShoe SyntheticStreetShoe
public StreetShoe CreateShoe()
{
return new LeatherStreetShoe();
}
public Sandal CreateSandal()
{
return new LeatherSandal();
}
public StreetShoe CreateShoe()
{
return new SyntheticStreetShoe();
}
public Sandal CreateSandal()
{
return new SyntheticSandal();
}
49. Abstract factory
• Créer des objets regroupés
en familles sans devoir
connaître les classes
concrètes destinées à la
création de ces objets
• Configuration flexible
• Les implémentations
doivent changer en fonction
de certains paramètres
AbstractFabric
+ CreateProductA()
+ CreateProductB()
Client
ProductA
ProductB
ConcreteFabric1
+ CreateProductA()
+ CreateProductB()
ConcreteFabric2
+ CreateProductA()
+ CreateProductB()
ProductA1 ProductA2
ProductB1 ProductB2
50. Abstract factory – use cases
• Un système utilisant des
produits a besoin d’être
indépendant de la façon
dont ces produits sont créés
et regroupés
• Un système est paramétré
par plusieurs familles de
produits qui peuvent
évoluer
AbstractFabric
+ CreateProductA()
+ CreateProductB()
Client
ProductA
ProductB
ConcreteFabric1
+ CreateProductA()
+ CreateProductB()
ConcreteFabric2
+ CreateProductA()
+ CreateProductB()
ProductA1 ProductA2
ProductB1 ProductB2
51. Abstract factory - attention
• Si une règle de gestion
exige de ne pas pouvoir
créer des sandales en
synthétique par exemple,
ce pattern ne correspond
pas car on va implémenter
la méthode CreateSandal
pour le synthétique
• Risque de créer des objets
incompatibles entre eux
AbstractFabric
+ CreateProductA()
+ CreateProductB()
Client
ProductA
ProductB
ConcreteFabric1
+ CreateProductA()
+ CreateProductB()
ConcreteFabric2
+ CreateProductA()
+ CreateProductB()
ProductA1 ProductA2
ProductB1 ProductB2
53. Faire du shopping
• Un client commande des articles sur internet
• Statuts de la commande :
• En cours : le client est en train de choisir les articles
• Validée : le client valide et paye la commande
• Livrée : le client reçoit les articles
54. Faire du shopping
• Inconvénients
• Il faut gérer tous les statuts
dans les méthodes
• Les transitions des statuts
sont gérées par la
commande
Order
- state
- items
+ AddItem()
+ RemoveItem()
+ NextState()
public void AddItem(Item item)
{
if(state == InProgress)
{
items.Add(item);
}
else
{
throw new System.NotSupportedException();
}
}
public void RemoveItem(Item item)
{
if(state == InProgress || state == Validated)
{
items.Remove(item);
}
else
{
throw new System.NotSupportedException();
}
}
55. Faire du shopping
• Inconvénients
• Il faut gérer tous les statuts
dans les méthodes
• Les transitions des statuts
sont gérées par la
commande
• Nouvel objet à
chaque modif de
statut
Order
-state
-items
+AddItem()
+RemoveItem()
+NextState()
InprogressOrder
+AddItem()
+RemoveItem()
ValidatedOrder
+AddItem()
+RemoveItem()
DeliveredOrder
+AddItem()
+RemoveItem()
public void AddItem(Item item)
{
throw new NotSupportedException();
}
public void RemoveItem(Item item)
{
throw new NotSupportedException();
}
public void AddItem(Item item)
{
throw new NotSupportedException();
}
public void RemoveItem(Item item)
{
items.Remove(item);
}
public void AddItem(Item item)
{
items.Add(item);
}
public void RemoveItem(Item item)
{
items.Remove(item);
}
56. Faire du shopping
• Inconvénients
• Il faut gérer tous les statuts
dans les méthodes
• Les transitions des statuts
sont gérées par la
commande
• Nouvel objet à
chaque modif de
statut
OrderStatus
+AddItem()
+RemoveItem()
InprogressStatus
+AddItem()
+RemoveItem()
ValidatedStatus
+AddItem()
+RemoveItem()
DeliveredStatus
+AddItem()
+RemoveItem()
Order
-state
-items
+AddItem()
+RemoveItem()
+NextState()
public void AddItem(Item item)
{
throw new NotSupportedException();
}
public void RemoveItem(Item item)
{
throw new NotSupportedException();
}
public void AddItem(Item item)
{
throw new NotSupportedException();
}
public void RemoveItem(Item item)
{
items.Remove(item);
}
public void AddItem(Item item)
{
items.Add(item);
}
public void RemoveItem(Item item)
{
items.Remove(item);
}
public void AddItem(Item item)
{
state.AddItem(item);
}
public void RemoveItem(Item item)
{
state.RemoveItem(item);
}
57. State
• Permet à un objet (Context) d’adapter son comportement en fonction
de son état interne
Context
+Request()
State
+Handle()
ConcreteStateA
+Handle()
ConcreteStateB
+Handle()
Request()
{
state.Handle();
}
58. State – use cases
• Le comportement d’un objet dépend de son état
• L’implémentation par des instructions conditionnelles est trop complexe
Context
+Request()
State
+Handle()
ConcreteStateA
+Handle()
ConcreteStateB
+Handle()
Request()
{
state.Handle();
}
59. State – attention
• Ne pas implémenter si deux états (simples)
Context
+Request()
State
+Handle()
ConcreteStateA
+Handle()
ConcreteStateB
+Handle()
Request()
{
state.Handle();
}
61. Comparateur d'hôtels
• J’ai créé un service de comparaison des hôtels du coin
• Je veux cherche des clients potentiels pour mon service
Comparator
+ GetAvailableRooms(Date) : List<RoomPrice>
<<Interface>>
IComparator
+ GetAvailableRooms(Date) : List<RoomPrice>
62. Comparator
+ GetAvailableRooms(Date) : List<RoomPrice>
Client
<<Interface>>
IComparator
+ GetRoomAvailailities(Date) : List<RoomAvailability>
<<Interface>>
IComparator
+ GetAvailableRooms(Date) : List<RoomPrice>
Comparateur d'hôtels
• Le client impose un contrat de service qui lui correspond
?
63. Comparateur d'hôtels
• Le client impose un contrat de service qui lui correspond
Comparator
+ GetAvailableRooms(Date) : List<RoomPrice>
Client
<<Interface>>
IComparator
+ GetRoomAvailailities(Date) : List<RoomAvailability>
<<Interface>>
IComparator
+ GetAvailableRooms(Date) : List<RoomPrice>
ComparatorAdaptor
+ GetRoomAvailailities(Date) : List<RoomAvailability>
64. Adapter
• Convertir une class existante en une interface attendue par les clients
également existants afin qu’ils puissant travailler ensemble
Adaptee
+ SpecificRequest()
Client
<<Interface>>
ITarget
+ Request()
Adapter
+ Request()
adaptee.SpecificRequest()
65. Adapter – use cases
• Intégrer dans un système un objet dont l’interface ne correspond pas à
l’interface requise au sein de ce système
• Fournir des interfaces multiples à un objet lors de sa conception
Adaptee
+ SpecificRequest()
Client
<<Interface>>
ITarget
+ Request()
Adapter
+ Request()
adaptee.SpecificRequest()
66. Adapter – attention
• Il y aura autant d’adaptateurs que de fournisseur
Adaptee
+ SpecificRequest()
Client
<<Interface>>
ITarget
+ Request()
Adapter
+ Request()
adaptee.SpecificRequest()
68. Compter le nombre de LIKE
• Dans mon application, j’ai un service pour liker mon produit
• Créer un service de comptage
LikeCounter
+ CountLikes()
ProductAnalysis
+ LikeProduct()
69. Compter le nombre de LIKE
LikeCounter
+ Update(int)
<<Interface>>
ILikeCounter
+ Update(int)
ProductAnalysis
- List<ILikeCounter> counters
+ Add(ILikeCounter)
+ LikeProduct()
public void Add(ILikeCounter counter)
{
counters.Add(counter);
}
public void LikeProduct()
{
likesNumber ++;
foreach(var counter in this.counters)
{
counter.Update(likesNumber);
}
}
public LikeCounter(ProductAnalysis product)
{
product.Add(this);
}
public void Update(int number)
{
Console.WriteLine(number);
}
70. Observer
• Permet aux observateurs de mettre à jour leur état quand le sujet est
modifié
<<abstract>>
Subject
+ Add(Observer)
+ Remove(Observer)
+ Notify()
ConcretSubject
- subjectState
+ GetState()
<<abstract>>
Observer
+ Update()
ConcreteObserver
- observerState
+ Update()
subject
observer
return subjectState;
public void Add(Observer observer)
{
this.observers.Add(observer);
}
public void Notify()
{
foreach(var obs in observers)
{
obs.Update();
}
}
public void Update()
{
observerState = subject.GetState();
}
71. Observer – use cases
• La modification d’un objet engendre des modifications dans d’autres
objets qui sont déterminés dynamiquement
• Un objet veut prévenir d’autres objets sans devoir connaitre leur type
• pas de couplage
<<abstract>>
Subject
+ Add(Observer)
+ Remove(Observer)
+ Notify()
ConcretSubject
- subjectState
+ GetState()
<<abstract>>
Observer
+ Update()
ConcreteObserver
- observerState
+ Update()
subject
observer
return subjectState;
public void Add(Observer observer)
{
this.observers.Add(observer);
}
public void Notify()
{
foreach(var obs in observers)
{
obs.Update();
}
}
public void Update()
{
observerState = subject.GetState();
}
72. Observer – Attention
• Plusieurs implémentations
• Garder le statut dans le sujet et l’appeler dans la méthode Update de l’observer
• Ne pas exposer le statut du sujet et envoyer Event (contient les infos de
notification)
<<abstract>>
Subject
+ Add(Observer)
+ Remove(Observer)
+ Notify(Event)
<<abstract>>
Observer
+ Update(Event)
ConcreteObserver
- observerState
+ Update(Event)
observer
public void Add(Observer observer)
{
this.observers.Add(observer);
}
public void Notify(Event e)
{
foreach(var obs in observers)
{
obs.Update(e);
}
}
public void Update(Event e)
{
observerState = e;
}
74. Composer son PC
• Choisir les options
• Taille écran : 11’, 16’ …
• Processeur : i5, i7 …
• Mémoire RAM : 12 Go, 16 Go …
• Taille du disque dur : 1To, SSD …
• Carte vidéo : NVIDIA, AMD …
• Casque : USB, Bluetooth …
• Souris : filaire, sans fil…
• Sac : à dos, sacoche …
• Clavier : Azerty, Querty
• Chaque option a un prix
• Le prix du PC = prix de base
(300€) + somme des options
76. Chez le dentiste
• L’assistante accueille les patients par ordre d’arrivée
• Le dentiste ne sait pas s’il y a un patient qui attend, ni qui il est
• Il demande de faire entrer le suivant
• Il ne s’intéresse pas à la façon dont la salle d’attente est gérée.
Dentist
+ EnterNext()
Patient
- name
works with
Secretary
- patients : Collection<Patient>
+ HasNext() : bool
+ Next() : Patient
77. Chez le dentiste
• Introduire la notion de collection pour dissocier la collection en tant
que tel et la manière de la parcourir
• Si on change la règle d’itération par arrivée en itération par priorité
Dentist
+ EnterNext()
Patient
- name
works with
Secretary
+ HasNext() : bool
+ Next() : Patient
Patients
+
83. Catalogue de films
• Au lieu de charger tout le film en mémoire, on affiche une image
• Un clic sur la photo permet de jouer le film
• But : réduire la quantité de mémoire utilisée
84. Catalogue de films
• Au lieu de charger tout le
film en mémoire, on
affiche une image
• Un clic sur la photo permet
de jouer le film
• But : réduire la quantité de
mémoire utilisée
Animation
+ Show()
+ Clic()
Film
+ Show()
+ Clic()
+ Run()
AnimationSubstitute
# photo
+ Show()
+ Clic()
0..1
#film
public void Show()
{
if(film == null)
photo.Show();
else
film.Show();
}
public void Clic()
{
if(film == null)
{
film = new Film();
}
film.Run();
}
85. Proxy
• Concevoir un objet qui se substitue à un autre et qui en contrôle
l’accès
<<Interface>>
ISubject
+ DoSomething()
RealSubject
+ DoSomething()
Proxy
- wrapee
+ DoSomething()
Client
public void DoSomething()
{
// Optionnal functionnality
wrapee.DoSomething();
// Optionnal functionnality
}
86. Proxy – use cases
• Proxy virtuel : créer un objet de taille importante au moment approprié
• Proxy remote : accéder à un objet s’exécutant sur un autre environnement
• Proxy de protection : sécuriser l’accès à un objet (authentification)
<<Interface>>
ISubject
+ DoSomething()
RealSubject
+ DoSomething()
Proxy
- wrapee
+ DoSomething()
Client
public void DoSomething()
{
// Optionnal functionnality
wrapee.DoSomething();
// Optionnal functionnality
}
88. Chez le grossiste de fournitures bureau
• Tous les mois, l’école passe la même commande de fournitures (stylo,
feutres, papiers …)
• Afin de faciliter la saisie pour ses clients, le grossiste a décidé de
fournir une fonctionnalité de duplication de commande passée
89. Chez le grossiste de fournitures bureau
• Fonctionnalité de duplication de commande
<<Interface>>
ICloneableOrder
+ Clone()
Order
+ Clone()
Client
+ CreateOrderFromAnother()
order
public void CreateOrderFromAnother()
{
...
var newOrder = order.Clone();
...
}
public ICloneableOrder Clone()
{
return this.MemberwiseClone();
}
90. Prototype
• Créer de nouveaux objets par duplication d’objets existants qui
disposent de la capacité de clonage
<<Interface>>
IPrototype
+ Clone()
ContretePrototype1
+ Clone()
ContretePrototype2
+ Clone()
Client
+ Operation()
prototype
public void Operation()
{
...
var clone = prototype.Clone();
...
}
public IPrototype Clone()
{
return this.MemberwiseClone();
}
91. Prototype – uses cases
• Créer des instances sans connaitre la hiérarchie des classes
<<Interface>>
IPrototype
+ Clone()
ContretePrototype1
+ Clone()
ContretePrototype2
+ Clone()
Client
+ Operation()
prototype
public void Operation()
{
...
var clone = prototype.Clone();
...
}
public IPrototype Clone()
{
return this.MemberwiseClone();
}
94. Faire du shopping
• Un client commande des articles sur internet
• Statuts de la commande :
• En cours : le client est en train de choisir les articles
• Validée : le client valide et paye la commande
• Livrée : le client reçoit les articles
95. Memento
• Sauvegarder et restaurer l’état d’un objet sans en violer
l’encapsulation
Originator
- state
+ SetMemento(Memento);
+ CreateMemento();
Memento
- state
+ GetState()
CareTaker
public void SetMemento(Memento memento)
{
state = memento.GetState();
}
public Memento CreateMemento()
{
return new Memento(state);
}
96. Memento – use cases
• Quand l’état interne d’un objet (totalement ou partiellement) doit
être mémorisé afin de pouvoir être restauré ultérieurement sans que
l’encapsulation de cet objet ne doive être brisée
Originator
- state
+ SetMemento(Memento);
+ CreateMemento();
Memento
- state
+ GetState()
CareTaker
public void SetMemento(Memento memento)
{
state = memento.GetState();
}
public Memento CreateMemento()
{
return new Memento(state);
}
98. L’usine à chaussures
• Ajouter un nouveau type : botte de pluie
• Complexité combinatoire
IShoe
Sandal StreetShoe
LeatherSandal SyntheticSandal LeatherStreetShoe SyntheticStreetShoe
RainBoot
LeatherRainBoot SyntheticRainBoot
99. L’usine à chaussures
• Eviter la complexité combinatoire
<<Interface>>
IShoe
<<Interface>>
IMaterial
Sandal Shoe
RainBoot
Leather Synthetic
100. Bridge
• Séparer l’aspect d’implémentation d’un objet de son aspect de
représentation et d’interface
<<Interface>>
IAbstraction
+ DoOperation()
Client
<<Interface>>
IImplementor
+ DoOperationImplem()
RefinedAbstraction ConcreteImplementorA
+ DoOperationImplem()
ConcreteImplementorB
+ DoOperationImplem()
DoOperation()
{
implementor.DoOperationImplem();
}
101. Bridge – use cases
• Eviter une liaison forte entre la
représentation des objets et leur
implémentation
• Changer la représentation des
objets sans impacter les
interactions entre les objets et les
clients
• Garder la capacité d’extension par
la création de nouvelles sous-
classes
• Eviter d’obtenir des hiérarchies de
classes extrêmement complexes
<<Interface>>
IAbstraction
+ DoOperation()
Client
<<Interface>>
IImplementor
+ DoOperationImplem()
RefinedAbstraction ConcreteImplementorA
+ DoOperationImplem()
ConcreteImplementorB
+ DoOperationImplem()
DoOperation()
{
implementor.DoOperationImplem();
}
103. Elements
• Changement de Write(), 3 fichiers touchés
Folder
+ Name : String
File
+ Name : String
<<Interface>>
IElement
+ Name : String
<<Interface>>
IReferenceableElement
Link
+ Name : String
References
+ Write()
+ Write() + Write() + Write()
104. Elements
Folder
+ Name : String
File
+ Name : String
<<Interface>>
IElement
+ Name : String
<<Interface>>
IReferenceableElement
Link
+ Name : String
References
+ Accept(IWriter writer)
+ Accept(IWriter writer) + Accept(IWriter writer) + Accept(IWriter writer)
Writer
+ Write(IElement element)
+ Write(Link link)
+ Writer(File file)
+ Write(Folder folder)
<<Interface>>
IWriter
+ Write(IElement element)
+ Write(Link link)
+ Writer(File file)
+ Write(Folder folder)
public Write(IElement element)
{
element.Accept(this);
}
public Write(Link link)
{
Console.WriteLine(link.Name);
}
...
public Accept(IWriter writer)
{
writer.Write(this);
}
105. Visitor
• Construire une opération à réaliser sur les éléments d’un ensemble
d’objets
• De nouvelles opérations peuvent ainsi être ajoutées sans modifier les
classes de ces objets <<Interface>>
IElement
+ AcceptVisitor(IVisitor visitor)
ConcreteElement1
+ AcceptVisitor(IVisitor visitor)
ConcreteElement2
+ AcceptVisitor(IVisitor visitor)
<<Interface>>
IVisitor
+ Visit(ConcreteElement1 element)
+ Visit(ConcreteElement2 element)
ConcreteVisitor1
+ Visit(ConcreteElement1 element)
+ Visit(ConcreteElement2 element)
ConcreteVisitor2
+ Visit(ConcreteElement1 element)
+ Visit(ConcreteElement2 element)
public AcceptVisitor(IVisitor visitor)
{
visitor.Visit(this);
}
106. Visitor – use case
• De nouvelles fonctionnalités doivent être ajoutées à un ensemble de
classes sans que ces ajouts viennent alourdir ces classes
• Un ensemble de classes possèdent une structure fixe et il est
nécessaire de leur adjoindre des fonctionnalités sans modifier leur
interface <<Interface>>
IElement
+ AcceptVisitor(IVisitor visitor)
ConcreteElement1
+ AcceptVisitor(IVisitor visitor)
ConcreteElement2
+ AcceptVisitor(IVisitor visitor)
<<Interface>>
IVisitor
+ Visit(ConcreteElement1 element)
+ Visit(ConcreteElement2 element)
ConcreteVisitor1
+ Visit(ConcreteElement1 element)
+ Visit(ConcreteElement2 element)
ConcreteVisitor2
+ Visit(ConcreteElement1 element)
+ Visit(ConcreteElement2 element)
public AcceptVisitor(IVisitor visitor)
{
visitor.Visit(this);
}
107. Visitor – attention
• Complexité
• Cycle de dépendance
• Il faut avoir des getters dans les classes utilisées
<<Interface>>
IElement
+ AcceptVisitor(IVisitor visitor)
ConcreteElement1
+ AcceptVisitor(IVisitor visitor)
ConcreteElement2
+ AcceptVisitor(IVisitor visitor)
<<Interface>>
IVisitor
+ Visit(ConcreteElement1 element)
+ Visit(ConcreteElement2 element)
ConcreteVisitor1
+ Visit(ConcreteElement1 element)
+ Visit(ConcreteElement2 element)
ConcreteVisitor2
+ Visit(ConcreteElement1 element)
+ Visit(ConcreteElement2 element)
public AcceptVisitor(IVisitor visitor)
{
visitor.Visit(this);
}
109. A la crêperie
• Au menu, on trouve crêpe et gaufre.
• On peut y ajouter du chocolat, des fraises, de la chantilly …
• Le prix de la crêpe chocolat banane =
prix crêpe + prix chocolat + prix banane
110. A la crêperie
• Au menu, crêpe et gaufre avec supplément chocolat, bananes, fraises,
chantilly …
Crepe
+ GetPrice()
Waffle
+ GetPrice()
<<Interface>>
IDessert
+ GetPrice()
Extra
+ GetPrice()
Chocolate
+ GetPrice()
Chantilly
+ GetPrice()
Strawberry
+ GetPrice()
dessert
public GetPrice()
{
return this.price + this.dessert.GetPrice();
}
111. Decorator
• Ajouter dynamiquement des fonctionnalités supplémentaires à un
objet.
<<Interface>>
IComponent
+ DoSomething()
ConcreteComponent
+ DoSomething()
<<Interface>>
IDecorator
+ DoSomething()
Decorator
+ DoSomething()
public Decorator(IComponent component)
{
this.component = component;
}
public DoSomething()
{
...
this.component.DoSomething-);
...
}
112. Decorator – use case
• Ajouter dynamiquement des fonctionnalités à un objet sans modifier
son interface
• Gérer des fonctionnalités qui peuvent être retirées dynamiquement
• Eviter l’héritage complexe
<<Interface>>
IComponent
+ DoSomething()
ConcreteComponent
+ DoSomething()
<<Interface>>
IDecorator
+ DoSomething()
Decorator
+ DoSomething()
public Decorator(IComponent component)
{
this.component = component;
}
public DoSomething()
{
...
this.component.DoSomething-);
...
}
113. Decorator – attention
• Ce pattern ne traite pas l’ordre de composition ou de fabrication
<<Interface>>
IComponent
+ DoSomething()
ConcreteComponent
+ DoSomething()
<<Interface>>
IDecorator
+ DoSomething()
Decorator
+ DoSomething()
public Decorator(IComponent component)
{
this.component = component;
}
public DoSomething()
{
...
this.component.DoSomething-);
...
}
115. Au roi du burger
• Constituer son burger
• Choisir le pain et ajouter des ingrédients avec type de portion
• Légère
• Normal
• Extra
116. Builder
• Construire des objets complexes sans préoccuper le client des
différentes implémentations
Director
+ Construct()
<<Interface>>
IBuilder
+ BuildFirstPart()
+ BuildSecondPart()
+ GetProduct()
ConcreteBuilder
+ BuildFirstPart()
+ BuildSecondPart()
+ GetProduct()
Product
117. Builder – use case
• Le client a besoin de construire un objet complexe sans connaitre son
implémentation
• Le client a besoin de construire des objets complexes ayant plusieurs
implémentations ou représentations
Director
+ Construct()
<<Interface>>
IBuilder
+ BuildFirstPart()
+ BuildSecondPart()
+ GetProduct()
ConcreteBuilder
+ BuildFirstPart()
+ BuildSecondPart()
+ GetProduct()
Product
118. Builder – attention
• Quand il s’agit d’une construction step by step
• Complexité du code
Director
+ Construct()
<<Interface>>
IBuilder
+ BuildFirstPart()
+ BuildSecondPart()
+ GetProduct()
ConcreteBuilder
+ BuildFirstPart()
+ BuildSecondPart()
+ GetProduct()
Product
120. Un peu de mathématiques
• Réalisez l’expression suivante
(4 - 5) * 2
• L’idée est de pouvoir réaliser plusieurs
opérations à la suite
((4 - 5) * 2) + (4 * (1 + 6))
4 5
-1
-
2
-2
*
1 6
4
+
7
28
*
+
26
121. Un peu de mathématiques
Operand
+ Evaluate()
Operator
+ Evaluate()
<<Interface>>
IExpression
+ Evaluate()
PlusOperator
+ Evaluate()
MinusOperator
+ Evaluate()
2
#LeftExpression
#RightExpression
public Operand(double value)
{
this.operand = value;
}
public double Evaluate()
{
return this.operand;
}
public double Evaluate()
{
return this.leftExpression.Evaluate() +
this.rightExpression.Evaluate();
}
public Operator(IExpression leftExpression, IExpression rightExpression)
{
this.leftExpression = leftExpression;
this.rightExpression = rightExpression;
}
public abstract double Evaluate();
public double Evaluate()
{
return this.leftExpression.Evaluate() -
this.rightExpression.Evaluate();
}
122. Interpreter
• Définir une représentation de grammaire pour interpréter des
expressions
TerminalExpression
+ Interpret(Context)
NonTerminalExpression
+ Interpret(Context)
<<Interface>>
IExpression
+ Interpret(Context)
NonTerminalExpression1
+ Interpret(Context)
NonTerminalExpression2
+ Interpret(Context)
123. Interpreter – use cases
• Interpréter des expressions
représentées sous forme d’arbres
syntaxiques
• Quand la grammaire des expressions est
simple
• L’évaluation n’a pas besoin d’être rapide
• Si la grammaire est complexe, il vaut
mieux utiliser un analyseur
syntaxique
• Si l’évaluation doit être rapide, il vaut
mieux utiliser un compilateur
TerminalExpression
+ Interpret(Context)
NonTerminalExpression
+ Interpret(Context)
<<Interface>>
IExpression
+ Interpret(Context)
NonTerminalExpression1
+ Interpret(Context)
NonTerminalExpression2
+ Interpret(Context)
126. Comportement graphique des sites web
• La modification d’une valeur d’un
widget peut conduire à modifier
l’aspect d’autres widget :
• Visible ou masqué
• Changement des valeurs d’un menu
• Changement du format des valeurs
• Inconvénient
• Chaque objet doit connaitre tous les
objets dépendants
• La réutilisabilité des objets est réduite
Widget 1
Widget 2
Widget 3
Widget 4
Widget 5
Widget 6
127. Mediator
• Construire un objet dont la vocation est la gestion et le contrôle des
interactions dans un ensemble d’objets sans que ses éléments
doivent se connaitre mutuellement.
<<Interface>>
Mediator
+ DoSomething()
<<Interface>>
Element
ConcreteElement1
ConcreteElement1
ConcreteMediator
128. Mediator – use cases
• Un système est formé d’un ensemble
d’objets basé sur une communication
complexe conduisant à associer de
nombreux objets entre eux
• Les objets d’un système sont difficiles
à réutiliser car ils possèdent de
nombreuses associations avec d’autres
objets
• La modularité d’un système est
médiocre, obligeant dans le cas d’une
adaptation d’une partie du système à
écrire de nombreuses sous classes.
<<Interface>>
Mediator
+ DoSomething()
<<Interface>>
Element
ConcreteElement1
ConcreteElement1
ConcreteMediator
131. Chain of responsibility
• Une chaine d’objets tel que si un objet ne peut pas répondre à une
requête, il puisse la transmettre à son successeur jusqu’à ce que l’un
des objets de la chaine y réponde.
Handler
+ HandleRequest()
successor
ConcreteHandler1
+ HandleRequest()
ConcreteHandler1
+ HandleRequest()
Client
132. Chain of responsibility – use cases
• Une chaine d’objets gère une requête selon un ordre qui es défini
dynamiquement
• La façon dont une chaine d’objets gère une requête ne doit pas être
connue par le client
Handler
+ HandleRequest()
successor
ConcreteHandler1
+ HandleRequest()
ConcreteHandler1
+ HandleRequest()
Client
134. Composer son PC
• Choisir les options
• Taille écran : 11’, 16’ …
• Processeur : i5, i7 …
• Mémoire RAM : 12 Go, 16 Go …
• Taille du disque dur : 1To, SSD …
• Carte vidéo : NVIDIA, AMD …
• Casque : USB, Bluetooth …
• Souris : filaire, sans fil…
• Sac : à dos, sacoche …
• Clavier : Azerty, Querty
• Chaque option a un prix
• Le prix du PC = prix de base
(300€) + somme des options
135. Flyweight
• Permet de partager de façon efficace un ensemble important d’objets
de granularité fine
FlyweightFabric
+ GetFlyweight(key)
Flyweight
- intrinsicState
+ Operation(extrinsicState)
Client
if(flyweights.ContainsKey(key))
{
return flyweights[key];
}
else
{
var newFlyweight = new Flyweight();
flyweights.Add(key, newFlyweight);
return newFlyweight;
}
136. Flyweight – use cases
• Le système utilise un grand nombre d’objets
• Le stockage des objets est couteux à cause d’une grande quantité
• Il existe de nombreux ensembles d’objets qui peuvent être remplacés
par quelques objets partagés une fois qu’une partie de leur état est
rendue extrinsèque
FlyweightFabric
+ GetFlyweight(key)
Flyweight
- intrinsicState
+ Operation(extrinsicState)
Client
if(flyweights.ContainsKey(key))
{
return flyweights[key];
}
else
{
var newFlyweight = new Flyweight();
flyweights.Add(key, newFlyweight);
return newFlyweight;
}
138. Chez le concessionnaire
• Un client veut passer une commande pour une voiture
• Il a le choix de payer au comptant ou à crédit
139. Chez le concessionnaire
• Un client veut passer une commande pour une voiture
• Il a le choix de payer au comptant ou à crédit
OrderCreator
+ CreateNewOrder()
- CreateOrder() : Order
Order
+ Pay();
+ IsValid() : bool
SpotOrder
+ Pay();
+ IsValid() : bool
ForwardOrder
+ Pay();
+ IsValid() : bool
public void CreateNewOrder()
{
Order order= CreateOrder();
if(order.IsValid())
{
order.Pay();
}
}
private Order CreateOrder()
{
if(paymentType == PaymentType.Spot)
{
return new SpotOrder();
}
else
{
return new ForwardOrder();
}
}
140. Chez le concessionnaire
BaseOrderCreator
+ CreateNewOrder()
# CreateOrder() : Order
SpotOrderCreator
# CreateOrder() : Order
Order
+ Pay();
+ IsValid() : bool
SpotOrder
+ Pay();
+ IsValid() : bool
ForwardOrder
+ Pay();
+ IsValid() : bool
ForwardOrderCreator
# CreateOrder() : Order
public void CreateNewOrder()
{
Order order= CreateOrder();
if(order.IsValid())
{
order.Pay();
}
}
protected Order CreateOrder()
{
return new SpotOrder();
}
protected Order CreateOrder()
{
return new ForwardOrder();
}
141. AbstractCreator
+ CreateProduct() : IProduct
Creator
+ CreateProduct() : IProduct
IProduct
Product
Factory Method
• Introduire une méthode abstraite de création d’un objet en reportant
aux sous-classes concrètes la création effective
142. AbstractCreator
+ CreateProduct() : IProduct
Creator
+ CreateProduct() : IProduct
IProduct
Product
Factory Method – use cases
• Une classe ne connait que les classes abstraites des objets avec
lesquels elle possède des relations
• Une classe veut transmettre à ses sous-classes les choix
d’instanciation en profitant du mécanisme de polymorphisme
144. Période des soldes
• Le responsable du magasin a besoin
d’appliquer et annuler une réduction sur un
produit
• Aujourd’hui -20% sur le prix initial
• Demain -10% supplémentaires => -30% en tout
• Le lendemain annuler le -10% => -20% en tout
145. Command
• Permet de transformer une requête en un objet pour faciliter les
opérations comme l’annulation ou la mise en file des requêtes et leur
suivi
Command
+ Execute()
<<Interface>>
ICommand
+ Execute()
Invoker
+ SetCommand(ICommand)
+ ExecuteCommand()
IReceiver
+ Action()
receiver.Action();
146. Command – use cases
• Un objet doit être paramétré par un traitement à réaliser
• Les commandes
• À stocker dans une file
• À exécuter à tout moment ou plusieurs fois
• À Annuler
• À tracer dans un fichier de log
Command
+ Execute()
<<Interface>>
ICommand
+ Execute()
Invoker
+ SetCommand(ICommand)
+ ExecuteCommand()
IReceiver
+ Action()
receiver.Action();
147. Command – attention
• Les commandes peuvent être regroupées sous la forme d’une
transaction (un ensemble ordonné de commandes qui agissent sur
l’état d’un système, si l’une est annulée tout l’ensemble l’est aussi)
Command
+ Execute()
<<Interface>>
ICommand
+ Execute()
Invoker
+ SetCommand(ICommand)
+ ExecuteCommand()
IReceiver
+ Action()
receiver.Action();