web-dev-qa-db-fra.com

Le modèle d'usine viole-t-il le principe ouvert / fermé?

Pourquoi cette ShapeFactory utilise-t-elle des instructions conditionnelles pour déterminer quel objet instancier. Ne devons-nous pas modifier ShapeFactory si nous voulons ajouter d'autres classes à l'avenir? Pourquoi cela ne viole-t-il pas le principe ouvert et fermé?

Factory Pattern Design

ShapeFactory Design

14
Armon Safai

La sagesse conventionnelle orientée objet consiste à éviter les instructions if et à les remplacer par une répartition dynamique des méthodes redéfinies dans les sous-classes d'une classe abstraite. Jusqu'ici tout va bien.

Mais le point du modèle d'usine est pour vous éviter d'avoir à connaître les sous-classes individuelles et à travailler uniquement avec la super-classe abstraite. L'idée est que l'usine sait mieux que vous quelle classe spécifique à instancier, et vous feriez mieux de travailler uniquement avec les méthodes publiées par la super classe. C'est souvent vrai et c'est un modèle valable.

Par conséquent, l'écriture d'une classe de fabrique ne peut en aucun cas se passer des instructions if. Cela déplacerait le fardeau du choix d'une classe spécifique vers l'appelant, ce qui est exactement ce que le modèle est censé éviter. Tous les principes ne sont pas absolus (en fait, le principe no est absolu), et si vous utilisez ce modèle, vous supposeriez que l'avantage en est supérieur à l'avantage de ne pas utiliser un if.

20
Kilian Foth

L'exemple utilise probablement une instruction conditionnelle car c'est la plus simple. Une implémentation plus complexe pourrait utiliser une carte ou une configuration ou (si vous voulez vraiment être fantaisiste) une sorte de registre où les classes peuvent s'enregistrer. Cependant, il n'y a rien de mal à utiliser un conditionnel si le nombre de classes est petit et change rarement.

Prolonger le conditionnel pour ajouter le soutien à une nouvelle sous-classe à l'avenir serait en effet à proprement parler une violation du principe ouvert/fermé. La solution "correcte" serait de créer une nouvelle usine avec la même interface. Cela dit, l'adhésion au principe O/C doit toujours être mise en balance avec d'autres principes de conception tels que KISS et YAGNI.

Cela dit, le code affiché est clairement un exemple de code conçu pour montrer le concept d'une usine et rien d'autre. Par exemple. c'est vraiment un mauvais style de retourner null comme le fait l'exemple, mais une gestion d'erreur plus élaborée ne ferait qu'obscurcir le point. Un exemple de code n'est pas un code de qualité de production, vous ne devriez pas vous y attendre.

5
JacquesB

Le motif lui-même ne viole pas le principe ouvert/fermé (OCP). Cependant, nous violons l'OCP lorsque nous utilisons le modèle de manière incorrecte.

La réponse simple à cette question est la suivante:

  1. Créez votre fonctionnalité de base à l'aide de Méthode d'usine Pattern.
  2. ÉTENDEZ vos fonctionnalités en utilisant Abstract Factory Modèle

Dans l'exemple fourni, votre fonctionnalité de base prend en charge trois formes: cercle, rectangle et carré. Supposons que vous deviez prendre en charge Triangle, Pentagone et Hexagone à l'avenir. Pour ce faire SANS violer OCP, vous devez créer une fabrique supplémentaire pour prendre en charge vos nouvelles formes (appelons AdvancedShapeFactory) puis utiliser AbstractFactory pour décider quelle usine vous devez créer afin de créer toutes les formes dont vous avez besoin.

2
hfontanez

Si vous parlez du modèle Abstract Factory, la prise de décision n'est souvent pas dans l'usine elle-même mais dans le code d'application. C'est ce code qui choisit quelle usine concrète instancier et transmettre au code client qui utilisera les objets produits par l'usine. Voir la fin de l'exemple Java Java ici: https://en.wikipedia.org/wiki/Abstract_factory_pattern

La prise de décision n'implique pas nécessairement des instructions if. Il pourrait lire le type concret Factory à partir d'un fichier de configuration, le dériver d'une structure de carte, etc.

1
guillaume31

Si vous pensez à l'Open-Close au niveau de la classe avec cette usine, vous créez une autre classe dans votre système Open-Close, par exemple si vous avez une autre classe qui prend une forme et calcule la zone (exemple typique), cette classe est OpenClose car il peut calculer l'aire de nouveaux types de formes sans modification. Ensuite, vous avez une autre classe qui dessine la forme, une autre classe qui prend N formes et renvoie la plus grande et vous pouvez penser en général que les autres classes de votre système qui traitent les formes sont Open-Close (au moins sur les formes). En regardant la conception, l'usine permet au reste du système d'être ouvert-fermé et bien sûr l'usine elle-même n'est PAS ouverte-fermée.

Bien sûr, vous pouvez également ouvrir/fermer cette usine, via une sorte de chargement dynamique et votre système entier peut être ouvert-fermé (vous pouvez ajouter de nouvelles formes en déposant un pot dans le chemin de classe par exemple). Vous devez évaluer si cette complexité supplémentaire vaut selon le système que vous construisez, tous les systèmes n'ont pas besoin de fonctionnalités enfichables et tout le système n'a pas besoin d'être complètement ouvert-fermé.

0
AlfredoCasado

Le principe ouvert-fermé, comme le principe de substitution de Liskov, s'applique aux arbres de classes, aux hiérarchies d'héritage. Dans votre exemple, la classe d'usine ne se trouve pas dans l'arbre généalogique des classes qu'elle instancie donc elle ne peut pas violer ces règles. Il y aurait une violation si votre GetShape (ou plus convenablement nommé, CreateShape) était implémenté dans la classe de base Shape.

0
Martin Maat