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é?
design
design-patterns
factory-method
factory
Armon Safai
la source
la source
Réponses:
La sagesse conventionnelle orientée objet consiste à éviter les
if
déclarations et à les remplacer par une répartition dynamique des méthodes remplacées dans les sous-classes d'une classe abstraite. Jusqu'ici tout va bien.Mais le but du modèle d'usine est de vous éviter d'avoir à connaître les sous-classes individuelles et de travailler uniquement avec la superclasse 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 d'usine ne peut en aucun cas renoncer aux
if
instructions. 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, aucun principe n'est absolu), et si vous utilisez ce modèle, vous supposeriez que l'avantage en est supérieur à l'avantage de ne pas utiliser unif
.la source
if
s. Voir la réponse de @ BЈовић pour un exemple simple de comment y parvenir. Voté.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 comme 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 l'exemple le fait, 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.
la source
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:
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 l'OCP, vous devez créer une usine supplémentaire pour prendre en charge vos nouvelles formes (appelons-le
AdvancedShapeFactory
), puis utiliser AbstractFactory pour décider quelle usine vous devez créer afin de créer les formes dont vous avez besoin.la source
Si vous parlez du modèle de l'usine abstraite, 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 ici: https://en.wikipedia.org/wiki/Abstract_factory_pattern
La prise de décision n'implique pas nécessairement des
if
déclarations. Il pourrait lire le type concret Factory à partir d'un fichier de configuration, le dériver d'une structure de carte, etc.la source
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é.
la source
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.
la source
Tout dépend de la façon dont vous l'implémentez. Vous pouvez utiliser
std::map
pour maintenir des pointeurs de fonction vers des fonctions créant des objets. Ensuite, le principe d'ouverture / fermeture n'est pas violé. Ou interrupteur / boîtier.Quoi qu'il en soit, si vous n'aimez pas le modèle d'usine, vous pouvez toujours utiliser l'injection de dépendance.
la source