Fabrique abstraite
Intention
Fabrique abstraite est un patron de conception qui permet de créer des familles d’objets apparentés sans préciser leur classe concrète.
Problème
Imaginons la création d’un simulateur pour un magasin de meubles. Votre code contient les classes suivantes :
-
Une famille de produits appartenant à un même thème :
Chaise
+Sofa
+TableBasse
. -
Plusieurs variantes de cette famille. Par exemple, les produits
Chaise
+Sofa
+TableBasse
sont disponibles dans les variantes suivantes :Moderne
,Victorien
,ArtDéco
.
Vous devez trouver une solution pour créer des objets individuels (des meubles) et les faire correspondre à d’autres objets de la même famille. Les clients sont agacés lorsqu’ils reçoivent des meubles qui ne se marient pas.
De plus, vous n’avez pas envie de réécrire votre code chaque fois que vous ajoutez de nouvelles familles de produits à votre programme. Les vendeurs de meubles alimentent régulièrement leurs catalogues et il n’est pas envisageable de restructurer le code à chaque mise à jour.
Solution
La première chose que propose la fabrique abstraite est de déclarer explicitement des interfaces pour chaque produit de la famille de produits (dans notre cas : chaise, sofa, table basse). Toutes les autres variantes de produits peuvent ensuite se servir de ces interfaces. Par exemple, toutes les variantes de chaises peuvent implémenter l’interface Chaise
; toutes les variantes de tables basses peuvent implémenter TableBasse
, etc.
La prochaine étape est de déclarer la fabrique abstraite — une interface armée d’une liste de méthodes de création pour toutes les familles de produits (par exemple : créerChaise
, créerSofa
et créerTableBasse
). Ces méthodes doivent renvoyer tous les types de produits abstraits des interfaces que nous avons créées précédemment : Chaise
, Sofa
, TableBasse
, etc.
Mais que deviennent les variantes des produits ? Pour chaque variante d’une famille de produits, nous créons une classe fabrique qui implémente l’interface FabriqueAbstraite
. Une fabrique est une classe qui retourne un certain type de produits. Par exemple, la FabriqueMeubleModerne
peut uniquement créer des ChaiseModerne
, des SofaModerne
et des TableBasseModerne
.
Le code client travaille simultanément avec les interfaces abstraites respectives des fabriques et des produits. Nous pouvons ainsi changer le type de fabrique passé au code client et la variante de produit qu’il reçoit, sans avoir à le modifier.
Imaginons un cas où le client désire une fabrique qui peut produire une chaise. Le client n’a pas à se préoccuper de la classe de la fabrique ni du type de chaise qu’il va obtenir. Il doit traiter les chaises de la même manière, que ce soit un modèle de style moderne ou victorien, en utilisant l’interface abstraite Chaise
. Grâce à cette approche, le client ne sait qu’une seule chose à propos de la chaise, c’est qu’elle implémente la méthode sasseoir
. De plus, quelle que soit la variante de la chaise renvoyée, elle correspondra systématiquement au type de sofa ou de table basse produit par le même objet fabrique.
Il ne reste plus qu’un point à éclaircir : si le client n’est lié qu’aux interfaces abstraites, comment les objets de la fabrique sont-ils créés ? En général, l’application crée un objet fabrique concret lors de l’initialisation. Mais avant cela, l’application doit choisir le type de la fabrique en fonction de la configuration ou des paramètres d’environnement.
Structure
-
Les Produits Abstraits déclarent une interface pour un ensemble d’objets distincts mais apparentés, qui forment une famille de produits.
-
Les Produits Concrets sont des implémentations des produits abstraits groupés par variantes. Chaque produit abstrait (chaise/sofa) doit être implémenté dans toutes les variantes (victorien/moderne).
-
L’interface Fabrique Abstraite déclare un ensemble de méthodes pour créer chacun des produits abstraits.
-
Les Fabriques Concrètes implémentent les opérations de création d’objets de la fabrique abstraite. Chaque fabrique concrète correspond à une variante spécifique de produits et ne crée que ces variantes.
-
Bien que les fabriques concrètes instancient des produits concrets, leurs méthodes de création ont une valeur de retour correspondant aux produits abstraits. Le code client qui sollicite une fabrique est ainsi isolé de la variante du produit obtenu. Le client peut travailler avec n’importe quelle variante de fabrique ou produit, tant qu’il interagit avec les interfaces abstraites.
Pseudo-code
Cet exemple montre comment la Fabrique abstraite peut être utilisée pour créer des éléments d’une UI multiplateforme sans coupler le code client avec les classes concrètes de l’UI, tout en gardant une cohérence entre les éléments créés et le système d’exploitation sélectionné.
Tous les éléments de l’UI d’une application multiplateforme sont censés avoir un comportement identique quel que soit le système d’exploitation, mais leur apparence peut légèrement varier. Vous devez faire en sorte que les éléments de l’interface correspondent bien au style du système d’exploitation. Vous ne voudriez pas vous retrouver avec des boutons macOS dans Windows.
L’interface de la fabrique abstraite déclare un ensemble de méthodes de création que le code client peut utiliser pour produire différents types d’éléments de l’UI. Chaque fabrique concrète correspond à un système d’exploitation particulier et crée ses propres éléments de l’UI en fonction de ce système d’exploitation.
Le fonctionnement est le suivant : lorsqu’une application est exécutée, elle vérifie le système d’exploitation utilisé et exploite cette information pour créer un objet de la fabrique qui y correspond. Cette fabrique est ensuite utilisée pour générer tout le reste des éléments de l’UI, ce qui évite les erreurs.
Grâce à cette approche, tant qu’il utilise leurs interfaces abstraites, le code client ne dépend plus des classes concrètes de la fabrique et des éléments de l’UI. Cela lui permet également d’exploiter les nouveaux éléments de l’UI ou les fabriques que vous pourriez ajouter dans le futur.
Nous n’avons ainsi plus besoin de modifier le code client à chaque nouvel élément de l’interface utilisateur que vous voulez ajouter dans votre application. Vous devrez simplement créer une nouvelle classe fabrique produisant ces éléments et apporter quelques modifications au code d’initialisation afin qu’il choisisse la classe appropriée.
Possibilités d’application
Utilisez la fabrique abstraite si votre code a besoin de manipuler des produits d’un même thème, mais que vous ne voulez pas qu’il dépende des classes concrètes de ces produits — soit vous ne les connaissez pas encore, soit vous voulez juste rendre votre code évolutif.
La fabrique abstraite fournit une interface qui permet de créer des objets pour chaque classe de la famille de produits. Tant que votre code utilise cette interface pour créer ses objets, il prendra systématiquement les bonnes variantes des produits disponibles dans votre application.
Étudiez la possibilité d’utiliser la fabrique abstraite lorsqu’une classe se retrouve avec un ensemble de patrons Fabrique qui occultent sa fonction principale.
Dans un programme bien conçu chaque classe n’a qu’une seule responsabilité. Lorsqu’une classe gère plusieurs types de produits, déplacer les méthodes fabrique dans des fabriques individuelles ou dans une implémentation à part entière d’une fabrique abstraite est souvent plus pratique.
Mise en œuvre
-
Établissez une matrice de vos différents types de produits et leurs variantes.
-
Déclarez des interfaces abstraites pour tous vos types de produits. Mettez en place vos produits concrets dans des classes qui implémentent ces interfaces.
-
Déclarez une interface abstraite de la fabrique avec un ensemble de méthodes de création pour tous les produits abstraits.
-
Implémentez une classe fabrique concrète pour chaque variante des produits.
-
Insérez le code de l’initialisation de la fabrique quelque part dans votre application. Il doit instancier une des fabriques concrètes en fonction de la configuration de l’application ou de l’environnement d’exécution. Passez cette fabrique à toutes les classes qui construisent des produits.
-
Parcourez votre code et repérez tous les appels aux constructeurs des produits. Remplacez-les par des appels à la méthode de création appropriée de la fabrique.
Avantages et inconvénients
- Vous êtes assurés que les produits d’une fabrique sont compatibles entre eux.
- Vous découplez le code client des produits concrets.
- Principe de responsabilité unique. Vous pouvez déplacer tout le code de création des produits au même endroit, pour une meilleure maintenabilité.
- Principe ouvert/fermé. Vous pouvez ajouter de nouvelles variantes de produits sans endommager l’existant.
- Le code peut devenir plus complexe que nécessaire, car ce patron de conception impose l’ajout de nouvelles classes et interfaces.
Liens avec les autres patrons
-
La Fabrique est souvent utilisée dès le début de la conception (moins compliquée et plus personnalisée grâce aux sous-classes) et évolue vers la Fabrique abstraite, le Prototype, ou le Monteur (ce dernier étant plus flexible, mais plus compliqué).
-
Le Monteur se concentre sur la construction d’objets complexes étape par étape. La Fabrique abstraite se spécialise dans la création de familles d’objets associés. La fabrique abstraite retourne le produit immédiatement, alors que le monteur vous permet de lancer des étapes supplémentaires avant de récupérer le produit.
-
Les classes Fabrique abstraite sont souvent basées sur un ensemble de Fabriques, mais vous pouvez également utiliser le Prototype pour écrire leurs méthodes.
-
La Fabrique abstraite peut remplacer la Façade si vous voulez simplement cacher au code client la création des objets du sous-système.
-
Vous pouvez utiliser la Fabrique abstraite avec le Pont. Ce couple est très utile quand les abstractions définies par le pont ne fonctionnent qu’avec certaines implémentations spécifiques. Dans ce cas, la fabrique abstraite peut encapsuler ces relations et cacher la complexité au code client.
-
Les Fabriques abstraites, Monteurs et Prototypes peuvent tous être implémentés comme des Singletons.
Informations supplémentaires
- Lisez notre Comparaison des fabriques pour en savoir plus sur la différence entre les divers patrons et concepts.