J'ai écrit une réponse précédemment sur le principe ouvert-fermé (OCP) vs le principe de substitution de Liskov (LSP) et ces deux principes se rapportent beaucoup, mais sont toujours conceptuellement différents avec quelques exemples artificiels d'avoir l'un mais pas l'autre. En raison de cette réponse, je ne parlerai que brièvement d'OCP et approfondirai DIP et ce qui fait que cela fonctionne.
Essayons de discuter de la façon dont l'OCP est lié et diffère du principe d'inversion de dépendance (DIP) en expliquant d'abord les différents principes.
Principe d'inversion de dépendance
En lisant les principes d'OOD de l'oncle Bob, vous constaterez que DIP indique ce qui suit:
Dépendent d'abstractions, pas de concrétions.
Une abstraction en Java est simplement réalisée avec les mots interface
- abstract
clés et , ce qui signifie que vous avez un "contrat" pour une entité logicielle que le code doit suivre. Certains langages de programmation n'ont pas la possibilité de définir explicitement les comportements à suivre par le code, de sorte que les abstractions doivent être suivies de manière plus manuelle plutôt que de demander au compilateur de vous aider à appliquer le contrat. Par exemple, en C ++, vous avez des classes avec des méthodes virtuelles et des langages de programmation dynamiques tels que Javascript, vous devez vous assurer que vous utilisez les objets de la même manière (bien que dans le cas de Javascript, cela a été étendu en TypeScript qui ajoute un système de type pour vous aider avec des contrats d'écriture vérifiés par le compilateur).
Le nom inclut le terme "inversion" parce que traditionnellement (vous savez dans les vieux âges sombres de la programmation) vous écriviez des structures logicielles qui avaient des modules de niveau supérieur en fonction de modules de bas niveau. Par exemple, il était logique d'avoir ButtonAtKitchen
des entrées de gestion pour a KitchenLamp1
et KitchenLamp2
. Malheureusement, cela a rendu le logiciel beaucoup plus spécifique qu'il ne devait l'être et le graphique d'objet ressemblerait à ceci:
Donc quand vous rendez le logiciel plus général, en ajoutant des "contrats". Remarquez comment les flèches du graphique d'objet "inversent" la direction. Que les lampes de cuisine dépendent désormais d'un Button
. En d'autres termes, les détails dépendent désormais des abstractions et non l'inverse.
Ainsi, nous avons une définition plus générale de DIP, également détaillée dans l' article original de DIP par Oncle Bob .
A. Les modules de haut niveau ne doivent pas dépendre de modules de bas niveau. Les deux devraient dépendre de l'abstraction. B. Les abstractions ne devraient pas dépendre des détails. Les détails doivent dépendre des abstractions.
Principe ouvert-fermé
Suite aux principes de l'oncle Bob, vous constaterez que l'OCP déclare ce qui suit:
Vous devriez pouvoir étendre un comportement de classe sans le modifier.
Un exemple de cet objectif consiste à utiliser le modèle de stratégie dans lequel une Context
classe est fermée pour modifications (c'est-à-dire que vous ne pouvez pas du tout changer son code interne) mais est également ouverte pour une extension via ses dépendances collaboratrices (c'est-à-dire les classes de stratégie).
Dans un sens plus général, tout module est conçu pour être extensible via ses points d'extension.
OCP est similaire à DIP, non?
Non , pas vraiment.
Bien qu'ils discutent tous les deux des abstractions, ils sont conceptuellement différents. Les deux principes examinent différents contextes, OCP sur un module particulier et DIP sur plusieurs modules. Vous pouvez réaliser les deux en même temps qu'avec la plupart des modèles de conception Gang of Four, mais vous pouvez toujours vous éloigner du chemin.
Dans l'exemple DIP mentionné ci-dessus, avec le bouton et les lampes de cuisine, aucune des lampes de cuisine n'est extensible (ni aucune exigence expliquant actuellement qu'elles doivent l'être). La conception brise l'OCP mais suit DIP .
Un exemple inversé (et artificiel) serait une lampe de cuisine extensible (le point d'extension étant quelque chose comme un LampShade
), mais le bouton dépend toujours des lampes . Il casse DIP mais suit OCP .
Ne t'inquiète pas, ça arrive
C'est en fait quelque chose que vous verrez arriver souvent dans le code de production, dont une partie peut briser un principe. Dans les systèmes logiciels plus grands (c'est-à-dire quelque chose de plus grand que les exemples ci-dessus), vous pourriez casser un principe mais garder l'autre généralement parce que vous devez garder le code simple. C'est, à mon avis, correct pour les petits modules autonomes, car ils sont dans le contexte lié au principe de responsabilité unique (PRS).
Une fois que le module se complique un peu si vous avez besoin le plus susceptible de regarder avec tous les principes à l' esprit et redessiner ou factoriser à un certain modèle bien connu.