pdf/4

Transcription

pdf/4
Patrons de conception
Introduction
Patrons de conception (Design patterns)
Programmation et Conception Orientées Objet
Bertrand Estellon
Département Informatique et Interactions
Aix-Marseille Université
▶
Solutions standards pour répondre aux problèmes rencontrés en
conception orientée objet
▶
Ils tendent à respecter les 5 principes SOLID
▶
Ils sont le plus souvent indépendants du langage de programmation
▶
Ils ont été formalisés dans le livre du “Gang of Four” ( Erich Gamma,
Richard Helm, Ralph Johnson et John Vlissides – 1995)
▶
“Les patrons offrent la possibilité de capitaliser un savoir précieux né
du savoir-faire d’experts” (Buschmann – 1996)
▶
Les anti-patrons (ou anti-patterns) sont des erreurs courantes de
conception.
17 novembre 2016
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
1 / 466
Bertrand Estellon (DII – AMU)
Introduction
Prog. et Conception Orientées Objet
Patrons de conception
Catégories de patrons de conception
17 novembre 2016
344 / 466
Introduction
Quelques patrons de conception
Comportement
▶
▶
▶
▶
Description de la fabrication des objets (création, initialisation)
Regroupement et séparation du code de fabrication des objets
Création
Structure :
▶
▶
▶
Structure
Création :
Rendre les connexions indépendantes des évolutions futures
Découplage de l’application avec des interfaces
Comportement :
▶
Coder proprement le comportement et les interactions entre les objets
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
345 / 466
Factory
Abstract Factory
Builder
Prototype
...
Bertrand Estellon (DII – AMU)
Adapter
Proxy
Composite
Decorator
Facade
Flyweight
Bridge
...
Prog. et Conception Orientées Objet
Template Method
Strategy
Observer
Callback
Chain of Responsibility
State
Iterator
Visitor
Command
Mediator
Memento
Interpreter
...
17 novembre 2016
346 / 466
Patrons de conception
Factory
Patrons de conception
Factory
Factory
Supposons que nous disposions de l’interface et la classe suivante :
Supposons que nous souhaitions ajouter un nouveau bouton sans modifier
le précédent :
public interface Button {
public void draw();
}
public class SimpleButton implements Button
public void draw() {
System.out.println("Simple button.");
}
}
public class ModernButton implements Button
public void draw() {
System.out.println("Modern button.");
}
}
{
La classe SimpleButton est instanciée dans de nombreuses autres classes.
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
347 / 466
{
Afin d’utiliser ce bouton, nous devons modifier toutes les instanciations
présentes dans notre code ⇒ violation de OCP.
Bertrand Estellon (DII – AMU)
Factory
Prog. et Conception Orientées Objet
Patrons de conception
Factory
17 novembre 2016
348 / 466
Factory
Factory
Une fabrique consiste à isoler la création des objets de leurs utilisations :
Client
- productA : ProductA
- productB : ProductB
public class ButtonFactory {
public Button createButton() {
return new SimpleButton();
}
}
+ Client(factory : Factory)
Toutes les instanciations doivent se faire via cette classe. Les modifications
nécessaires à l’utilisation de la classe ModernButton sont isolées :
public class ButtonFactory {
public Button createButton() {
return new ModernButton();
}
}
Bertrand Estellon (DII – AMU)
Factory
Prog. et Conception Orientées Objet
Factory
+ createProductA() : ProductA
+ createProductB() : ProductB
≪interface≫
ProductB
≪interface≫
ProductA
ProductB2
17 novembre 2016
349 / 466
Bertrand Estellon (DII – AMU)
ProductB1
ProductA1
Prog. et Conception Orientées Objet
ProductA2
17 novembre 2016
350 / 466
Patrons de conception
Abstract Factory
Patrons de conception
Abstract Factory
Abstract Factory
Abstract Factory
Imaginons que la fabrique fournisse différents éléments :
Le code suivant semble résoudre le problème :
public class Factory {
public Button createButton() { return new SimpleButton(); }
public TextBox createTextBox() { return new SimpleTextBox(); }
public List createList() { return new SimpleList(); }
}
public class Factory {
private String type;
public Factory(String type) { this.type = type; }
public Button createButton() {
if (type.equals("modern")) return new ModernButton();
else return new SimpleButton();
}
Pour passer du style simple au moderne, il nous faut changer toutes les
méthodes de la fabrique, ce qui est une violation de OCP :
public class Factory {
public Button createButton() { return new ModernButton(); }
public TextBox createTextBox() { return new ModernTextBox(); }
public List createList() { return new ModernList(); }
}
public TextBox createTextBox() {
if (type.equals("modern")) return new ModernTextBox();
else return new SimpleTextBox();
}
/* Idem pour createList. */
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
351 / 466
Bertrand Estellon (DII – AMU)
Abstract Factory
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
Abstract Factory
Abstract Factory
Que se passe-t-il nous avons besoin d’introduire un nouveau type :
Les fabriques abstraites permettent de corriger ce défaut :
public interface AbstractFactory {
public Button createButton();
public TextBox createTextBox();
public List createList();
}
public class Factory {
private String type;
public Factory(String type) { this.type = type; }
public Button createButton() {
if (type.equals("modern")) return new ModernButton();
else if (type.equals("simple")) return new SimpleButton();
else return new OtherButton();
}
/* Idem pour createTextBox et createList. */
}
Toutes les méthodes de la fabrique doivent être modifiées, ce qui est une
nouvelle violation de OCP.
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
352 / 466
Abstract Factory
353 / 466
Les classes qui devront instancier des boutons, des boites de texte ou des
listes auront à leur disposition une instance d’une classe qui implémente
l’interface ButtonFactory :
AbstractFactory factory = new SimpleFactory();
/* new ModernFactory(); */
Button button = factory.createButton();
/* ... */
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
354 / 466
Patrons de conception
Abstract Factory
Patrons de conception
Abstract Factory
Abstract Factory
On implémente ensuite une fabrique par type :
Client
- productA : ProductA
- productB : ProductB
public class SimpleFactory implements AbstractFactory {
public Button createButton() { return new SimpleButton(); }
public TextBox createTextBox() { return new SimpleTextBox(); }
public List createList() { return new SimpleList(); }
}
+ Client(factory : AbstractFactory)
≪interface≫
AbstractFactory
+ createProductA() : ProductA
+ createProductB() : ProductB
public class ModernFactory implements AbstractFactory {
public Button createButton() { return new ModernButton(); }
public TextBox createTextBox() { return new ModernTextBox(); }
public List createList() { return new ModernList(); }
}
≪interface≫
ProductB
L’ajout d’un nouveau type s’effectue par l’ajout d’une nouvelle classe, ce
qui respecte OCP.
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
355 / 466
Factory1
Factory2
+ createProductA() : ProductA
+ createProductB() : ProductB
+ createProductA() : ProductA
+ createProductB() : ProductB
ProductB2
Bertrand Estellon (DII – AMU)
Builder
ProductB1
ProductA1
≪interface≫
ProductA
ProductA2
Prog. et Conception Orientées Objet
Patrons de conception
Builder
17 novembre 2016
356 / 466
Builder
Builder
Problématique :
public class User {
/* ... */
public class User {
private final String firstName;
private final String lastName;
private final int age;
private final String phone;
private final String address;
public User(String firstName, String lastName) {
this(firstName, lastName, 0);
}
public User(String firstName, String lastName, int age) {
this(firstName, lastName, age, "");
}
public User(String firstName, String lastName,
int age, String phone, String address) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
/* etc. */
}
public User(String firstName, String lastName,
int age, String phone) {
this(firstName, lastName, age, phone, "");
}
}
}
Bertrand Estellon (DII – AMU)
Abstract Factory
Prog. et Conception Orientées Objet
17 novembre 2016
357 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
358 / 466
Patrons de conception
Builder
Patrons de conception
Builder
Builder
Builder
Dans le code donné précédemment :
Obligation de fournir le nom et le prénom :
▶
Beaucoup de constructeurs différents ;
▶
Ce nombre de constructeurs risque d’augmenter ;
▶
⇒ Violation de OCP (et de SRP).
public class UserBuilder {
private
private
private
private
private
Solution :
Séparer la construction de l’objet sa représentation
public UserBuilder(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Avantages :
▶
Proposer une interface pratique pour construire les objets ;
▶
Contrôler les étapes de création de l’objet ;
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
final String firstName;
final String lastName;
int age = 0;
String phone = "";
String address = "";
}
17 novembre 2016
359 / 466
Bertrand Estellon (DII – AMU)
Builder
Prog. et Conception Orientées Objet
Patrons de conception
Builder
Builder
Setters pour les champs facultatifs :
Méthode de construction de l’objet :
360 / 466
public class UserBuilder {
public class UserBuilder {
private
private
private
private
private
17 novembre 2016
Builder
public User build() {
/* Possibles vérifications avant de construire. */
return new User(firstName, lastName, age, phone, address);
}
final String firstName;
final String lastName;
int age = 0;
String phone = "";
String address = "";
}
public UserBuilder setAge(int age) {
this.age = age;
return this;
}
Utilisation :
User user = new UserBuilder("machin", "truc")
.setAge(25)
.setAddress("Rue de machin truc")
.build();
/* idem pour setPhone et setAddress */
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
361 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
362 / 466
Patrons de conception
Builder
Patrons de conception
Builder
Builder
Builder
Avec une classe interne :
StringBuilder :
public class User {
private User(Builder builder) {
this.firstName = builder.firstName; /* ... */
}
StringBuilder builder = new StringBuilder("aaa");
builder.append("toto");
builder.append("abc");
String s = builder.toString();
public class Builder {
private final String firstName; /* ... */
Système de notification d’Android :
public User build() { return new User(this); }
Notification notification = new Notification.Builder(context)
.setContentTitle("Message de " + sender.toString())
.setContentText(subject)
.setSmallIcon(R.drawable.new_mail)
.setLargeIcon(bitmap)
.build();
}
}
Utilisation :
User user = new User.Builder("a", "b").setAge(25).build();
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
363 / 466
Bertrand Estellon (DII – AMU)
Builder
Prog. et Conception Orientées Objet
Patrons de conception
Builder
17 novembre 2016
364 / 466
Adapter
Adapter
Supposons que la classe suivante existe :
public class Drawer {
public void draw(Pencil pencil) {
pencil.drawLine(0,0,10,10);
pencil.drawCircle(5,5,5);
pencil.drawLine(10,0,0,10);
}
}
Builder
Product
Client
+
+
+
+
Builder(value)
setValue1(value) : Builder
setValue2(value) : Builder
build() : Product
Nous avons également l’interface suivante :
- Product(builder : Builder)
public interface Pencil {
public void drawLine(int x1, int y1, int x2, int y2);
public void drawCircle(int x, int y, int radius);
}
Cette classe et cette interface ne peuvent pas être modifiées.
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
365 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
366 / 466
Patrons de conception
Adapter
Patrons de conception
Adapter
Adapter
Adapter
Pour ce faire, nous définissons l’adaptateur suivant :
Nous avons également la classe Crayon suivante :
public class CrayonAdapter implements Pencil {
public class Crayon {
public void dessinerLigne(int x1, int y1, int x2, int y2) {
/* ... */
}
private Crayon crayon;
public CrayonAdapter(Crayon c) { this.crayon = c; }
public void drawLine(int x1, int y1, int x2, int y2) {
crayon.dessinerLigne(x1, y1, x2, y2);
}
public void dessinerCercle(Point center, int rayon) {
/* ... */
}
}
public void drawCircle(int x, int y, int radius) {
crayon.dessinerCercle(new Point(x, y), radius);
}
Nous souhaitons utiliser la classe Crayon comme un Pencil pour qu’elle
puisse être utilisée par la classe Drawer.
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
367 / 466
Adapter
Bertrandclasse
Estellon (DII
AMU)
Prog.
Conception
Orientées Objet
Cette
est– utilisable
de
laetfaçon
suivante
:
Patrons de conception
17 novembre 2016
368 / 466
Adapter
Pencil pencil = new CrayonAdapter(new Crayon());
Drawer drawer = new Drawer();
Adapter
drawer.draw(pencil);
Adapter
Client
Cette classe est utilisable de la façon suivante :
≪interface≫
Target
+ requiredMethod()
Pencil pencil = new CrayonAdapter(new Crayon());
Drawer drawer = new Drawer();
drawer.draw(pencil);
Adapter
Adaptee
- adaptee : Adaptee
+ requiredMethod()
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
369 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
+ otherMethod()
17 novembre 2016
370 / 466
Patrons de conception
Proxy
Patrons de conception
Proxy
Proxy
Proxy
Supposons que nous avons la classe suivante :
Faisons en sorte que la classe précédente implémente la classe suivante : s
public interface Stack {
public void push(int value);
public int pop();
public int size();
public int get(int index);
}
public class ArrayStack {
private int[] stack = new int[10];
private int size = 0;
public void push(int value) {
stack[size] = value;
size++;
}
Nous souhaitons pouvoir rendre une pile non modifiable :
Stack stack = new ArrayStack(20);
stack.push(2);
stack.push(4);
stack = new UnmodifiableStack(stack);
/* stack.push et stack.pop interdit (à l'exécution). */
public int pop() { size--; return stack[size]; }
public int size() { return size; }
public int get(int index) { return stack[index]; }
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
371 / 466
Bertrand Estellon (DII – AMU)
Proxy
Prog. et Conception Orientées Objet
Patrons de conception
Proxy
17 novembre 2016
372 / 466
Proxy
Proxy
Pour ce faire, nous définissons un proxy :
public class UnmodifiableStack implements Stack {
private Stack stack;
≪interface≫
Subject
Client
+ doAction()
public UnmodifiableStack(Stack stack) { this.stack = stack; }
public void push(int value) {
throw new UnsupportedOperationException();
}
public int pop() { throw new UnsupportedOperationException(); }
Proxy
public int size() { return stack.size(); }
public int get(int index) { return stack.get(i); }
+ doAction()
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
RealSubject
17 novembre 2016
373 / 466
Bertrand Estellon (DII – AMU)
delegate
Prog. et Conception Orientées Objet
+ doAction()
17 novembre 2016
374 / 466
Patrons de conception
Composite
Patrons de conception
Composite
Composite
Composite
Considérons les classes et l’interface suivantes :
Nous pouvons définir un groupe de formes de la façon suivante :
public interface Shape { void draw(GraphicsContext gc); }
public class Group implements Shape {
private List<Shape> shapes;
public class Circle implements Shape {
private double x, y, radius;
public Group(Shape... shapes) {
this.shapes = new ArrayList<>();
for (Shape shape : shapes) this.shapes.add(shape);
}
public Circle(double x, double y, double radius) { /* ... */ }
public void draw(GraphicsContext gc) { / * ... */ }
}
public class Rectangle implements Shape {
private double x, y, radius;
public void draw(GraphicsContext gc) {
for (Shape shape : shapes) shape.draw(gc);
}
public Rectangle(double x1, double y1,
double x2, double y2) { /* ... */ }
public void draw(GraphicsContext gc) { / * ... */ }
}
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
375 / 466
Bertrand Estellon (DII – AMU)
Composite
Prog. et Conception Orientées Objet
Patrons de conception
Composite
Composite
Considérons les classes et l’interface suivantes :
Nous souhaitons définir des opérateurs :
376 / 466
public class Sqrt implements Formula {
private final Formula operand;
public interface Formula { double asValue(); }
public class Variable implements Formula {
private double value;
public Sqrt(Formula operand) { this.operand = operand; }
public double asValue() { return Math.sqrt(operand.asValue()); }
}
public Variable(double value) { setValue(value); }
public void setValue(double value) { this.value = value; }
public double asValue() { return value; }
public class Sum implements Formula {
private final Formula[] operands;
}
public Sum(Formula... operands) { this.operands = operands; }
public double asValue() {
double sum = 0;
for (Formula operand : operands) sum += operand.asValue();
return sum;
}
public class Constant implements Formula {
private final double value;
public Constant(double value) { this.value = value; }
public double asValue() { return value; }
}
Bertrand Estellon (DII – AMU)
17 novembre 2016
Composite
}
Prog. et Conception Orientées Objet
17 novembre 2016
377 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
378 / 466
Patrons de conception
Composite
Patrons de conception
Composite
Composite
≪interface≫
Component
▶
Nous souhaitons définir une structure d’arbre (ou de DAG) ;
▶
Tous les objets possèdent une interface commune ;
▶
Nous avons des opérations à faire récursivement.
parent 1
Composite
Leaf
+ doAction()
Prog. et Conception Orientées Objet
Patrons de conception
0..*
child
+ doAction()
Dans les deux exemples précédents :
Bertrand Estellon (DII – AMU)
Composite
17 novembre 2016
379 / 466
Bertrand Estellon (DII – AMU)
Decorator
- children : List<Component>
+
+
+
+
doAction()
addChild(component : Component)
removeChild(component : Component)
getChildren() : List<Component>
Prog. et Conception Orientées Objet
Patrons de conception
Decorator
17 novembre 2016
380 / 466
Decorator
Decorator
Nous souhaitons ajouter des logs pour débugger notre programme :
Supposons que nous avons la classe suivante :
public class ArrayStack {
private int[] stack = new int[10];
private int size = 0;
public class ArrayStack {
private int[] stack = new int[10];
private int size = 0;
public void push(int value) {
System.out.println("push("+value+")");
list[size] = value; size++;
}
public void push(int value) {
list[size] = value;
size++;
}
public int pop() {
System.out.println("pop()");
size--; return list[size];
}
public int pop() { size--; return list[size]; }
}
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
381 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
382 / 466
Patrons de conception
Decorator
Patrons de conception
Decorator
Decorator
Cette modification a été réalisée en modifiant une classe existante. De
plus, une nouvelle modification est nécessaire pour retirer les logs.
Il suffit alors de définir un décorateur :
public class VerboseStack implements Stack {
private Stack stack;
Définissons l’interface suivante :
public VerboseStack(Stack stack) { this.stack = stack; }
public interface Stack {
public void push(int value);
public int pop();
}
public void push(int value) {
System.out.println("push("+value+")");
stack.push(value);
}
Faisons en sorte que ArrayStack implémente cette interface :
public int pop() {
System.out.println("pop()");
return stack.pop();
}
public class ArrayStack implements Stack {
/* ... */
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
Decorator
}
17 novembre 2016
383 / 466
Bertrand Estellon (DII – AMU)
Decorator
Prog. et Conception Orientées Objet
Patrons de conception
Decorator
17 novembre 2016
384 / 466
Decorator
Decorator
Supposons que nous ayons le code suivant :
Nous définissons un nouveau décorateur :
Stack stack = new ArrayStack(10);
stack.push(2);
stack.pop();
public class CounterStack implements Stack {
private Stack stack;
private int size;
Il est très facile d’introduire le décorateur :
public VerboseStack(Stack stack) {
this.stack = stack; size = 0;
}
Stack stack = new ArrayStack(10);
stack = new VerboseStack(stack);
stack.push(2);
stack.pop();
public void push(int value) { size++; stack.push(value); }
public int pop()
{ size--; return stack.pop(); }
public int getSize()
{ return size; }
Ce code produit la sortie suivante :
}
push(2);
pop();
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
385 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
386 / 466
Patrons de conception
Decorator
Patrons de conception
Decorator
Decorator
Decorator
Il est possible d’utiliser plusieurs décorateurs simultanément :
Stack stack = new ArrayStack(10);
stack = new VerboseStack(stack);
CounterStack counterStack = new CounterStack(stack);
stack = counterStack;
stack.push(2); stack.push(3);
stack.pop();
System.out.println(counterStack.getSize());
Un exemple d’utilisateur du décorateur précédent :
Stack stack = new ArrayStack(10);
CounterStack counterStack = new CounterStack(stack);
stack = counterStack;
stack.push(2); stack.push(3);
stack.pop();
System.out.println(counterStack.getSize());
Ce code produit la sortie suivante :
Ce code produit la sortie suivante :
push(2);
push(3);
pop();
1
1
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
387 / 466
Bertrand Estellon (DII – AMU)
Decorator
Prog. et Conception Orientées Objet
Patrons de conception
Decorator
17 novembre 2016
388 / 466
Template Method
Template Method
Le patron de méthode permet de faire partager un comportement par
plusieures classes :
≪interface≫
Component
+doAction()
void doAction() {
component.doAction() ;
doNewAction() ;
}
public abstract class List {
private int[] list = new int[10]; private int size = 0;
public void add(int value) { list[size] = value; size++; }
Decorator
ConcreteComponent
public int eval() {
int result = neutral(); // util. d'une méthode abstraite
for (int i = 0; i < list.length; i++)
result = compute(result, list[i]); // idem
return result;
}
- component : Component
+ doAction()
# doNewAction()
+ doAction()
ConcreteDecorator1
ConcreteDecorator2
# doNewAction()
# doNewAction()
protected abstract int neutral(); // méthode abstraite
protected abstract int compute(int a, int b); // idem
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
389 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
390 / 466
Patrons de conception
Template Method
Patrons de conception
Template Method
Template Method
Template Method
La classe abstraite est alors étendue de la façon suivante :
Dans l’exemple précédent :
public class ListSum extends List {
public int neutral() { return 0; }
public int compute(int a, int b) { return a+b; }
}
public class ListProduct extends List {
public int neutral() { return 1; }
public int compute(int a, int b) { return a*b; }
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
391 / 466
▶
la classe List est abstraite ;
▶
la méthode eval est une méthode socle qui utilise neutral et
compute pour définir son comportement ;
▶
les méthodes neutral et compute sont abstraites ;
▶
elles sont définies dans les classes qui étendent List.
Bertrand Estellon (DII – AMU)
Template Method
Prog. et Conception Orientées Objet
Patrons de conception
Template Method
17 novembre 2016
392 / 466
Strategy
Strategy
Il est également possible d’utiliser une stratégie :
AbstractClass
+ doAction()
+ doOperation1()
+ doOperation2()
final void doAction() {
doOperation1() ;
...
doOperation2() ;
}
public interface Strategy {
public int neutral();
public int compute(int a, int b);
}
public class SumStrategy implements Strategy {
public int neutral() { return 0; }
public int compute(int a, int b) { return a+b; }
}
ConcreteClass
public class ProductStrategy implements Strategy {
public int neutral() { return 1; }
public int compute(int a, int b) { return a*b; }
}
+ doOperation1()
+ doOperation2()
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
393 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
394 / 466
Patrons de conception
Strategy
Patrons de conception
Strategy
Strategy
Strategy
On délègue à la stratégie une partie du traitement :
public class List {
/* Propriétés et méthode add */
Utilisation des stratégies :
private Stategy strategy;
List listSum = new List(new SumStrategy());
listSum.add(2); listSum.add(3);
System.out.println(listSum.eval());
List listProduct = new List(new ProductStrategy());
listProduct.add(2); listProduct.add(3);
System.out.println(listProduct.eval());
public List(Stategy strategy) {
this.strategy = strategy;
}
public int eval() {
int result = strategy.neutral();
for (int i = 0; i < list.length; i++)
result = strategy.compute(result, list[i]);
return result;
}
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
395 / 466
Bertrand Estellon (DII – AMU)
Strategy
Prog. et Conception Orientées Objet
Patrons de conception
Strategy
17 novembre 2016
396 / 466
Observer
Observer
Considérons les classes et l’interface suivantes :
Context
- strategy : Strategy
+ setStrategy(strategy : Strategy)
+ doAction()
public interface Shape {
void draw(GraphicsContext gc);
}
≪interface≫
Strategy
public class Rectangle implements Shape {
public double x, y, w, h;
+ doStrategyAction1()
+ doStrategyAction2()
ConcreteStrategy1
ConcreteStrategy2
ConcreteStrategy3
+ doStrategyAction1()
+ doStrategyAction2()
+ doStrategyAction1()
+ doStrategyAction2()
+ doStrategyAction1()
+ doStrategyAction2()
public Rectangle(double x, double y, double w, double h) {
this.x = x; this.y = y; this.w = w; this.h = h;
}
@Override
public void draw(GraphicsContext gc) {
gc.strokeRect(x, y, w, h);
}
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
397 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
398 / 466
Patrons de conception
Observer
Patrons de conception
Observer
Observer
Observer
On veut pouvoir écrire le code suivant :
public class Main extends Application { /* ... */
public class ShapeContainer extends Canvas {
final List<Shape> shapes;
public void start(Stage primaryStage) throws Exception {
ShapeContainer container = new ShapeContainer();
Rectangle rectangle = new Rectangle(10, 10, 100, 100);
rectangle.addOnAction(this::handleRectangleClicked);
container.add(rectangle);
/* ... */
}
public ShapeContainer() { shapes = new ArrayList<>(); }
void add(Shape shape) { shapes.add(shape); repaint(); }
public void repaint() {
GraphicsContext context = getGraphicsContext2D();
context.clearRect(0, 0, getWidth(), getHeight());
for (Shape shape : shapes) shape.draw(context);
}
void handleRectangleClicked(ActionEvent actionEvent) {
Shape shapeClicked = actionEvent.source();
/* shapeClicked a été cliqué par l'utilisateur */
}
}
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
399 / 466
Bertrand Estellon (DII – AMU)
Observer
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
Observer
Observer
J’observe les événements de type MouseEvent de JavaFX :
Je fais en sorte que les formes puissent recevoir cet événement :
public class ShapeContainer extends Canvas {
public interface Shape {
void draw(GraphicsContext gc);
boolean handleMouseEvent(MouseEvent event);
}
public ShapeContainer() {
/* ... */
setOnMouseClicked(this::handleMouseClicked);
}
Je définis la classe ActionEvent :
private void handleMouseClicked(MouseEvent event) {
for (int i = shapes.size() - 1 ; i >= 0 ; i--) {
Shape shape = shapes.get(i);
boolean eventHandled = shape.handleMouseEvent(event);
if (eventHandled) return;
}
}
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
400 / 466
Observer
401 / 466
public class ActionEvent {
private final Shape source;
public ActionEvent(Shape source) { this.source = source; }
public Shape source() { return source; }
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
402 / 466
Patrons de conception
Observer
Patrons de conception
Observer
Observer
Observer
J’implémente la nouvelle méthode dans Rectangle :
J’ajoute les méthodes pour gérer la liste des ActionHandler :
public class Rectangle extends Shape {
public double x, y, w, h;
public class Rectangle extends ActionableShape {
private final List<ActionHandler> handlers;
public boolean contains(double px, double py) {
return x <=px && y <= py && px <= x + w && py <= y + h;
}
public Rectangle(double x, double y, double w, double h) {
this.handlers = new ArrayList<>();
/* ... */
}
@Override
public boolean handleMouseEvent(MouseEvent event) {
if (this.contains(event.getX(), event.getY())) {
this.notifyAction(new ActionEvent(this));
return true;
}
return false;
}
public void addOnAction(ActionHandler handler) {
handlers.add(handler);
}
public void removeOnAction(ActionHandler handler) {
handlers.remove(handler);
}
}
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
403 / 466
Bertrand Estellon (DII – AMU)
Observer
Observer
17 novembre 2016
404 / 466
Observer
Observer
public abstract class ActionableShape implements Shape {
List<ActionHandler> handlers;
J’implémente la méthode notifyAction :
public class Rectangle extends ActionableShape {
private final List<ActionHandler> handlers;
public ActionableShape() { this.handlers = new ArrayList<>(); }
public void addOnAction(ActionHandler handler) {
handlers.add(handler);
}
/* ... */
public void notifyAction(ActionEvent event) {
for(ActionHandler handler : handlers)
handler.handle(event);
}
public void removeOnAction(ActionHandler handler) {
handlers.remove(handler);
}
public void notifyAction(ActionEvent event) {
for(ActionHandler handler : handlers)
handler.handle(event);
}
/* ... */
}
Quel principe SOLID est violé par le code de Rectangle ?
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
Prog. et Conception Orientées Objet
17 novembre 2016
}
405 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
406 / 466
Patrons de conception
Observer
Patrons de conception
Observer
Observer
Observer
Je fais en sorte que Rectangle étende cette classe nouvelle classe et je
retire les méthodes déjà implémentées :
public class Rectangle extends ActionableShape {
public double x, y, w, h;
public Rectangle(double x, double y, double w, double h) { /*...*/ }
public boolean contains(double px, double py) { /*...*/ }
public void draw(GraphicsContext gc) { /*...*/ }
public boolean handleMouseEvent(MouseEvent event) { /*...*/ }
}
void notify(event : Event) {
for (Observer observer : observers)
observer.notify(event)
}
Observable
+ addObserver(observer : Observer)
+ removeObserver(observer : Observer)
+ notify(event : Event)
Je peux modifier la classe ActionEvent :
public class ActionEvent {
private final ActionableShape source;
public ActionEvent(ActionableShape source) {this.source = source;}
public ActionableShape source() { return source; }
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
≪interface≫
Observer
- observers : List<Observer>
17 novembre 2016
407 / 466
ConcreteObservable
Bertrand Estellon (DII – AMU)
Callback
0..*
+ handle(event : Event)
ConcreteObserver1
ConcreteObserver2
+ handle(event : Event)
+ handle(event : Event)
Prog. et Conception Orientées Objet
Patrons de conception
Callback – C/C++
17 novembre 2016
408 / 466
Callback
Callback – Java 8
typedef void (*Callback)(int); /* Pointeur sur fonction */
interface Callback { void accept(int c); }
public class MyClass {
void handle1(int c) { printf("%d\n", c); }
void handle2(int c) { printf("* %d *\n", c); }
public static void main(String[] args) {
function(2, MyClass::handle1);
function(4, MyClass::handle2);
}
void function(int d, Callback callback) {
/* ... */ callback(d); /* ... */
}
static void function(int d, Callback callback)
{ /* ... */ callback.accept(d); /* ... */ }
int main(void) {
function(2, handle1);
function(4, handle2);
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
static void handle1(int c) { System.out.println(c);
}
static void handle2(int c) { System.out.println("* "+c+" *");
}
}
17 novembre 2016
409 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
410 / 466
Patrons de conception
Callback
Patrons de conception
Callback – C#
Chain of Responsibility
Chain of Responsibility
public delegate void Callback(int c);
Nous souhaitons réaliser un système de Log :
class MyClass {
public static void handle1(int c)
{ Console.WriteLine("{0}",c); }
▶
Plusieurs niveaux de Log : INFO, DEBUG, ERROR
▶
Plusieurs façon d’écrire les messages : console ou fichier
Création du Logger :
public static void handle2(int c)
{ Console.WriteLine("* {0} *",c); }
Logger logger = new FileLogger(Message.Severity.ERROR,
new PrintWriter("log"), null);
logger = new ConsoleLogger(Message.Severity.DEBUG, "[DEBUG]", logger);
logger = new ConsoleLogger(Message.Severity.INFO, "[INFO]", logger);
public static void function(int d, Callback callback) {
/* ... */ callback(d); /* ... */
}
Utilisation du Logger :
static void Main(string[] args) {
Callback c1 = new Callback(handle1); function(2,c1);
Callback c2 = new Callback(handle2); function(4,c2);
}
logger.acceptMessage(new Message(Message.Severity.ERROR, "error"));
logger.acceptMessage(new Message(Message.Severity.DEBUG, "debug"));
logger.acceptMessage(new Message(Message.Severity.INFO, "info"));
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
411 / 466
Bertrand Estellon (DII – AMU)
Chain of Responsibility
Chain of Responsibility
17 novembre 2016
412 / 466
Chain of Responsibility
Chain of Responsibility
public class Message {
public enum Severity {
INFO,
DEBUG,
ERROR
}
public abstract class Logger {
private final Logger successor;
public Logger(Logger successor) {
this.successor = successor;
}
public boolean handle(Message message) {
boolean messageAccepted = acceptMessage(message);
if (messageAccepted) return true;
if (successor!=null) return successor.handle(message);
return false;
}
public final Severity severity;
public final String text;
public Message(Severity severity, String message) {
this.severity = severity;
this.text = message;
}
public abstract boolean acceptMessage(Message message);
}
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
Prog. et Conception Orientées Objet
17 novembre 2016
413 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
414 / 466
Patrons de conception
Chain of Responsibility
Patrons de conception
Chain of Responsibility
Chain of Responsibility
Chain of Responsibility
public class FileLogger extends Logger {
public final Message.Severity severity;
public final PrintWriter writer;
public class ConsoleLogger extends Logger {
public final Message.Severity severity;
private final String prefix;
public FileLogger(Message.Severity severity, PrintWriter writer,
Logger successor) {
super(successor);
this.severity = severity; this.writer = writer;
}
public ConsoleLogger(Message.Severity severity, String prefix,
Logger successor) {
super(successor);
this.severity = severity; this.prefix = prefix;
}
@Override
public boolean acceptMessage(Message message) {
if (message.severity == severity) {
writer.println(message.text);
return true;
}
return false;
}
@Override
public boolean acceptMessage(Message message) {
if (message.severity == severity) {
System.out.println(prefix+" "+message.text);
return true;
}
return false;
}
}
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
415 / 466
Bertrand Estellon (DII – AMU)
Chain of Responsibility
Prog. et Conception Orientées Objet
Patrons de conception
Chain of Responsibility
17 novembre 2016
416 / 466
State
State
Considérons la classe suivante :
public class Writer {
private int state = 0;
Handler
- successor : Handler
Client
+ setSuccessror(successor : Handler)
+ handle(request : Request)
+accept(request : Request)
public void write(char character) {
switch (state) {
case 0 :
if (character=='{') state = 1;
else System.out.print(character);
break;
case 1:
if (character=='}') state = 0;
else System.out.print(Character.toUpperCase(character));
break;
}
}
successor
ConcreteHandler1
ConcreteHandler2
+ accept(request : Request)
+ accept(request : Request)
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
417 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
418 / 466
Patrons de conception
State
Patrons de conception
State
State
State
Un exemple d’utilisation de la classe précédente :
Pour corriger ces défauts, nous déclarons la classe et l’interface suivantes :
public class WriterContext {
private WriterState state = new WriterState0();
Writer writer = new Writer();
String string = "abc{def}ghi";
for (int index = 0; index < string.length(); index++)
writer.write(string.charAt(index));
public void write(char character) {
state.write(this, character);
}
Le code précédent génère la sortie suivante :
public void changeState(WriterState state) {
this.state = state;
}
abcDEFghi
Notez que si nous souhaitons ajouter de nouveaux états, nous devons
modifier les méthodes de la classe Writer ⇒ violation de OCP.
}
De plus, le fait qu’une classe implémente un grand nombre d’états peut
être considéré comme une violation de SRP.
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
419 / 466
public interface WriterState {
public void write(WriterContext context, char character);
}
Bertrand Estellon (DII – AMU)
State
Prog. et Conception Orientées Objet
Patrons de conception
State
17 novembre 2016
420 / 466
State
State
Nous pouvons maintenant définir les différents états :
public class WriterState0 implements WriterState {
public void write(WriterContext context, char c) {
if (c=='{') {context.changeState(new WriterState1());}
else System.out.print(c);
}
}
public class WriterState1 implements WriterState {
public void write(WriterContext context, char c) {
if (c=='}') {context.changeState(new WriterState0());}
else System.out.print(Character.toUpperCase(c));
}
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
421 / 466
Un exemple d’utilisation de la classe précédente :
WriterContext writer = new WriterContext();
String string = "abc{def}ghi";
for (int index = 0; index < string.length(); index++)
writer.write(string.charAt(index));
Le code précédent génère la sortie suivante :
abcDEFghi
Notez que l’ajout d’un nouvel état s’effectue en ajoutant une nouvelle
classe qui implémente l’interface WriterState.
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
422 / 466
Patrons de conception
State
Patrons de conception
State
Iterator
Iterator
Considérons l’interface suivante :
public interface List {
public void add(int value);
}
public void request1() {
state.handle1(this) ;
}
Context
- state : State
Considérons l’implémentation suivante de cette interface :
≪interface≫
State
public class ArrayList implements List {
private int[] list = new int[10];
private int size = 0;
+ request1()
+ request2()
+ changeState(state : State)
+ handle1(context : Context)
+ handle2(context : Context)
ConcreteState1
ConcreteState2
ConcreteState3
+ handle1(context : Context)
+ handle2(context : Context)
+ handle1(context : Context)
+ handle2(context : Context)
+ handle1(context : Context)
+ handle2(context : Context)
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
423 / 466
public void add(int value) {list[size] = value;size++;}
public int size() { return size; }
public int get(int index) { return list[index]; }
}
Bertrand Estellon (DII – AMU)
Iterator
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
424 / 466
Iterator
Iterator
Iterator
Supposons que nous avons également cette implémentation :
Nous voulons itérer de la même façon sur les deux implémentations :
List list = new ArrayList(); /* ou LinkedList() */
list.add(2); list.add(4);
Iterable iterable = list;
Iterator iterator iterator = iterable.iterator();
while (iterator.hasNext()) {
int value = iterator.next();
System.out.println(value);
}
public class Node {
private int value; private Node next;
public Node(int value, Node next) {
this.value = value; this.next = next;
}
public getValue() { return value; }
public getNext() { return next; }
}
public class LinkedList implements List {
private Node first = null;
public void add(int value) { first = new Node(value, first); }
public Node getFirst() { return first; }
}
Nous introduisons l’interface Iterable pour permettre que des objets
autres que des listes puissent être parcourus en utilisant un itérateur.
⇒ Nous voulons séparer la notion de liste et d’objet “itérable”.
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
425 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
426 / 466
Patrons de conception
Iterator
Patrons de conception
Iterator
Iterator
Iterator
L’implémentation de l’itérateur pour les ArrayList :
Nous en déduisons les deux interfaces suivantes :
public class ArrayListIterator implements Iterator {
private ArrayList list;
private int position;
public interface Iterator {
public boolean hasNext();
public int next();
}
public ArrayListIterator(ArrayList list) {
this.list = list; position = 0;
}
public interface Iterable {
public Iterator iterator();
}
public boolean hasNext() { return position < list.size(); }
public int next() {
int value = list.get(position);
position++;
return value;
}
De plus, l’interface List doit étendre l’interface Iterable :
public interface List { /* ... */ } extends Iterable;
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
427 / 466
Bertrand Estellon (DII – AMU)
Iterator
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
428 / 466
Iterator
Iterator
Iterator
L’implémentation de l’itérateur pour les LinkedList :
Nous devons également implémenter la méthode iterator dans les deux
classes ArrayList et LinkedList :
public class LinkedListIterator implements Iterator {
private Node next;
public class ArrayList implements List {
/* ... */
public Iterator iterator() {
return new ArrayListIterator(this);
}
}
public LinkedListIterator(LinkedList list) {
next = list.getFirst();
}
public boolean hasNext() { return next!=null; }
public class LinkedList implements List {
/* ... */
public Iterator iterator() {
return new LinkedListIterator(this);
}
}
public int next() {
int value = next.getValue();
next = next.getNext();
return value;
}
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
429 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
430 / 466
Patrons de conception
Iterator
Patrons de conception
Iterator
Visitor
Visitor
≪interface≫
Iterator
≪interface≫
Iterable
Client
+ next() : Object
+ hasNext() : boolean
+ iterator() : Iterator
Nous avons défini l’interface suivante :
public interface Shape {
void draw(GraphicsContext context);
double area();
}
ConcreteIterator
ConcreteIterable
- concreteIterable : ConcreteIterable
+ next() : Object
+ hasNext() : boolean
+ iterator() : Iterator
Quel principe SOLID est violé par cette interface ?
public Iterator iterator() {
return new ConcreteIterator(this) ;
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
431 / 466
Bertrand Estellon (DII – AMU)
Visitor
Visitor
17 novembre 2016
432 / 466
Visitor
Visitor
Implémentation d’un rectangle :
draw
area
...
public class Rectangle implements Shape {
public double x, y, w, h;
public Rectangle(double x, double y, double w, double h) {
this.x = x; this.y = y; this.w = w; this.h = h;
}
public double area() { return w*h; }
Circle
Polygon
...
...
...
...
...
...
...
▶
Une classe par colonne
▶
SRP violé car plusieurs responsabilités dans chaque classe
▶
OCP violé car le nombre de lignes peut augmenter
Solution :
}
Définir une classe par ligne en utilisant Visitor
Quel principe SOLID est violé par cette classe ?
Prog. et Conception Orientées Objet
Rectangle
Dans le code donné précédemment, on a :
public void draw(GraphicsContext context) {
context.strokeRect(x, y, h, w);
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
433 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
434 / 466
Patrons de conception
Visitor
Patrons de conception
Visitor
Visitor
Visitor
Simplification des classes et implémentation de la méthode accept :
public class Circle implements Shape {
public final double x, y;
public final double radius;
Création des deux interfaces qui permettent de coder Visitor :
public interface Shape {
<R> R accept(ShapeVisitor<R> visitor);
}
public Circle(double x, double y, double radius) {
this.x = x;
this.y = y;
this.radius = radius;
}
public interface ShapeVisitor<R> {
R visit(Rectangle rectangle);
R visit(Circle circle);
}
@Override
public <R> R accept(ShapeVisitor<R> visitor) {
return visitor.visit(this);
}
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
435 / 466
Bertrand Estellon (DII – AMU)
Visitor
Prog. et Conception Orientées Objet
Patrons de conception
Visitor
17 novembre 2016
436 / 466
Visitor
Visitor
Implémentation du visiteur DrawerVisitor :
Simplification des classes et implémentation de la méthode accept :
public class DrawerVisitor implements ShapeVisitor<Void> {
public class Rectangle implements Shape {
public double x, y, w, h;
private GraphicsContext context;
public Rectangle(double x, double y, double w, double h) {
this.x = x; this.y = y; this.w = w; this.h = h;
}
public DrawerVisitor(GraphicsContext context) {
this.context = context;
}
@Override
public <R> R accept(ShapeVisitor<R> visitor) {
return visitor.visit(this);
}
public void draw(List<Shape> shapes) {
for (Shape shape : shapes)
shape.accept(this);
}
}
/* Voir slide suivant pour la suite */
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
437 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
438 / 466
Patrons de conception
Visitor
Patrons de conception
Visitor
Visitor
Implémentation du visiteur AreaVisitor :
public class DrawerVisitor implements DrawableVisitor {
public class AreaVisitor implements ShapeVisitor<Double> {
@Override
public Void visit(Rectangle rectangle) {
context.strokeRect(rectangle.x, rectangle.y,
rectangle.h, rectangle.w);
return null;
}
public double sumOfArea(List<Shape> shapes) {
double sum = 0;
for (Shape shape : shapes)
sum += shape.accept(this);
return sum;
}
@Override
public Void visit(Circle circle) {
context.strokeOval(circle.x - circle.radius,
circle.y - circle.radius,
circle.radius*2, circle.radius*2);
return null;
}
public double area(Shape shape) {
return shape.accept(this);
}
/* Voir slide suivant pour la suite */
}
}
Bertrand Estellon (DII – AMU)
Visitor
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
439 / 466
Bertrand Estellon (DII – AMU)
Visitor
Prog. et Conception Orientées Objet
Patrons de conception
Visitor
17 novembre 2016
440 / 466
Visitor
Visitor
≪interface≫
Visitor
Client
public class DrawerVisitor implements DrawableVisitor {
+ visit(e : ConcreteElement1)
+ visit(e : ConcreteElement2)
@Override
public Double visit(Rectangle rectangle) {
return rectangle.w * rectangle.h;
}
@Override
public Double visit(Circle circle) {
return Math.pow(circle.radius,2) * Math.PI;
}
ConcreteVisitor1
ConcreteVisitor2
+ visit(e : ConcreteElement1)
+ visit(e : ConcreteElement2)
+ visit(e : ConcreteElement1)
+ visit(e : ConcreteElement2)
≪interface≫
Element
Container<Element>
+ accept(v : Visitor)
public void accept() {
v.visit(this) ;
}
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
441 / 466
ConcreteElement1
ConcreteElement2
+ accept(v : Visitor)
+ accept(v : Visitor)
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
442 / 466
Patrons de conception
Command
Patrons de conception
Command
Command
Command
Nous souhaitons pouvoir écrire le code suivant :
Nous avons implémenté la classe suivante :
public class Calculator {
private double value;
public
public
public
public
public
public
void setValue(double value) { this.value = value; }
double value() { return value; }
void add(double value) { this.value += value; }
void sub(double value) { this.value -= value; }
void divide(double value) { this.value /= value; }
void multiply(double value) { this.value *= value; }
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
443 / 466
Calculator calculator = new Calculator();
Invoker invoker = new Invoker(calculator);
invoker.add(new SetCommand(10));
invoker.add(new AddCommand(20));
invoker.add(new DivideCommand(3));
System.out.println(calculator.value()); //
invoker.undo();
System.out.println(calculator.value()); //
invoker.redo();
System.out.println(calculator.value()); //
invoker.undo();
invoker.add(new DivideCommand(2));
System.out.println(calculator.value()); //
Bertrand Estellon (DII – AMU)
Command
10.0
30.0
10.0
15.0
Prog. et Conception Orientées Objet
Patrons de conception
Command
Command
Exécution des commandes ajoutées :
Nous ajoutons la possibilité d’annuler une commande :
public class Invoker {
private List<Command> commands;
private Calculator calculator;
444 / 466
public class Invoker {
private List<Command> commands;
private Calculator calculator;
private int undoDepth;
public Invoker(Calculator calculator) {
this.calculator = calculator;
this.commands = new ArrayList<>();
}
public Invoker(Calculator calculator) {
/* ... */ this.undoDepth = 0;
}
public void add(Command command) {
command.execute(calculator);
commands.add(command);
}
public void undo() {
if (undoDepth >= commands.size()) return;
int positon = commands.size() - undoDepth - 1;
Command command = commands.get(positon);
command.unexecute(calculator);
undoDepth++;
}
/* ... */
}
Bertrand Estellon (DII – AMU)
17 novembre 2016
Command
}
Prog. et Conception Orientées Objet
17 novembre 2016
445 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
446 / 466
Patrons de conception
Command
Patrons de conception
Command
Command
Command
Nous supprimons l’historique si une commande est ajoutée :
Nous ajoutons la possibilité d’exécuter à nouveau une commande annulée :
public class Invoker {
private List<Command> commands;
private Calculator calculator;
private int undoDepth;
public class Invoker {
private List<Command> commands;
private Calculator calculator;
private int undoDepth;
public void add(Command command) {
clearHistory();
command.execute(calculator); commands.add(command);
}
public void redo() {
if (undoDepth == 0) return;
int positon = commands.size() - undoDepth;
Command command = commands.get(positon);
command.execute(calculator);
undoDepth--;
}
private void clearHistory() {
while (undoDepth > 0) {
commands.remove(commands.size()-1); undoDepth--;
}
}
}
}
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
447 / 466
Bertrand Estellon (DII – AMU)
Command
Prog. et Conception Orientées Objet
Patrons de conception
Command
17 novembre 2016
448 / 466
Command
Command
Implémentation de la commande DivideCommand :
Invoker
public class DivideCommand implements Command {
private double oldValue;
private final double value;
- receiver : Receiver
- commands : List<Command>
- undoDepth : int
+ add(c : Command)
+ undo()
+ redo()
public DivideCommand(double value) { this.value = value; }
public void execute(Calculator calculator) {
oldValue = calculator.value();
calculator.divide(value);
}
1
}
Prog. et Conception Orientées Objet
*
+ execute(receiver : Receiver)
+ unexecute(receiver : Receiver)
Client
public void unexecute(Calculator calculator) {
calculator.setValue(oldValue);
}
Bertrand Estellon (DII – AMU)
≪interface≫
Command
17 novembre 2016
449 / 466
≪instantiate≫
Receiver
ConcreteCommand
+ doAction1()
+ doAction2()
+ execute(receiver : Receiver)
+ unexecute(receiver : Receiver)
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
450 / 466
Patrons de conception
Résumé
Patrons de conception
Quelques patrons de conception
Factory
Comportement
Adapter
Proxy
Composite
Decorator
Facade
Flyweight
Bridge
...
Factory
Abstract Factory
Builder
Prototype
...
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
Client
Template Method
Strategy
Observer
Callback
Chain of Responsibility
State
Iterator
Visitor
Command
Mediator
Memento
Interpreter
...
Structure
Création
Résumé
17 novembre 2016
451 / 466
- productA : ProductA
- productB : ProductB
+ Client(factory : Factory)
Factory
+ createProductA() : ProductA
+ createProductB() : ProductB
≪interface≫
ProductB
≪interface≫
ProductA
ProductB2
Bertrand Estellon (DII – AMU)
Résumé
ProductB1
Prog. et Conception Orientées Objet
Patrons de conception
Abstract Factory
ProductA1
ProductA2
17 novembre 2016
452 / 466
Résumé
Builder
Client
- productA : ProductA
- productB : ProductB
+ Client(factory : AbstractFactory)
≪interface≫
AbstractFactory
Builder
+ createProductA() : ProductA
+ createProductB() : ProductB
Product
Client
Factory1
≪interface≫
ProductB
+ createProductA() : ProductA
+ createProductB() : ProductB
ProductB2
Bertrand Estellon (DII – AMU)
ProductB1
Factory2
≪interface≫
ProductA
+ createProductA() : ProductA
+ createProductB() : ProductB
ProductA1
Prog. et Conception Orientées Objet
+
+
+
+
Builder(value)
setValue1(value) : Builder
setValue2(value) : Builder
build() : Product
- Product(builder : Builder)
ProductA2
17 novembre 2016
453 / 466
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
454 / 466
Patrons de conception
Résumé
Patrons de conception
Adapter
Résumé
Proxy
≪interface≫
Target
Client
≪interface≫
Subject
Client
+ requiredMethod()
+ doAction()
Adapter
Adaptee
Proxy
+ otherMethod()
+ doAction()
RealSubject
- adaptee : Adaptee
+ requiredMethod()
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
455 / 466
Bertrand Estellon (DII – AMU)
Résumé
delegate
Prog. et Conception Orientées Objet
Patrons de conception
Composite
+ doAction()
17 novembre 2016
Decorator
≪interface≫
Component
≪interface≫
Component
0..*
child
+doAction()
void doAction() {
component.doAction() ;
doNewAction() ;
}
+ doAction()
Decorator
parent 1
ConcreteComponent
Composite
Leaf
+ doAction()
Bertrand Estellon (DII – AMU)
456 / 466
Résumé
- component : Component
+ doAction()
# doNewAction()
+ doAction()
- children : List<Component>
+
+
+
+
doAction()
addChild(component : Component)
removeChild(component : Component)
getChildren() : List<Component>
Prog. et Conception Orientées Objet
17 novembre 2016
457 / 466
Bertrand Estellon (DII – AMU)
ConcreteDecorator1
ConcreteDecorator2
# doNewAction()
# doNewAction()
Prog. et Conception Orientées Objet
17 novembre 2016
458 / 466
Patrons de conception
Résumé
Patrons de conception
Template Method
Résumé
Strategy
AbstractClass
final void doAction() {
doOperation1() ;
...
doOperation2() ;
}
+ doAction()
+ doOperation1()
+ doOperation2()
≪interface≫
Strategy
Context
- strategy : Strategy
+ setStrategy(strategy : Strategy)
+ doAction()
+ doStrategyAction1()
+ doStrategyAction2()
ConcreteStrategy1
ConcreteStrategy2
ConcreteStrategy3
+ doStrategyAction1()
+ doStrategyAction2()
+ doStrategyAction1()
+ doStrategyAction2()
+ doStrategyAction1()
+ doStrategyAction2()
ConcreteClass
+ doOperation1()
+ doOperation2()
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
459 / 466
Bertrand Estellon (DII – AMU)
Résumé
Prog. et Conception Orientées Objet
Patrons de conception
Observer
17 novembre 2016
460 / 466
Résumé
Chain of Responsibility
void notify(event : Event) {
for (Observer observer : observers)
observer.notify(event)
}
Handler
- successor : Handler
Client
+ setSuccessror(successor : Handler)
+ handle(request : Request)
+accept(request : Request)
Observable
≪interface≫
Observer
- observers : List<Observer>
+ addObserver(observer : Observer)
+ removeObserver(observer : Observer)
+ notify(event : Event)
ConcreteObservable
Bertrand Estellon (DII – AMU)
successor
0..*
+ handle(event : Event)
ConcreteObserver1
ConcreteObserver2
+ handle(event : Event)
+ handle(event : Event)
Prog. et Conception Orientées Objet
17 novembre 2016
461 / 466
ConcreteHandler1
ConcreteHandler2
+ accept(request : Request)
+ accept(request : Request)
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
462 / 466
Patrons de conception
Résumé
Patrons de conception
State
Résumé
Iterator
≪interface≫
Iterator
≪interface≫
Iterable
public void request1() {
state.handle1(this) ;
}
Client
+ next() : Object
+ hasNext() : boolean
+ iterator() : Iterator
Context
≪interface≫
State
- state : State
+ request1()
+ request2()
+ changeState(state : State)
ConcreteIterator
+ handle1(context : Context)
+ handle2(context : Context)
ConcreteIterable
- concreteIterable : ConcreteIterable
+ next() : Object
+ hasNext() : boolean
+ iterator() : Iterator
ConcreteState1
ConcreteState2
ConcreteState3
+ handle1(context : Context)
+ handle2(context : Context)
+ handle1(context : Context)
+ handle2(context : Context)
+ handle1(context : Context)
+ handle2(context : Context)
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
Patrons de conception
17 novembre 2016
463 / 466
public Iterator iterator() {
return new ConcreteIterator(this) ;
}
Bertrand Estellon (DII – AMU)
Résumé
Prog. et Conception Orientées Objet
Patrons de conception
Visitor
17 novembre 2016
464 / 466
Résumé
Command
≪interface≫
Visitor
Client
Invoker
+ visit(e : ConcreteElement1)
+ visit(e : ConcreteElement2)
- receiver : Receiver
- commands : List<Command>
- undoDepth : int
ConcreteVisitor1
ConcreteVisitor2
+ visit(e : ConcreteElement1)
+ visit(e : ConcreteElement2)
+ visit(e : ConcreteElement1)
+ visit(e : ConcreteElement2)
+ add(c : Command)
+ undo()
+ redo()
+ accept(v : Visitor)
public void accept() {
v.visit(this) ;
}
ConcreteElement1
ConcreteElement2
+ accept(v : Visitor)
+ accept(v : Visitor)
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
*
+ execute(receiver : Receiver)
+ unexecute(receiver : Receiver)
Client
≪interface≫
Element
Container<Element>
≪interface≫
Command
17 novembre 2016
1
465 / 466
≪instantiate≫
Receiver
ConcreteCommand
+ doAction1()
+ doAction2()
+ execute(receiver : Receiver)
+ unexecute(receiver : Receiver)
Bertrand Estellon (DII – AMU)
Prog. et Conception Orientées Objet
17 novembre 2016
466 / 466