SPRING SALE
/ Patrons de conception / Patrons de
création
Fabrique
Alias : Constructeur virtuel, Factory Method
Intention
Fabrique est un patron de conception de
création qui dé;nit une interface pour créer
des objets dans une classe mère, mais
délègue le choix des types d’objets à créer
aux sous-classes.
SPRING SALE
Problème
Imaginez que vous êtes en train de créer une
application de gestion logistique. La
première version de votre application ne
propose que le transport par camion, la
majeure partie de votre code est donc située
dans la classe Camion .
Au bout d’un certain temps, votre application
devient populaire et de nombreuses
entreprises de transport maritime vous
demandent tous les jours d’ajouter la gestion
de la logistique maritime dans l’application.
L’ajout d’une nouvelle classe au programme ne
s’avère pas si simple que cela si le reste du code est
déjà couplé aux classes existantes.
C’est super, n’est-ce pas ? Mais qu’en est-il du
code ? La majeure partie est actuellement
couplée à la classe Camion . Pour pouvoir
ajouter des Bateaux dans l’application, il
faudrait revoir la base du code. De plus, si
vous décidez plus tard d’ajouter un autre type
de transport dans l’application, il faudra
effectuer à nouveau ces changements.
Par conséquent, vous allez vous retrouver
avec du code pas très propre, rempli de
conditions qui modi;ent le comportement du
programme en fonction de la classe des
objets de transport.
Solution
Le patron de conception fabrique vous
propose de remplacer les appels directs au
constructeur de l’objet (à l’aide de l’opérateur
new ) en appelant une méthode fabrique
spéciale. Pas d’inquiétude, les objets sont
toujours créés avec l’opérateur new , mais
l’appel se fait à l’intérieur de la méthode
fabrique. Les objets qu’elle retourne sont
souvent appelés produits.
Les sous-classes peuvent modi;er les classes des
objets retournés par la méthode fabrique.
À première vue, cette modi;cation peut
sembler inutile : nous avons juste déplacé
l’appel du constructeur dans une autre partie
du programme. Mais maintenant, vous
pouvez redé;nir la méthode fabrique dans la
sous-classe et changer la classe des produits
créés par la méthode.
Il y a tout de même une petite limitation : les
sous-classes peuvent retourner des produits
différents seulement si les produits ont une
classe de base ou une interface commune. De
plus, cette interface doit être le type retourné
par la méthode fabrique de la classe de base.
Les produits doivent tous implémenter la
même interface.
Par exemple, les classes Camion et Bateau
doivent toutes les deux implémenter
l’interface Transport , qui déclare une
méthode livrer . Chaque classe implémente
cette méthode à sa façon : les camions
livrent par la route et les bateaux livrent par
la mer. La méthode fabrique de la classe
LogistiqueRoute retourne des camions, alors
que celle de la classe LogistiqueMer
retourne des bateaux.
Tant que les classes produit implémentent une
interface commune, vous pouvez passer leurs objets
au code client sans tout faire planter.
Le code qui appelle la méthode fabrique
(souvent appelé le code client) ne fait pas la
distinction entre les différents produits
concrets retournés par les sous-classes, il les
considère tous comme des Transports
abstraits. Le client sait que tous les objets
transportés sont censés avoir une méthode
livrer , mais son fonctionnement lui
importe peu.
Structure
1. L’interface est déclarée par le Produit et
est commune à tous les objets qui
peuvent être conçus par le créateur et ses
sous-classes.
2. Les Produits Concrets sont différentes
implémentations de l’interface produit.
3. La méthode fabrique est déclarée par la
classe Créateur et retourne les nouveaux
produits. Il est important que son type de
retour concorde avec l’interface produit.
Vous pouvez rendre la méthode fabrique
abstraite a;n d’obliger ses sous-classes à
implémenter leur propre version de la
méthode ou vous pouvez modi;er la
méthode fabrique de la classe de base
a;n qu’elle retourne un type de produit
par défaut.
Il faut bien comprendre que malgré son
nom, la création de produits n’est pas la
responsabilité principale du créateur. La
classe créateur a en général déjà un
fonctionnement propre lié à la nature de
ses produits. La fabrique aide à découpler
cette logique des produits concrets. C’est
un peu comme une grande entreprise de
développement de logiciels : elle peut
posséder un département spécialisé dans
la formation des développeurs, mais son
activité principale reste d’écrire du code,
pas de produire des développeurs.
4. Les Créateurs Concrets redé;nissent la
méthode fabrique de la classe de base
a;n de pouvoir retourner les différents
types de produits.
Notez toutefois que la méthode fabrique
n’est pas obligée de créer tout le temps de
nouvelles instances. Elle peut retourner
des objets depuis un cache, un réservoir
d’objets ou une autre source.
Pseudo-code
Cet exemple montre comment la fabrique
peut être utilisée pour créer des éléments
d’une UI (interface utilisateur)
multiplateforme sans coupler le code client
aux classes concrètes de l’UI.
Exemple multiplateforme pour une boîte
de dialogue.
La classe de base dialogue utilise différents
éléments d’UI pour le rendu de ses fenêtres.
Ces éléments peuvent un peu varier en
fonction du système d’exploitation, mais ils
doivent garder le même comportement. Un
bouton sous Windows est aussi un bouton
sous Linux.
Lorsque la fabrique entre dans l’équation, il
est inutile de réécrire la logique de la boîte
de dialogue pour chaque système
d’exploitation. Si nous déclarons une
méthode fabrique qui crée des boutons à
l’intérieur de la classe de base dialogue, nous
pourrons par la suite sous-classer cette
dernière a;n que la méthode fabrique
retourne des boutons dotés du style
Windows. La sous-classe hérite alors du code
de la classe de base dialogue, mais grâce à la
fabrique, elle est capable d’af;cher à l’écran
des boutons à l’allure Windows.
Pour le bon fonctionnement de ce patron, la
classe de base dialogue doit utiliser des
boutons abstraits représentés par une classe
de base ou une interface dont tous les
boutons concrets hériteront. Ainsi, quel que
soit le type de boutons, le code de la classe
dialogue reste fonctionnel.
Bien sûr, cette approche fonctionne
également avec les autres éléments de l’UI.
Cependant, chaque nouvelle méthode
fabrique ajoutée à Dialogue nous rapproche
du patron de conception Fabrique abstraite.
Pas de panique, nous aborderons ce patron
plus tard !
// La classe créateur déclare la méthode fabri
// renvoyer un objet de la classe produit. Les
// créateur fournissent en général une impléme
// méthode.
class Dialog is
// Le créateur peut également fournir des
// par défaut de la méthode fabrique.
abstract method createButton():Button
// Ne vous laissez pas berner par son nom,
// principale du créateur n’est pas de fab
// produits. Il héberge en général de la l
// concerne les produits retournés par la
// Les sous-classes peuvent modifier indir
// logique métier en redéfinissant la méth
// lui faisant retourner un type de produi
method render() is
// Appelle la méthode fabrique pour cr
// Produit.
Button okButton = createButton()
// Utilise le produit.
[Link](closeDialog)
[Link]()
// Les créateurs concrets redéfinissent la mét
// changer le type du produit qui en résulte.
class WindowsDialog extends Dialog is
method createButton():Button is
return new WindowsButton()
class WebDialog extends Dialog is
method createButton():Button is
return new HTMLButton()
// L’interface du produit déclare les traiteme
// produits concrets doivent implémenter.
interface Button is
method render()
method onClick(f)
// Les produits concrets fournissent diverses
// l’interface du produit.
class WindowsButton implements Button is
method render(a, b) is
// Affiche un bouton avec le style Win
method onClick(f) is
// Attribue un événement sur un clic n
// système d’exploitation.
class HTMLButton implements Button is
method render(a, b) is
// Retourne une représentation HTML d’
method onClick(f) is
// Attribue un événement sur un clic d
// Internet.
class Application is
field dialog: Dialog
// L’application choisit un type de créate
// la configuration actuelle ou des paramè
// d’environnement.
method initialize() is
config = readApplicationConfigFile()
if ([Link] == "Windows") then
dialog = new WindowsDialog()
else if ([Link] == "Web") then
dialog = new WebDialog()
else
throw new Exception("Error! Unknow
// Le code client manipule une instance d’
// concret, mais uniquement au moyen de so
// base. Tant que le client continue de pa
// interface pour manipuler le créateur, v
// n’importe quelle sous-classe du créateu
method main() is
[Link]()
[Link]()
Possibilités
d’application
Utilisez la fabrique si vous ne connaissez
pas à l’avance les types et dépendances
précis des objets que vous allez utiliser
dans votre code.
La fabrique effectue une séparation entre
le code du constructeur et le code qui
utilise réellement le produit. Le code du
constructeur devient ainsi plus évolutif
et indépendant du reste du code.
Par exemple, si vous voulez ajouter un
nouveau produit dans l’application, il
vous suf;t d’ajouter une sous-classe de
création et d’y redé;nir la méthode
fabrique.
Utilisez la fabrique si vous voulez mettre
à disposition une librairie ou un
framework pour vos utilisateurs avec un
moyen d’étendre ses composants
internes.
L’héritage est probablement le moyen le
plus simple pour étendre le
comportement par défaut d’une librairie
ou d’un framework. Mais comment le
framework peut-il savoir qu’il doit utiliser
votre sous-classe plutôt qu’un composant
standard ?
La solution est de réunir dans une seule
méthode fabrique le code qui construit
les composants dans le framework, et
non seulement d’étendre ceux-ci, mais de
laisser la possibilité de redé;nir la
méthode fabrique.
Voyons un exemple d’utilisation.
Imaginez la conception d’une application
qui utilise un framework d’UI open
source. Vous désirez utiliser des boutons
ronds, mais le framework ne fournit que
des boutons carrés. Vous étendez le
Bouton standard avec une magni;que
sous-classe BoutonRond . Mais vous devez
à présent expliquer à la classe principale
UIFramework qu’elle doit utiliser la sous-
classe du nouveau bouton plutôt que
celle par défaut.
Pour ce faire, vous créez une sous-classe
UIAvecBoutonsRonds depuis une classe
de base du framework et redé;nissez sa
méthode créerBouton . Même si la
méthode de la classe de base retourne
des Boutons , votre sous-classe renvoie
des BoutonsRonds . Vous pouvez
dorénavant utiliser UIAvecBoutonsRonds
à la place de UIFramework . Et c’est à peu
près tout !
Utilisez la fabrique lorsque vous voulez
économiser des ressources système en
réutilisant des objets au lieu d’en
construire de nouveaux.
Le besoin se présente souvent lorsque
l’on utilise des objets qui prennent
beaucoup de ressources tels que des
bases de données, des systèmes de
;chiers ou des ressources réseau.
Que faut-il pour réutiliser un objet
existant ?
1. Tout d’abord, vous devez créer un
moyen de stockage a;n de garder la
trace de tous les objets créés.
2. Lorsqu’un nouvel objet est demandé,
le programme doit chercher un objet
libre dans cette réserve.
3. … et le renvoyer au code client.
4. Si aucun objet n’est disponible, le
programme en crée un nouveau (et
l’ajoute à la réserve).
Cela représente un paquet de code ! De
plus, il faut tout mettre au même endroit
a;n de ne pas polluer le code avec des
doublons.
Il serait probablement plus pratique de
l’écrire dans le constructeur de la classe
de l’objet que l’on veut réutiliser, mais par
dé;nition, un constructeur doit toujours
renvoyer de nouveaux objets. Il ne peut
pas retourner des instances existantes.
C’est pourquoi vous devez disposer d’une
méthode non seulement capable de
créer de nouveaux objets, mais aussi de
réutiliser ceux qui existent déjà. Cela
ressemble énormément à un patron de
conception fabrique.
Mise en œuvre
1. Implémentez la même interface pour tous
les produits. Cette interface doit déclarer
des méthodes que tous les produits
peuvent avoir en commun.
2. Ajoutez une méthode fabrique vide à
l’intérieur de la classe créateur. Le type de
retour de la méthode doit correspondre à
l’interface commune des produits.
3. Localisez toutes les références aux
constructeurs des produits dans le code
du créateur. Remplacez-les une par une
par des appels à la méthode fabrique et
déplacez le code de la création de
produits dans la méthode fabrique.
Vous allez peut-être devoir ajouter un
paramètre temporaire à la méthode
fabrique pour véri;er le type du produit
retourné.
À ce stade, le code de la méthode fabrique
peut paraître désordonné. Il pourrait
même contenir un gros switch qui choisit
la classe à instancier. Ne vous inquiétez
pas, tout va bientôt rentrer dans l’ordre.
4. Pour chaque type de produit listé dans la
méthode fabrique, créez une sous-classe
de Créateur. Redé;nissez la méthode
fabrique dans les sous-classes et
récupérez les morceaux de code
appropriés de la méthode de base.
5. S’il y a trop de types de produits et peu
d’intérêt de créer des sous-classes pour
tous, vous pouvez réutiliser le paramètre
de contrôle de la classe de base dans les
sous-classes.
Imaginons la hiérarchie de classes
suivante : la classe de base Courrier avec
les sous-classes CourrierAérien et
CourrierTerrestre ; les classes de
Transport sont Avion , Camion et Train .
La classe CourrierAérien n’utilise que des
Avions et la classe CourrierTerrestre
peut utiliser à la fois des Camions et des
Trains . Vous pouvez créer une nouvelle
sous-classe ( CourrierFerroviaire par
exemple) pour gérer les deux cas, mais il y
a une autre possibilité. Le code client peut
passer un argument à la méthode
fabrique du CourrierTerrestre pour
désigner le type de produit qu’elle veut
recevoir.
6. Si après tous ces changements la
méthode fabrique de base est devenue
complètement vide, vous pouvez la rendre
abstraite. S’il reste encore quelques lignes,
vous pouvez y laisser un comportement
par défaut.
Avantages et
inconvénients
Vous désolidarisez le Créateur des
produits concrets.
Principe de responsabilité unique. Vous
pouvez déplacer tout le code de création
des produits au même endroit,
permettant ainsi une meilleure
maintenabilité.
Principe ouvert/fermé. Vous pouvez
ajouter de nouveaux types de produits
dans le programme sans endommager
l’existant.
Le code peut devenir plus complexe
puisque vous devez introduire de
nombreuses sous-classes pour la mise en
place du patron. La condition optimale
d’intégration du patron dans du code
existant se présente lorsque vous avez
déjà une hiérarchie existante de classes
de création.
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 mexible,
mais plus compliqué).
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.
Vous pouvez utiliser la Fabrique avec
l’Itérateur pour permettre aux sous-
classes des collections de renvoyer
différents types d’itérateurs compatibles
avec les collections.
Le Prototype n’est pas basé sur l’héritage,
il n’a donc pas ses désavantages. Mais le
prototype requiert une initialisation
compliquée pour l’objet cloné. La Fabrique
est basée sur l’héritage, mais n’a pas
besoin d’une étape d’initialisation.
La Fabrique est une spécialisation du
Patron de méthode. Une fabrique peut
aussi faire of;ce d’étape dans un grand
patron de méthode.
Exemples de code