Le modèle fabrique abstraite(Abstract Factory Pattern)

Le pattern abstract factory selon l'ouvrage "Design Patterns: Elements of Reusable Object-Oriented Software" du GOF.

Alias

  • Fr : modèle fafrique abstraite
  • Angl : abstract factory, kit

 

Intention de abstract factory

Le pattern abstract factory fournit une interface pour la création de familles d'objets apparentés ou indépendants sans devoir spécifier leurs classes concrètes.

 

Motivation de abstract factory

GOF exemple de pattern abstract factory

Considérons une boite à outils interface utilisateur qui supporte de multiples look and feels (apparences) standards tels que Motif et Presentation Manager. Les comportements spécifiques de certains éléments (widgets) de l'interface graphique (tels que les scrollbars - ascenceurs, ou ls boutons) peuvent varier en fonction du look and feel utilisé. pour qu'une application soit portable indépendament de l'apparence utilisée, ces widgets ne doivent pas être codés en dur pour une apparence donnée. Si nous instancions des classes de widgets spécifiques d'une apparence dans l'application, nous ne pourons difficilement changer l'apparence ultérieurement.

Nous pouvons résoudre ce problème en utilisant une classe abstraite WidgetsFactory qui définit une interface pour la création de toute variété de widgets. Nous devons de même définir une classe abstraite pour chaque variété de widget, dont les sous-classes concrètes implémenteront les versions de ces widgets conformément aux standards des apparences supportées. L'interface WidgetFactory possède une opération qui retourne un nouvel objet widget pour chaque classe abstraite de widgets. Les clients peuvent appeler ces opérations pour obtenir des instances de widgets sans savoir quelles classes concrètes ils utilisent. De cette manière, le client reste indépendant de l'apparence utilisée.

 

Utilisation de abstract factory

Le modèle fabrique abstraite peut nous aider dans les cas suivants :

  • Un système doit être indépendant de la manière dont ses produits ont été créés, combinés, et représentés.
  • Un système doit être constitué à partir d'une famille de produits, parmi plusieurs.
  • Nous souhaitons renforcer le caractère communauté d'une famille d'objets produits concus pour être utilisés ensemble.
  • Nous souhaitons fabriquer une bibliothèque de classes de produits, en n'en révélant que l'interface et non l'implémentation.

 

Structure de abstract factory

GOF schema pattern abstract factoryConcreteProductB1 implements AbstractProductB (example : WindowScrollBar)ConcreteProductB2 implements AbstractProductB (example : PMScrollBar)ConcreteProductA1 implements AbstractProductA (example : MotifWindow)AbstractProductB (example : ScrollBar)ConcreteFactory1 class implements AbstractFactory (example : MotifWidgetFactory)AbstractFactory class (example : WidgetFactory)ConcreteFactory2 class implements AbstractFactory (example : PMWidgetFactory)AbstractProductA (example : Window)ConcreteProductA2 implements AbstractProductA (example : PMWindow)Client

 

Constituants de abstract factory

  • AbstractFactory (fabrique abstraite)
    Déclare une interface contenant les opérations de création d'objets produits abstraits. Dans notre exemple, la fabrique abstraite est WidgetFactory.
  • ConcreteFactory (fabrique concrète)
    Implémente les opérations de création d'objets produits concrets. Dans notre exemple, les fabriques concrètes sont MotifWidgetFactory et PMWidgetFactory.
  • AbstractProduct (produit abstrait)
    Déclare une interface pour un objet de type produit. Dans notre exemple, les produits abstraits sont Window et ScrollBar.
  • ConcreteProduct (produit concret)
    Définit un objet "produit" qui doit être créé par la fabrique concrète correspondante. Dans notre exemple, les produits concrets créés par MotifWidgetFactory sont MotifWindow et MotifScrollBar. Les produits concrets créés par PMWidgetFactory sont PMWindow et PMScrollBar.
    Remarque : la demande de construction ne sera pas adressée à MotifWidgetFactory ou à PMWidgetFactory, mais bien à WidgetFactory.
  • Client
    Le client n'utilisera que les interfaces AbstractFactory et AbstractProduct (WidgetFactory et Window ou ScrollBar dans notre exemple).

 

Collaborations de abstract factory

Normalement, une instance unique de la classe ConcreteFactory est créée à l'exécution. Cette fabrique concrète crée des produits dotés d'une implémentation spécifique. Pour créer des objets produits différents, les clients doivent utiliser une ConcreteFactory différente.

AbstractFactory délègue la création des objets produits à sa sous-classe ConcreteFactory.

 

Conséquences de abstract factory

Avantages du pattern abstract factory :

Isolation des classes concrètes. Le modèle fabrique abstraite facilite le contrôle des classes d'objets créés par une application. Une fabrique encapsule les actions de prise en charge et de création des objets produits, donc elle isole les clients des classes d'implémentation. Les clients manipulent les instances à travers leurs interfaces abstraites. Les noms des classes produits sont isolés dans l'implémentation de la fabrique concrète; ils n'apparaissent pas dans le code client.

Substitution de familles de produits facilitée. Une classe fabrique concrète n'apparait qu'une seule fois dans une application - précisément, là où elle est instanciée. Il est alors aisé de modifier la fabrique concrète utilisée par cette application. Comme une fabrique abstraite engendre une famille complète de produits, c'est la totalité de la famille de produits qui est remplacée si nous modifions la fabrique abstraite.

Favorise la consistance (maintien de la cohérence) entre les produits. Quand les objets produits d'une famille sont destinés à travailler ensemble, il ets important qu'une application utilise les ojets d'une seule famille à la fois. La classe AbstractFactory facilite le renforcement de cette condition.

 

Contraintes du pattern abstract factory :

Supporter de nouveaux types de produits est difficile. Etendre la production des fabriques abstraites à de nouveaux produits n'est pas aisé. La cause de cette difficulté est que l'interface AbstractFactory détermine l'ensemble des produits qui peuvent être créés. Supporter de nouveaux types de produits requiert l'extension de l'interface de la fabrique, ce qui implique la modification de la classe AbstractFactory et de toutes ses sous-classes.

 

Implémentation de abstract factory

Quelques conseils :

Utiliser les fabriques en tant que singletons. Une application ne nécessite qu'une seule instance d'une ConcreteFactory par famille de produits. Il est donc souvent préférable de l'implémenter en Singleton.

Création des produits. AbstractFactory ne fait que déclarer une interface pour la création des produits. C'set aux sous-classes ConcreteProduct de les créer. La façon la plus courante est de définir pour chaque produit une méthode de fabrication.  Une ConcreteFactory spécifiera ses produits par un override (surcharge), pour chacun d'eux, de la méthode de fabrication.

 

Exemples de codes de abstract factory

  1. using System;
  2. {
  3. // MainApp test application
  4. class MainApp{
  5. public static void Main(){
  6. // Abstract factory #1
  7. AbstractFactory factory1 = new ConcreteFactory1();
  8. Client c1 = new Client(factory1);
  9. c1.Run();
  10. // Abstract factory #2
  11. AbstractFactory factory2 = new ConcreteFactory2();
  12. Client c2 = new Client(factory2);
  13. c2.Run();
  14. // Wait for user input
  15. Console.Read();
  16. }
  17. }
  18. // AbstractFactory
  19. abstract class AbstractFactory{
  20. public abstract AbstractProductA CreateProductA();
  21. public abstract AbstractProductB CreateProductB();
  22. }
  23. // ConcreteFactory1
  24. class ConcreteFactory1: AbstractFactory{
  25. public override AbstractProductA CreateProductA(){
  26. return new ProductA1();
  27. }
  28. public override AbstractProductB CreateProductB(){
  29. return new ProductB1();
  30. }
  31. }
  32. // ConcreteFactory2
  33. class ConcreteFactory2: AbstractFactory{
  34. public override AbstractProductA CreateProductA(){
  35. return new ProductA2();
  36. }
  37. public override AbstractProductB CreateProductB(){
  38. return new ProductB2();
  39. }
  40. }
  41. // AbstractProductA
  42. abstract class AbstractProductA{
  43. }
  44. // AbstractProductB
  45. abstract class AbstractProductB{
  46. public abstract void Interact(AbstractProductA a);
  47. }
  48. // ProductA1
  49. class ProductA1: AbstractProductA{
  50. }
  51. // ProductB1
  52. class ProductB1: AbstractProductB{
  53. public override void Interact(AbstractProductA a){
  54. Console.WriteLine(this.GetType().Name +
  55. " interacts with " + a.GetType().Name);
  56. }
  57. }
  58. // ProductA2
  59. class ProductA2: AbstractProductA{
  60. }
  61. // ProductB2
  62. class ProductB2: AbstractProductB{
  63. public override void Interact(AbstractProductA a){
  64. Console.WriteLine(this.GetType().Name +
  65. " interacts with " + a.GetType().Name);
  66. }
  67. }
  68. // Client
  69. class Client{
  70. private AbstractProductA AbstractProductA;
  71. private AbstractProductB AbstractProductB;
  72. // Constructor
  73. public Client(AbstractFactory factory){
  74. AbstractProductB = factory.CreateProductB();
  75. AbstractProductA = factory.CreateProductA();
  76. }
  77. public void Run(){
  78. AbstractProductB.Interact(AbstractProductA);
  79. }
  80. }
  81. }

Ce qui produit en sortie :

ProductB1 interacts with ProductA1
ProductB2 interacts with ProductA2

  1. /**
  2. * DemoLauncher class for the abstract factory pattern.
  3. */
  4. public class DemoLauncher{
  5. public static void main( String arg[] ){
  6. AbstractFactory factory1 = new ConcreteFactory1();
  7. Client c1 = new Client(factory1);
  8. c1.run();
  9. AbstractFactory factory2 = new ConcreteFactory2();
  10. Client c2 = new Client(factory2);
  11. c2.run();
  12. }
  13. }
  14. /**
  15. * Declares an interface for operations that create
  16. * abstract product objects.
  17. */
  18. public interface AbstractFactory{
  19. AbstractProductA createProductA();
  20. AbstractProductB createProductB();
  21. }
  22. /**
  23. * Implements the operations to create concrete product objects.
  24. */
  25. public class ConcreteFactory1 implements AbstractFactory{
  26. public AbstractProductA createProductA() { return new ProductA1(); };
  27. public AbstractProductB createProductB() { return new ProductB1(); };
  28. }
  29. public class ConcreteFactory2 implements AbstractFactory{
  30. public AbstractProductA createProductA() { return new ProductA2(); };
  31. public AbstractProductB createProductB() { return new ProductB2(); };
  32. }
  33. /**
  34. * Declares an interface for a type of product object.
  35. */
  36. public interface AbstractProductA{
  37. }
  38. public interface AbstractProductB{
  39. public void interact(AbstractProductA a);
  40. }
  41. /**
  42. * Defines a product object to be created by the corresponsing
  43. * concrete factory. Implements the AbstractProduct interface.
  44. */
  45. public class ProductA1 implements AbstractProductA{
  46. }
  47. public class ProductB1 implements AbstractProductB{
  48. public void interact(AbstractProductA a) {
  49. System.out.println(this.getClass().getName()+
  50. " interacts with "+ a.getClass().getName());
  51. }
  52. }
  53. public class ProductA2 implements AbstractProductA{
  54. }
  55. public class ProductB2 implements AbstractProductB{
  56. public void interact(AbstractProductA a) {
  57. System.out.println(this.getClass().getName()+
  58. " interacts with "+ a.getClass().getName());
  59. }
  60. }
  61. /**
  62. * Client class for the abstract factory pattern.
  63. */
  64. public class Client{
  65. private AbstractProductA productA;
  66. private AbstractProductB productB;
  67. public Client(AbstractFactory factory){
  68. productA = factory.createProductA();
  69. productB = factory.createProductB();
  70. }
  71. public void run(){
  72. productB.interact(productA);
  73. }
  74. }

 

La classe AbstractFactory est souvent implémentée selon le pattern FactoryMethod, mais elle peut aussi être implémentée en utilisant le pattern Prototype.
Les classes ConcreteFactory sont souvent des Singleton.

 

Document créé le 09/10/2005, dernière modification le 26/10/2018
Source du document imprimé : https://www.gaudry.be/pattern-abstract-factory.html

L'infobrol est un site personnel dont le contenu n'engage que moi. Le texte est mis à disposition sous licence CreativeCommons(BY-NC-SA). Plus d'info sur les conditions d'utilisation et sur l'auteur.