Clarifier le principe ouvert / fermé

25

Comme je l'ai expliqué, le principe ouvert / fermé stipule qu'une fois le code écrit ne doit pas être modifié (à part les corrections de bugs). Mais si mes règles métier changent, ne devrais-je pas modifier le code implémentant ces changements? Je soupçonne que je ne comprends pas quelque chose sur la façon dont le principe parce qu'il n'a pas de sens pour moi.

Winston Ewert
la source

Réponses:

22

C'est probablement le plus solide des principes solides à expliquer. Laisse-moi essayer. Imaginez que vous avez écrit une classe Invoice qui fonctionne parfaitement et sans bugs. Il fait un PDF d'une facture.

Ensuite, quelqu'un dit qu'il veut une facture HTML avec des liens. Vous ne modifiez aucun code dans la facture pour satisfaire cette demande. Au lieu de cela, vous créez une autre classe, HTMLInvoice, qui fait ce qu'elle veut maintenant. Vous tirez parti de l'héritage pour ne pas avoir à écrire beaucoup de code en double dans HTMLInvoice.

L'ancien code qui utilisait l'ancienne facture n'est pas cassé ou vraiment affecté de quelque façon que ce soit. Le nouveau code peut utiliser HTMLInvoice. (Si vous utilisez également Liskov Substitutability , le L de solid, vous pouvez donner des instances HTMLInvoice au code existant qui attend des instances Invoice.) Tout le monde vit heureux pour toujours.

La facture est fermée à modification, ouverte à extension. Et vous devez écrire la facture correctement à l'avance pour que cela fonctionne, btw.

Kate Gregory
la source
1
Si les règles métier changent, il n'y a aucune hypothèse de fonctionnement parfait sans bogues, donc le principe d'ouverture / fermeture ne s'applique pas?
JeffO
J'ai moi-même lutté avec cette règle et ce que Kate suggère est essentiellement ce que j'en ai conclu. En affaires, vous essayez de programmer des classes plus petites et plus flexibles afin de bien les réutiliser. Si vous les avez fait fonctionner correctement, vous ne voulez pas les modifier. Mais elles sont rarement entièrement "terminées", donc certaines modifications sont inévitables. Notez que le texte dit "module" cependant, pas objet. J'applique souvent avec succès l'OCP au niveau de la fonction, avec des fonctions étroites qui font parfaitement une chose et n'ont jamais besoin d'être modifiées.
CodexArcanum
1
@Jeff OI distingue entre la correction d'un bogue (où le code ne répondait pas à l'exigence d'origine et que personne ne le veut tel quel) et la modification des exigences. Si j'ai besoin de PDF et que le code crée des PDF, il n'y a pas de bogue, même si je veux maintenant du HTML (et généralement les gens veulent aussi du HTML, pas à la place de.)
Kate Gregory
2
@Winston - c'est ce que je voulais dire quand j'ai dit que vous devez écrire correctement la facture. Idéalement, il y avait déjà une facture assez abstraite et vous avez hérité de PDFInvoice qui s'y attendait. Sinon, vous devez enfreindre la règle une fois pour vous préparer à ne pas la casser à l'avenir. Quoi qu'il en soit, prédire les changements futurs est une partie énorme de tout cela - et c'est la partie «attraper et découper un éléphant» de la recette.
Kate Gregory
1
Votre dernière déclaration est la plus importante. Ouvert / fermé est un idéal de conception - et vous devez obtenir la conception dès le départ pour y parvenir. Tout n'a pas non plus besoin de satisfaire ouvert / fermé, mais c'est un outil puissant si vous pouvez y arriver.
Alex Feinman
13

Avez-vous lu l'article The Open-Closed Principle par les copains d'oncle Bob chez ObjectMentor? Je pense que c'est l'une des meilleures explications.

Il existe de nombreuses heuristiques associées à la conception orientée objet. Par exemple, «toutes les variables membres doivent être privées», ou «les variables globales doivent être évitées», ou «l'utilisation de l'identification de type à l'exécution (RTTI) est dangereuse». Quelle est la source de ces heuristiques? Qu'est-ce qui les rend vrai? Sont-ils toujours vrais? Cette colonne examine le principe de conception qui sous-tend ces heuristiques - le principe ouvert-fermé.

Comme Ivar Jacobson l'a dit: «Tous les systèmes changent au cours de leur cycle de vie. Cela doit être pris en compte lors du développement de systèmes qui devraient durer plus longtemps que la première version. »Comment créer des conceptions stables face au changement et qui dureront plus longtemps que la première version? Bertrand Meyer nous a donné des conseils dès 1988 lorsqu'il a inventé le désormais célèbre principe ouvert-fermé. Pour le paraphraser:

LES ENTITÉS LOGICIELLES (CLASSES, MODULES, FONCTIONS, ETC.) DEVRAIENT ÊTRE OUVERTES POUR EXTENSION, MAIS FERMÉES POUR MODIFICATION.

Lorsqu'un seul changement dans un programme entraîne une cascade de changements dans les modules dépendants, ce programme présente les attributs indésirables que nous en sommes venus à associer à une «mauvaise» conception. Le programme devient fragile, rigide, imprévisible et inutilisable. Le principe ouvert-fermé attaque cela d'une manière très simple. Il dit que vous devez concevoir des modules qui ne changent jamais . Lorsque les exigences changent, vous étendez le comportement de ces modules en ajoutant du nouveau code, pas en changeant l'ancien code qui fonctionne déjà.

La description

Les modules conformes au principe ouvert-fermé ont deux attributs principaux.

  1. Ils sont «ouverts pour l'extension».
    Cela signifie que le comportement du module peut être étendu. Que nous pouvons faire en sorte que le module se comporte de façons nouvelles et différentes à mesure que les exigences de l'application changent ou pour répondre aux besoins de nouvelles applications.
  2. Ils sont «fermés pour modification».
    Le code source d'un tel module est inviolable. Personne n'est autorisé à y apporter des modifications de code source.

Il semblerait que ces deux attributs soient en contradiction l'un avec l'autre. La manière normale d'étendre le comportement d'un module consiste à apporter des modifications à ce module. Un module qui ne peut pas être modifié est normalement considéré comme ayant un comportement fixe. Comment résoudre ces deux attributs opposés?

L'abstraction est la clé ...

Martijn Verburg
la source
3
Ceci est un bon article expliquant l'abstraction. Il y a un point fondamental à considérer, cependant, et c'est un bon dessin abstrait présenté en premier lieu? De nombreux magasins ont beaucoup de code hérité que la seule façon de le changer est la "modification", pas "l'extension". Si c'est le cas, alors on devrait probablement travailler pour changer cela, mais jusqu'à ce que ce soit le cas, vous êtes bloqué en modifiant le code.
Michael K
@Chris, cool - Je recommande également le livre "Clean code" d'oncle Bob si vous aimez ce genre de chose.
Martijn Verburg
@Michael - Tout à fait d'accord, c'est presque comme avoir à refactoriser le code pour le rendre testable idéal.
Martijn Verburg
L'article montre très bien l'importance de l'abstraction. Mais je ne saisis pas la connexion entre les abstractions et n'essaye jamais de modifier les modules après les avoir écrits. L'abstraction signifie que je peux modifier le module X sans avoir à apporter des modifications au module Y. Mais n'est-ce pas la raison de le faire que je puisse modifier le module X ou le module Y si je le dois?
Winston Ewert
1
Sensationnel. Le code est inviolable? Je n'ai jamais été un grand fan d'Oncle Bob. Ce principe est pédant, extrêmement non pragmatique et a un lien limité avec la réalité.
user949300
12

La réponse de Kate Gregory est très bonne, mais considérons une situation différente où une nouvelle exigence peut être satisfaite par un changement relativement faible de l'existantInvoice classe . Par exemple, supposons qu'un nouveau champ doit être ajouté au PDF de la facture. Selon OCP, nous devrions toujours créer une nouvelle sous-classe, même si le nouveau champ pouvait être ajouté dans l'implémentation existante en changeant quelques lignes de code.

À ma connaissance, OCP reflète la réalité des années 80 et du début des années 90, où les projets n'utilisaient souvent même pas le contrôle de version, encore moins des tests de régression automatisés ou les avantages d'outils de refactorisation sophistiqués. OCP était une tentative pour éviter le risque de casser du code qui avait été testé manuellement et mis en production. Aujourd'hui, nous avons de meilleures façons de gérer le risque de casser un logiciel fonctionnel (à savoir, les systèmes de contrôle de version, les tests TDD et automatisés et les outils de refactoring).

Rogério
la source
2
Oui, car dans la pratique, il n'est pas possible de créer une classe qui peut être étendue pour convenir à tous les futurs possibles, à moins que vous ne protégiez toutes les méthodes (ce qui aspire et viole également le principe YAGNI, qui est beaucoup plus important que l'O / C dito).
Martin Wickman
"Selon OCP, nous devrions toujours créer une nouvelle sous-classe, même si le nouveau champ pouvait être ajouté dans l'implémentation existante en changeant quelques lignes de code.": Vraiment? Pourquoi ne pas ajouter de nouveaux champs ou de nouvelles méthodes? Le point important est que vous ajoutez (étendez) uniquement et ne modifiez pas ce qui existe déjà.
Giorgio
Je pense que le principe est logique lorsqu'il s'agit de bibliothèques / cadres standard. Vous ne voulez pas ouvrir et modifier des morceaux de code bien établis. Sinon, il s'agit de refactoring constant et test, test, test.
mastaBlasta
@Giorgio Bien sûr, l'ajout de nouveaux champs ou méthodes est ce que je recommanderais, dans la plupart des cas. Mais ce n'est pas une extension , c'est une "modification"; tout l'intérêt d'OCP est que le code doit être "fermé pour modification" (c'est-à-dire, aucune modification du fichier source préexistant) tout en étant "ouvert pour extension"; et l'extension dans OCP est obtenue grâce à l'héritage d'implémentation.
Rogério
@ Rogério: Pourquoi définissez-vous la frontière entre extension et modification au niveau de la classe? A-t-il une raison particulière pour ceci? Je préfère le définir au niveau de la méthode: changer une méthode change le comportement de votre application, ajouter une méthode (publique) étend son interface.
Giorgio
6

Personnellement, je pense que ce principe doit être pris avec une pincée de sel. Le code est organique, les entreprises changent et changent selon les besoins d'une entreprise au fil du temps.

Je trouve très difficile de comprendre ce que l'abstraction est la clé. Et si l'abstraction était incorrecte à l'origine? Et si la fonction commerciale a changé de manière significative?

Ce principe garantit essentiellement que les intentions et le comportement ORIGINAUX d'une conception ne doivent jamais changer. Cela fonctionne probablement pour ceux qui ont des API publiques et leurs clients ont du mal à suivre les nouvelles versions et certains autres cas marginaux. Cependant, si une entreprise possède TOUS les codes, je conteste ce principe.

Avoir une bonne couverture de test de votre code devrait rendre le refactoring de votre base de code un jeu d'enfant. Cela signifie qu'il est normal de se tromper - vos tests vous aideront à mieux concevoir.

En disant que, s'il n'y a pas de tests, ce principe est valable.

user126776
la source