Factory pattern : à la découverte du patron de conception fabrique
Plus de 500 000 exemplaires de « Design Patterns: Elements of Reusable Object-Oriented Software » ont été vendus depuis sa publication en 1994. Le livre, destiné aux développeurs de logiciels, décrit 23 patrons de conception différents, les Design Patterns. Ils sont également connus dans les cercles professionnels sous le nom de « Gang of Four » design patterns, un nom qui renvoie aux quatre auteurs : Erich Gamma, John Vlissides, Ralph Johnson et Richard Helm.
Parmi les nombreuses stratégies de conception enseignées dans cet ouvrage, on trouve le patron fabrique (en anglais, factory method) qui permet à une classe de déléguer la création d'objets à des sous-classes. Des informations concrètes sur l’utilisation de cette méthode pratique sont notamment fournies par le patron factory, qui est maintenant souvent simplement appelé le factory pattern.
Qu’est-ce que le factory pattern ?
Le factory pattern décrit une approche de programmation qui vous permet de créer des objets sans avoir à spécifier la classe exacte de ces objets. Cela permet d'échanger l’objet créé de manière souple et pratique. Pour la mise en œuvre, les développeurs ont recours au patron de conception fabrique, également appelé factory pattern, qui donne son nom au modèle. Cette méthode est soit spécifiée dans une interface et implémentée par une classe enfant, soit implémentée par une classe de base et éventuellement écrasée (par des classes dérivées). Le patron se substitue ainsi au constructeur de classe habituel pour détacher la création d’objets des objets eux-mêmes, permettant ainsi de suivre les principes dits SOLID.
Note
Les principes SOLID sont un sous-ensemble de principes de la programmation orientée objet (POO) qui visent à améliorer le processus de développement des logiciels orientés objet. L’acronyme "SOLID" désigne les cinq principes suivants :
- Principe de Single-Responsibility : chaque classe ne devrait avoir qu'une seule responsabilité.
- Principe de Open-Closed : les unités logicielles doivent pouvoir être étendues sans avoir à modifier leur comportement.
- Principe de Substitutions de Liskov : une classe dérivée doit toujours être utilisable à la place de sa classe de base.
- Principe de Interface-Segregation : les interfaces doivent être parfaitement adaptées aux besoins des clients qui y accèdent.
- Principe de Dependency-Inversion : les classes à un niveau d’abstraction supérieur ne devraient jamais dépendre de classes à un niveau d’abstraction inférieur.
Quel est l’objectif du factory pattern ?
Le factory pattern vise à résoudre un problème fondamental lors de l’instanciation, c’est-à-dire la création d’un objet concret d’une classe, dans la programmation orientée objet : créer un objet directement au sein de la classe, qui a besoin de cet objet ou devrait l’utiliser, est possible en principe, mais très rigide. Il lie la classe à cet objet particulier et rend impossible de modifier l'instanciation indépendamment de la classe. Le factory pattern évite un tel code en définissant d'abord une opération distincte pour la création de l'objet : la fabrique. Une fois appelée, elle génère l’objet, au lieu du constructeur de classe mentionné plus haut.
Factory pattern : diagramme UML du patron factory
Ainsi, dans les logiciels qui suivent le factory pattern, le code d’un objet à créer (également appelé « produit » dans ce contexte) est séparé dans une classe propre. Cette classe abstraite, également appelée « Créateur » ou, selon le modèle, « Fabrique » délègue l’instanciation de l’objet à une sous-classe (CréateurConcret), qui décide en dernier ressort du produit à créer. À cette fin, le CréateurConcret reprend la méthode createProduct() et renvoie ensuite à un ProduitConcret, qui peut éventuellement être complété par le Créateur avec un code de fabrication avant d'être transmis à l’interface en tant que produit fini.
Le processus du Factory Pattern est plus clair grâce au diagramme de classes UML qui résume graphiquement les relations et les processus décrits.
Les avantages et les inconvénients du factory pattern
Dans le factory pattern, l’appel d’une méthode de programme est complètement séparé de l’implémentation de nouvelles classes, ce qui présente certains avantages. Cela a notamment un effet sur l’extensibilité d’un logiciel : les instances du patron factory ont un degré élevé d’autonomie et permettent d’ajouter de nouvelles classes sans que l’application ait à changer de quelque façon que ce soit, parallèlement à l'exécution. Il suffit d’implémenter l'interface de l'usine et d'instancier le créateur en conséquence (via le CréateurConcret).
Un autre avantage est la bonne testabilité des composants de la fabrique. Si un Créateur met en place trois classes, par exemple, leur fonctionnalité peut être testée individuellement et indépendamment de la classe d’appel. Dans le cas de ce dernier, il suffit de s’assurer qu’il appelle correctement le Créateur, même si le logiciel est étendu à cette étape ultérieurement. La possibilité de donner un nom significatif au patron factory (contrairement à un constructeur de classe) est également bénéfique.
La grande faiblesse du factory pattern est le fait que son implémentation entraîne une forte augmentation des classes incluses, car chaque ProduitConcret nécessite toujours un CréateurConcret. Aussi rentable que soit en principe l’approche du factory pattern en ce qui concerne l’extension d'un logiciel, elle est également désavantageuse en ce qui concerne l’effort à fournir : si une famille de produits doit être étendue, non seulement l'interface, mais aussi toutes les classes subordonnées du CréateurConcret doivent être adaptées en conséquence. Une bonne planification préalable des types de produits requis est donc indispensable.
Avantages | Inconvénients |
---|---|
Extensibilité modulaire de l’application | Nombre élevé de classes requises |
Bonne testabilité | L'extension de l’application est très complexe |
Noms de méthodes significatifs |
Où utilise-t-on le factory pattern ?
Le factory pattern peut s'avérer précieux dans divers scénarios d'application. En particulier, les logiciels pour lesquels les produits concrets à créer ne sont pas connus ou pas définis à l'avance bénéficient de l'approche alternative de la gestion des sous-classes. Les cas d’utilisation typiques sont donc les frameworks ou les bibliothèques de classes, qui sont devenus pratiquement indispensables comme cadre de base pour le développement d’applications modernes.
Les systèmes d'authentification bénéficient également des avantages du patron de conception fabrique. Au lieu d’une classe centrale avec divers paramètres qui varient en fonction de l’autorisation de l’utilisateur, le processus d’authentification peut être délégué à des classes Fabrique qui prennent des décisions indépendantes concernant le traitement de l’utilisateur respectif.
En outre, la conception selon l’approche du factory pattern convient généralement à tous les logiciels dans lesquels de nouvelles classes sont ajoutées de manière programmée et régulière, surtout si ces classes doivent passer par le même processus de création.
Factory pattern : exemple (PHP)
Le patron Factory peut être utilisé dans des applications de divers langages de programmation. Les exemples les plus connus sont Java, JavaScript, C++, C#, Python et PHP. Ce dernier langage de script est également utilisé dans l'exemple pratique suivant, qui est basé sur un article du blog Phpmonkeys.
Dans ce cas, nous établissons un scénario avec la classe abstraite « Car » (Créateur) et la classe Fabrique « CarFactory » (CréateurConcret). Il est conçu aussi simplement que possible et ne contient qu'un code permettant de définir une couleur pour la voiture, couleur par défaut : blanc, et de la lire :
class Car {
private $color = null;
public function __construct() {
$this->color = "white";
}
public function setColor($color) {
$this->color = $color;
}
public function getColor() {
return $this->color;
}
}
La classe Fabrique introduit également des méthodes pour les voitures « rouges » (red) et « bleues » (blue), ainsi qu'une méthode privée pour la création de la classe proprement dite :
class CarFactory {
private function __construct() {
}
public static function getBlueCar() {
return self::getCar("blue");
}
public static function getRedCar() {
return self::getCar("red");
}
private static function getCar($color) {
$car = new Car();
$car->setColor($color);
return $car;
}
}
En option, cette classe Fabrique encore très claire peut maintenant, grâce au factory pattern, être étendue à diverses autres caractéristiques de la voiture, comme des couleurs supplémentaires, la marque de la voiture ou le prix.