IFT3912 - Conception OO

35
Bruno Dufour [email protected] IFT3912 - Développement et maintenance de logiciels Conception orientée-objet Principes OO Bruno Dufour - Université de Montréal Abstraction L’abstraction permet de gérer la complexité L’abstraction permet de se concentrer sur la vue externe d’un objet séparer le comportement d’un objet de son implémentation Tony Hoare: “Abstraction arises from a recognition of similarities between certain objects, situations, or processes in the real world, and the decision to concentrate upon those similarities and to ignore for the time being the differences.” 3 Bruno Dufour - Université de Montréal Encapsulation Séparation de l’interface contractuelle d’une abstraction de son implémentation Permet de cacher la structure interne et les détails d’implémentation d’un objet Les classes devraient être opaques et ne pas exposer leurs détails d’implémentation internes 4

Transcript of IFT3912 - Conception OO

IFT3912 - Conception OOConception orientée-objet Principes OO
Abstraction
• L’abstraction permet de gérer la complexité • L’abstraction permet de
• se concentrer sur la vue externe d’un objet • séparer le comportement d’un objet de son
implémentation • Tony Hoare: “Abstraction arises from a recognition of
similarities between certain objects, situations, or processes in the real world, and the decision to concentrate upon those similarities and to ignore for the time being the differences.”
3
Encapsulation
• Séparation de l’interface contractuelle d’une abstraction de son implémentation
• Permet de cacher la structure interne et les détails d’implémentation d’un objet
• Les classes devraient être opaques et ne pas exposer leurs détails d’implémentation internes
4
Encapsulation en Java
public double getSpeed() { return speed; }
public void setSpeed(double newSpeed) { speed = newSpeed; } }
5
Encapsulation en Java
public double getSpeed() { return speed; }
public void setSpeed(double newSpeed) { if (newSpeed < 0) { throw new IllegalArgumentException(“...”); } speed = newSpeed; } }
6
Encapsulation en Java
public double getSpeed() { return KPHtoMPH(speedInKPH); }
public void setSpeed(double newSpeedInMPH) { if (newSpeedInMPH < 0) { throw new IllegalArgumentException(“...”); } speedInKPH = MPHtoKPH(newSpeedInMPH); } }
7
Encapsulation en Java
public double getSpeed() { return speed; }
public void setSpeed(double newSpeed) { if (newSpeed < 0) { throw new IllegalArgumentException(“...”); } speed = newSpeed; notifySpeedChanged(); } }
8
Réutilisation
• Deux stratégies • Par héritage : la nouvelle fonctionnalité est ajoutée
par extension de l’implémentation d’un objet existant
• Par composition / délégation : la nouvelle fonctionnalité est ajoutée en créant un nouvel objet qui est composé d’objets existants, qui agissent comme délégués
9
Exemple - Par héritage
public class InstrumentedHashSet extends HashSet { // The number of attempted element insertions private int addCount = 0;
public boolean add(Object o) { addCount++; return super.add(o); }
public boolean addAll(Collection c) { addCount += c.size(); return super.addAll(c); } }
10
Exemple - Par héritage
public void testAddAll() { InstrumentedHashSet s = new InstrumentedHashSet(); s.addAll(Arrays.asList(new String[] { "Snap", "Crackle", "Pop"})); assert s.getAddCount() == 3; // 6! }
11
Bruno Dufour - Université de Montréal
Réutilisation par héritage
• Avantages • Facile, la plupart du code est hérité • Possibilité de modifier le comportement du code
réutilisé (overriding) • Inconvénients
• Les détails internes de la classe parente sont importants et souvent visibles à ses enfants
• Un changement à la classe parente peut nécessiter des changements à ses enfants
• Réutilisation rigide - ne peut être modifiée au cours de l’exécution
12
Exemple - Par héritage
public InstrumentedHashSet(Set s) { delegate = s; }
public boolean add(Object o) { addCount++; return delegate.add(o); }
public boolean addAll(Collection c) { addCount += c.size(); return delegate.addAll(c); } }
13
Réutilisation par composition
sont invisibles • Peut être modifiée au cours de l’exécution
• Inconvénients • Plus difficile, toutes les méthodes d’une interface
devront être implémentées (génération automatique possible)
• Les interfaces doivent être définies soigneusement • Peut produire des systèmes qui contiennent plus
d’objets
Single Responsibility Principle (SRP)
celles-ci deviennent couplées • plus de difficulté à assurer toutes les
responsabilités suite à des changements • fragilité lors de changements peut causer des effets
de bord inattendus
15
Une classe ne devrait avoir qu’une seule raison d’être modifiée.
Bruno Dufour - Université de Montréal
Exemple - 2 responsibilités 16
Source: Robert C. Martin
Exemple - Responsabilités séparées 17
Qu’est-ce qu’une responsabilité ? 18
• Définir une responsabilité peut être difficile • Nos habitudes nous poussent à grouper plusieurs
responsabilités :
public interface Modem { public void dial(String phoneNo); public void hangup(); public void send(byte[] data); public byte[] receive(); }
Gestion des communications
Échange des donnés
Séparation des responsabilités 19
Causes des changements
• Devrait-on séparer les deux responsabilités de Modem ?
• Dépend de la façon dont l’application peut changer • La connection peut changer indépendamment ?
• Conception trop rigide • Les deux responsabilités ne changent pas de façon
indépendante ? • Éviter la complexité inutile
20
Exemple #2 - Persistence 21
Logique d’affaires Persistence
Open-Closed Principle (OCP)
• Une classes devrait être conçue pour ne pas changer • Les changements devraient être effectués par ajout
de nouveau code plutôt que par modification de code existant
• Ouvert pour les extensions • Le comportement peut être étendu
• Fermé pour les modifications • Le code source ne doit pas être modifié
22
Une classe devrait être ouverte pour les extensions, mais fermée pour les modifications.
Bruno Dufour - Université de Montréal
OCP et abstraction
• Comment modifier une classe sans changer son code source ?
• En utilisant des abstractions • Les classes d’un système manipulent des
abstractions • Elles sont fermées pour modification si elles
dépendent d’abstractions fixes • Elles sont ouvertes pour extension par ajout de
nouvelles classes dérivées de ces abstractions
23
Exemple - Shape
struct Square { ... } struct Circle { ... }
void drawAllShapes(Shape* list[], int n) { int i; for (i=0; i<n; i++) { struct Shape* s = list[i]; switch (s->type) { case square: drawSquare((struct Square*)s); break; case circle: drawCircle((struct Circle*)s); break; } } }
24
Doit être modifié si on ajoute d’autres types de formes
Source: Robert C. Martin
Exemple - Shape
void drawAllShapes(Collection<Shape> shapes) { for (Shape s: shapes) s.draw(); }
25
Fermeture stratégique
• Un programme ne peut être 100% fermé au modifications
• ex: on veut modifier drawAllShapes pour afficher les formes dans un certain ordre
• Il existe toujours certains changements qui nécessitent la modification du code source d’une classe
• La classe devrait être fermée pour les changements les plus probables, et non pas tous les changements
• Fermeture stratégique
Exemple - Fermeture stratégique
abstract class Shape implements Comparable<Shape> { public abstract void draw(); public int compareTo(Shape s) { ... } }
class Square extends Shape {...} class Circle extends Shape {...}
void drawAllShapes(List<Shape> shapes) { for (Shape s: Collections.sort(shapes)) s.draw(); }
27
drawAllShapes est fermée pour les ajouts de nouvelles formes et pour les changements de l’ordre des formes.
Source: Bob Tarr Bruno Dufour - Université de Montréal
Conventions qui dérivent du OCP
• Utiliser des attributes privés • Permet de fermer les méthodes qui dépendent de
ces attributs (encapsulation) • Éviter les variables globales
• Un module qui dépend d’une variable globale ne peut pas est fermé contre les modifications dans d’autres modules qui peuvent écrire dans cette variable.
• Éviter les tests de types (instanceof) • Fragile en présence d’ajout de type
28
Liskov Substitution Principle (LSP)
• Une classe qui viole ce principe viole aussi OCP • elle doit connaître certains ou tous les types dérivés
d’une classe parente • ex: drawAllShapes fonctionne pour toutes les
formes
29
Une classe devrait toujours pouvoir être substituée pour une classe parente.
Bruno Dufour - Université de Montréal
Exemple - Rectangle
public class Rectangle { private double width; private double height;
public Rectangle(double w, double h) { width = w; height = h; }
public double getWidth() { return width; } public double getHeight() { return height; } public void setWidth(double w) { width = w; } public void setHeight(double h) { height = h; } public double area() { return (width * height); } }
30
Exemple - Square
public class Square extends Rectangle { public Square(double s) { super(s, s); }
public void setWidth(double w) { super.setWidth(w); super.setHeight(w); }
public void setHeight(double h) { super.setHeight(h); super.setWidth(h); } }
31
Rectangle, Shape et LSP
32
Quel est le problème ?
• Les classes Rectangle et Square semblent valides • Un carré est bien aussi un rectangle
(géométriquement) • Les attentes du concepteur sont raisonnable
• Il faut considérer les attentes des utilisateurs de ces classes
• Le comportement d’un rectangle est différent de celui d’un carré
33
LSP et contraintes
• Pour respecter le LSP, une sous-classe ne peut avoir plus de contraintes que sa classe parente
• Si une sous-classe a des contraintes plus fortes que sa classe parente, certains cas seront valide pour le parent mais pas pour l’enfant
• Une classe enfant peut par contre relaxer certaines contraintes de sa classe parente sans violer le LSP
• Plus formellement, une sous-classe peut • affaiblir des préconditions • renforcer des postconditions
34
Interface Segregation Principle (ISP)
• Chaque interface représente un seul comportement cohésif
• Une seule responsabilité par interface • Séparer les interfaces lourdes (fat) qui ont trop de
responsabilités en plusieurs interfaces distinctes
35
Les clients d’une classe ne devraient pas être forcés de dépendre d’interfaces qu’ils n’utilisent pas.
Bruno Dufour - Université de Montréal
Exemple - ISP
public interface Bird { public void eat(); public void chirp(); public void walk(); public void fly(); }
public class Ostrich implements Bird { ... public void fly() { throw new UnsupportedOperationException( “An ostrich doesn’t fly”); } }
36
ISP et confidentialité
public class Contact {     public String getName { ... }     public String getAddress { ... }     public String getEmailAddress { ... }     public String getTelephone { ... } }         public class Emailer {     public void SendMessage(Contact contact, String subject, String body) {         // Cette méthode a accès à plus d’informations // que nécessaire pour effectuer son travail!     } }
37
Dependency Inversion Principle (DIP)
• Sans ce principe, des changements à un module de bas niveau peuvent se propager à un module de haut niveau.
38
A - Les modules de haut niveau ne doivent pas dépendre des modules de bas niveau. Les deux
devraient dépendre d’abstractions.
B - Les abstractions ne doivent pas dépendre des détails. Les détails ne doivent pas dépendre des
abstractions.
Exemple 39
Exemple 40
Exemple - Button 41
---------- lamp.h ---------- class Lamp { public: void TurnOn(); void TurnOff(); };
---------- button.h ---------- class Lamp; class Button { public: Button(Lamp& l) : itsLamp(&l) {} void onToggle(); private: Lamp* itsLamp; };
---------- button.cc ---------- #include “button.h” #include “lamp.h” void Button::onToggle() { bool buttonOn = GetState(); if (buttonOn) itsLamp->TurnOn(); else itsLamp->TurnOff(); }
Bruno Dufour - Université de Montréal
Exemple - Button 42
l’utilisateur
Bruno Dufour - Université de Montréal
5 principes de la conception OO
• SRP - Single Responsibility Principle • OCP - Open-Closed Principle • LSP - Liskov Substitution Principle • ISP - Interface Segregation Principle • DIP - Dependency Inversion Principle
43
Patrons de conception
• Un patron décrit (et nomme) une solution à un problème commun
• Offre une solution abstraite pour faciliter la réutilisation
• Est formulé en termes de classes et d’objets • Peut être implémenté différemment en fonction du
langage de programmation utilisé • La solution proposée par un patron peut être modifiée
ou adaptée selon les besoins du logiciel • « Forcer » l’utilisation d’un patron dans un logiciel
est une mauvaise pratique de développement
45
Patrons et abstraction 46
Objectifs des patrons
• Les patrons visent en général à accroître la qualité du code en visant un ou plusieurs des objectifs suivant:
• Flexibilité accrue • Meilleure performance • Fiabilité accrue
• Attention! L’utilisation des patrons peut aussi augmenter la complexité du code
• Par exemple, ajout d’indirections • Il faut donc juger des avantages et inconvénients
de l’ajout de patrons dans la conception d’un logiciel
47
Types de patrons
• Créationnels: font l’abstraction du processus d’instanciation afin de rendre un système indépendant de la façon dont ses objets sont créés et représentés
• Structuraux: se concentrent sur la façon dont les classes et les objets sont composés pour obtenir de plus grandes structures
• Comportementaux: décrivent les modèles de communication et interaction entre les objets
48
Spécification des patrons
• Nom: court et descriptif • Intention: ce que le patron fait • Motivation: le(s) problème(s) que le patron permet de
résoudre • Applicabilité: situations pour lesquelles le patron peut
être utilisé • Structure: représentation graphique (ex: UML) • Participants: rôles joués par les objets • Collaborations: interactions entre les objets • Conséquences: avantages / inconvénients du patron
49
Exemple - Itérateur
50
Bruno Dufour - Université de Montréal
Exemple - Itérateur
• Intention: permettre un accès séquentiel aux éléments d’une collection sans en exposer la représentation interne
51
Bruno Dufour - Université de Montréal
Exemple - Itérateur
• Motivation: • Une collection telle qu’une liste devrait permettre
de traverser ses éléments tout en respectant les principes d’encapsulation
• Une liste devrait supporter différentes méthodes de traversée
• Une liste devrait supporter plusieurs traversées concurrentes
• On ne veut pas “polluer” l’interface de la liste
52
Bruno Dufour - Université de Montréal
Exemple - Itérateur
• Applicabilité: • Supporter l’accès aux éléments d’une collection
sans exposer sa représentation interne • Supporter plusieurs traversées à la fois • Fournir une interface uniforme pour traverser
différents types de structures
Bruno Dufour - Université de Montréal
Exemple - Itérateur
Bruno Dufour - Université de Montréal
Exemple - Itérateur
• Participants: • Aggregate : contient les éléments, permet de créer
des itérateurs (factory, à venir) • Iterator : définit une interface pour accéder aux
éléments • ConcreteAggregate, ConcreteIterator :
Bruno Dufour - Université de Montréal
Exemple - Itérateur polymorphique
Bruno Dufour - Université de Montréal
Exemple - FilteredIterator 57
...
Exemple - FilteredIterator 58
public class FilteredIterator<E> implements Iterator<E> { private final Iterator<E> delegate; private final Filter<? super E> filter; private E buffer = null; // always points to the next element to return private boolean bufferHasElement = false; ...
public boolean hasNext() { return this.bufferHasElement; }
public E next() { if (!this.bufferHasElement) { throw new NoSuchElementException(); } E next = this.buffer; this.advance(); return next; } }
Bruno Dufour - Université de Montréal
Autres langages
59
Python: class MyListIter: def __init__(self, seq): self.seq = seq self.pos = 0
def __iter__(self): return self
def next(self): if self.pos < len(self.lst): v = self.seq[self.pos] self.pos += 1 return v else: raise StopIteration()
Bruno Dufour - Université de Montréal
Les patrons peuvent être adaptés
• Un patron peut être adapté à une situation particulière
60
public interface Iterator { public Object next(); public boolean hasNext(); public void remove(); }
Patrons créationnels
Motivation
• Les langages de programmation OO permettent tous de créer des objets
• Ce mécanisme est habituellement rigide • ex : new ArrayList() • ArrayList est le nom d’une classe connue durant
la compilation
Patrons créationnels
• Les patrons créationnels visent à abstraire le processus de création d’objets
• Rendent le mécanisme de création des objets plus flexible
• En pratique : remplacer new par une méthode qui effectue la création d’objet
• peut créer différents types d’objets au cours de l’exécution, ou contrôler la création des objets
• peut être étendue pour modifier son comportement (OCP)
63
Classes vs objets
• 2 types de patrons créationnels • Par classe : utilisent l’héritage afin de déterminer le
type de l’objet à instancier • Par objet : utilisent la délégation afin de déterminer
type de l’objet à instancier
64
Singleton
Singleton (rappel)
• Intention : Il est souvent important pour une classe de n’avoir qu’une instance
• Motivation : • Parfois, nous ne voulons qu’une seule instance
d’une classe dans le système • ex: Collections.emptyList(), un seul
SwingWidgetFactory, etc. • souvent limité à des classes sans état mutable
• Cette instance doit être facilement accessible • On veut limiter le nombre d’instances qui peuvent
être crées
Singleton - Structure 67
Singleton - Implémentation 68
• Assurer une instance unique en cachant le mécanisme de création
• Java : constructeur privé • Garder une référence pour l’instance unique
• Java : attribut statique privé • Créer un point d’accès publique
• Java : une méthode qui retourne l’instance unique
Bruno Dufour - Université de Montréal
Singleton - Implémentation 69
private LazySingleton() {}
Bruno Dufour - Université de Montréal
Singleton - C++ 70
~Singleton(){ ... } }
Singleton - JavaScript
var instance;
71
Singleton - Variations 72
public static RoundRobinSingleton getInstance() { RoundRobinSingleton instance = instances[next]; if (instance == null) { instance = new RoundRobinSingleton(); instances[next] = instance; } next = (next + 1) % instances.length; return instance; } }
Bruno Dufour - Université de Montréal
Singleton - Variations 73
public class Symbol { private static Map<String,Symbol> symbols = new HashMap<>(); private String key;
public Symbol(String key) { this.key = key; } public String getKey() { return key; }
public static Symbol getSymbol(String key) { Symbol instance = symbols.get(key); if (instance == null) { instance = new Symbol(key); symbols.put(key, instance); } return instance; } }
Factory
Bruno Dufour - Université de Montréal
Abstraire la création d’objet
• new crée un objet concret (mécanisme inflexible) • introduit une dépendance vers une classe concrète
plutôt qu’une abstraction ou interface (DIP) • lors d’un changement dans le système, ces
dépendances sont plus susceptibles de propager le changement
• Le patron Factory rend la création d’objet plus abstraite
• remplace new par un appel de méthode qui retourne un objet
• cette méthode retourne une interface (abstraction) plutôt qu’un type concret
75
Exemple - Sans Factory
while (i.hasNext()) { System.out.println(i.next()); }
Exemple - Sans Factory
while (i.hasNext()) { System.out.println(i.next()); }
Exemple - Avec Factory
while (i.hasNext()) { System.out.println(i.next()); }
78
La liste concrète peut maintenant décider du type concret d’itérateur à utiliser.
La méthode iterator() retourne une abstraction (Iterator) plutôt qu’un type concret.
Bruno Dufour - Université de Montréal
Factory Method
• Intention : fournir une interface pour la création d’objets, mais en laissant les sous-classes décider du type concret d’objet à créer
• Motivation : • Une classe est incapable d’anticiper le type
d’objets qu’elle doit créer • Une classe désire laisser le choix du type d’objets
créés à ses sous-classes
Bruno Dufour - Université de Montréal
Factory Method pour la réutilisation
• Les frameworks nécessitent souvent des interfaces pour les classes d’application qu’ils doivent manipuler
• ex : documents dans une application MDI
80
public interface Document {...}
public class TextEditor extends Application { protected Document createDocument() { return new PlainTextDocument(); } }
public class WordProcessor extends Application { protected Document createDocument() { return new RichTextDocument(); } }
81
Factory method - Structure 82
Factory Method - Paramètres 83
• Le patron Factory Method peut prendre des paramètres pour plus de flexibilité
• this est déjà un paramètre implicite
public abstract class Application { public Document newDocument() { ... } public Document openDocument() { ... } protected abstract Document createDocument(String title); }
public class TextEditor extends Application { protected Document createDocument(String title) { return new PlainTextDocument(title + “.txt”); } }
Bruno Dufour - Université de Montréal
Créer des familles d’objets
• Dans certains cas, il faut créer une multitude d’objets différents, mais de la même famille, plutôt qu’un seul objet
• Les objets concrets sont reliés, mais on désire pour choisir entre les familles au cours de l’exécution
• La famille constitue une abstraction dans le système
84
Exemple de familles d’objets 85
Source: Tyler Burton Bruno Dufour - Université de Montréal
Factory method et familles 86
• Le patron Factory Method n’offre qu’une solution partielle:
• Factory Method n’a aucune notion de famille • On ne veut pas mélanger des widgets Windows,
Linux ou Mac OS! • Abstract Factory étend le mécanisme pour inclure les
familles d’objets
Bruno Dufour - Université de Montréal
Abstract Factory - Exemple
public class GTKWidgetFactory implements WidgetFactory { ... } public class MacOSWidgetFactory implements WidgetFactory { ... } public class MSWinWidgetFactory implements WidgetFactory { ... }
public class ApplicationWindow { public void create(WidgetFactory factory) { Button okButton = factory.newButton(“OK”); Button cancelButton = factory.newButton(“Cancel”); TextField text = factory.newTextField(); ... } }
87
Tous les éléments de l’interface pour cette fenêtre appartiendront à la même famille
Bruno Dufour - Université de Montréal
Abstract Factory - Structure 88
Dependency Injection 89
• La technique de Dependency Injection est une alternative à Factory dans bien des cas
• Vise à remplacer des dépendances statiques par des dépendances dynamiques
• Plusieurs implémentations populaires (Spring, Guice, PicoContainer, etc.)
• Utilisations courantes: • Chargement dynamique de plugins • Création d’instances ou de stubs lors de tests
automatisés
Dependency Injection - Guice 90
  @Inject   RealBillingService(CreditCardProcessor processor,       TransactionLog transactionLog) {     this.processor = processor;     this.transactionLog = transactionLog;   } }
Dépendances abstraites (interfaces)
Bruno Dufour - Université de Montréal
Dependency Injection - Guice 91
Crée une instance de VisaCreditCardProcessor, tel que défini par BillingModule (= factory)
Builder
Builder
• Parfois, les programmes doivent créer des objets complexes
• Si on utilise new, il faut passer toute l’information nécessaire au constructeur lors de la création
• Difficile, souvent beaucoup variations possibles • Comme pour Factory, l’algorithme de création peut
être indépendant des parties qui composent l’objet
93
Exemple - Sans Builder 94
• Créer un processus • Commande à exécuter • Arguments (String[], Collection?) • Dossier courant (String, File?) • Environnement (Map) • Redirection stdin, stdout, stderr (File, Stream?)
• Seule la commande à exécuter est absolument nécessaire; les autres paramètres ont tous des valeurs par défaut raisonnables
• Combien de constructeurs différents ?
162!!
Exemple - Avec Builder public final class ProcessBuilder {
public ProcessBuilder(String command) {...}
public ProcessBuilder directory(String directory) {...} public ProcessBuilder directory(File directory) {...}
public ProcessBuilder (File directory) {...}
...
Exemple - Utilisation
96
Builder
• Intention : séparer la construction d’un objet de sa représentation
• permet la construction de plusieurs représentations différentes à partir du même processus de construction
• permet de construire un objet complexe indépendamment des parties qui le composent et de leur agencement
97
Builder - Structure 98
Exemple - Builder 99
Builder - Performance 100
builder.append(“Hello. My name is ”); builder.append(speaker); builder.append(“. ”);
builder.append(“You ”); builder.append(action); builder.append(“. ”);
return builder.toString(); }
Patrons structuraux Façade
Façade 103
Façade&
• Intention : Fournir une interface unie pour l’ensemble des interfaces d’un sous-système afin de réduire la complexité tout en maintenant la fonctionnalité
Bruno Dufour - Université de Montréal
Façade 104
Source:(Paul(Mayne Source:(Logitech
Façade - Exemple 105
public class HomeTheaterFacade { private Amplifier amp; private Tuner tuner; private DvdPlayer dvd; private CdPlayer cd; private Projector projector; private TheaterLights lights; private Screen screen;
public void watchMovie() { System.out.println("Get ready to watch a movie..."); lights.dim(10); screen.down(); projector.on(); projector.wideScreenMode(); amp.on(); amp.setInput(dvd); amp.setSurroundSound(); amp.setVolume(5); dvd.on(); } }
Bruno Dufour - Université de Montréal
Façade - Exemple 106
Decorator - Motivation 108
• Considérons une boîte de texte avec • une bordure • des barres de défilement
Bruno Dufour - Université de Montréal
Decorator - Motivation 109
• 3 responsabilités • affichage du texte • affichage de la bordure • affichage des barres de défilement
• Supposons que la bordure et les barres de défilement sont appelées à changer
• ex: 2 types de bordures (Plain, 3D) • ex: barre de défilement horizontale, verticale ou les
deux à la fois
Exemple - Sans Decorator
• HorizPlainTextView, VertPlainTextView, BothPlainTextView, Horiz3DTextView, Vert3DTextView, Both3DTextView
• Et si on ajoute un autre type de bordure ? Une autre responsabilité ?
• Problèmes • Explosion du nombre de classes • Chaque sous-classe correspond à un ensemble de
choix fixés au moment de la compilation (rigide)
110
Exemple - Sans Decorator (#2)
public class TextView extends Component { private Border border; private Scrollbar sb;
public TextView(Border border, Scrollbar sb) { this.border = border; this.sb = sb; }
public void draw() { border.draw(); sb.draw(); // draw TextView itself } }
111
Exemple - Sans Decorator (Strategy)
public class TextView extends Component { private Border border; private Scrollbar sb;
public TextView(Border border, Scrollbar sb) { this.border = border; this.sb = sb; }
public void draw() { border.draw(); sb.draw(); // draw TextView itself } }
112
La classe TextView a été modifiée, et dépend maintenant de Border et Scrollbar (OCP)
Et si on voulait ajouter une autre fonctionnalité ? On devrait modifier TextView (OCP)
Utilise la délégation pour implémenter les différentes responsabilités.
Bruno Dufour - Université de Montréal
Exemple - Avec Decorator 113
• Le patron Decorator renverse la solution précédente • Nous allons décorer l’objet TextView avec de
nouvelles fonctionnalités
Exemple - TextView
public class TextView extends Component { public void draw() { // draw TextView itself. } }
public class PlainBorder { private Component component;
public PlainBorder(Component component) { this.component = component; }
public void draw() { component.draw(); // Draw the border itself. } }
114
Decorator délègue à TextView
Chaîner les décorateurs public class TextView extends Component { public void draw() { // draw TextView itself. } }
public class VertScrollbar extends Component { private Component component;
public PlainBorder(Component component) {...}
public PlainBorder(Component component) {...}
Exemple - Hiérarchie 116
...
VertScrollbar PlainBorder TextView
Decorator
• Intention : Ajouter des responsabilité à un objet durant l’exécution
• Alternative à l’héritage • Motivation :
• Permet d’ajouter des responsabilités à des objets de façon dynamique et transparente
• Permet de retirer des responsabilités • Fournit une alternative à l’héritage dans les cas où
plusieurs responsabilités indépendantes causeraient une explosion du nombre de classes
118
Decorator - Structure 119
Decorator - Exemple (JDK) 120
Decorator - Exemple (JDK) 121
Permet de lire un fichier
Améliore la performance de la lecture à l’aide de mémoire tampon
Permet de compter le nombre de lignes lues
Bruno Dufour - Université de Montréal
Decorator - Ajout de responsabilité 122
public class LowerCaseInputStream extends FilterInputStream { public LowerCaseInputStream(InputStream in) { super(in); }
public int read() throws IOException { int c = super.read(); return (c == -1 ? c : Character.toLowerCase((char)c)); }
public int read(byte[] b, int off, int len) throws IOException { int result = super.read(b, off, len); for (int i = off; i < off+result; i++) { b[i] = (byte) Character.toLowerCase((char)b[i]); } return result; } }
Adapter
Adapter (Structurel)
• Problème • Convertir l’interface d’une classe en une autre
interface attendue par le client (interface cible) afin de permettre à des classes incompatibles de travailler de concert
• Souvent motivé par la réutilisation de code: le code réutilisé doit se conformer à une interface requise
• Solutions • Par classe: héritage multiple / interfaces • Par objet: composition
124
Adapter – Par classe 125
Adapter – Par objet 126
Adapter - Exemple 127
Adapter - Exemple 128
public interface Enumeration<T> { public boolean hasMoreElements(); public T nextElement(); }
public interface Iterator<T> { public boolean hasNext(); public T next(); public void remove(); }
Bruno Dufour - Université de Montréal
Adapter - Exemple 129
public class EnumerationIterator<T> implements Iterator<T> { private Enumeration<T> e;
public EnumerationIterator(Enumeration<T> e) { this.e = e; }
public boolean hasNext() { return e.hasMoreElements(); }
public T next() { return e.nextElement(); }
public void remove() { throw new UnsupportedOperationException(); } }
Patrons comportementaux
Strategy
• Intention : permettre de choisir dynamiquement un algorithme parmi une famille d’algorithmes interchangeables
132
Strategy - Exemple 133
Bruno Dufour - Université de Montréal
Strategy - Structure 134
State
• Intention : Permettre à un objet de changer son comportement quand son état interne change
136
State - Motivation
• Permet de partitionner le comportement spécifique à un état pour les états complexes
• Moins compact, plus de classes • Rend les transitions d’état explicites • Permet de partager les objets état
137
State - Structure 138