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