J'essaie de comprendre les principes SOLID de la programmation orientée objet et je suis parvenu à la conclusion que LSP et OCP présentaient certaines similitudes (sinon pour en dire plus).
les états de principe ouvert / fermé "les entités logicielles (classes, modules, fonctions, etc.) devraient être ouvertes à extension, mais fermées à modification".
LSP en mots simples stipule que toute instance de Foo
peut être remplacée par une instance Bar
dont le résultat est dérivé Foo
et que le programme fonctionnera de la même manière.
Je ne suis pas un programmeur pro de la POO, mais il me semble que le LSP n'est possible que si Bar
, dérivé de, Foo
ne change rien, mais le prolonge. Cela signifie que, en particulier, le programme LSP n’est vrai que lorsque OCP est vrai et OCP est vrai que si LSP est vrai. Cela signifie qu'ils sont égaux.
Corrige moi si je me trompe. Je veux vraiment comprendre ces idées. Grand merci pour une réponse.
Square
deRectangle
ne viole pas le LSP. (Mais il est sans doute encore une mauvaise conception dans le cas immuable puisque vous pouvez avoir carrésRectangle
s qui ne sont pas unSquare
qui ne correspond pas les mathématiques)Réponses:
Mesdames, il y a des idées fausses étranges sur ce que OCP et LSP et certaines sont dues à l'inadéquation de certaines terminologies et à des exemples déroutants. Les deux principes ne sont que "la même chose" si vous les implémentez de la même manière. Les modèles suivent généralement les principes d’une manière ou d’une autre, à quelques exceptions près.
Les différences seront expliquées plus bas, mais commençons par plonger dans les principes eux-mêmes:
Principe ouvert-fermé (OCP)
Selon l' oncle Bob :
Notez que le mot expand dans ce cas ne signifie pas nécessairement que vous devez sous-classer la classe réelle qui nécessite le nouveau comportement. Voyez comment j'ai mentionné à la première discordance de la terminologie? Le mot clé
extend
ne signifie que des sous-classes en Java, mais les principes sont plus anciens que Java.L'original est venu de Bertrand Meyer en 1988:
Ici, il est beaucoup plus clair que le principe est appliqué aux entités logicielles . Un mauvais exemple serait de remplacer l'entité logicielle lorsque vous modifiez complètement le code au lieu de fournir un point d'extension. Le comportement de l'entité logicielle elle-même devrait être extensible et un bon exemple de cela est l'implémentation du modèle-stratégie (car c'est le plus facile à montrer du groupe de modèles GoF à mon humble avis):
Dans l'exemple ci-dessus, le
Context
est verrouillé pour d'autres modifications. La plupart des programmeurs voudront probablement sous-classer la classe afin de l'étendre, mais ce n'est pas le cas ici, car il suppose que son comportement peut être modifié par tout ce qui implémente l'IBehavior
interface.C'est-à-dire que la classe de contexte est fermée pour modification mais ouverte pour extension . En réalité, il suit un autre principe de base, car nous mettons le comportement avec la composition d'objet au lieu d'un héritage:
Je laisserai le lecteur prendre connaissance de ce principe car il sort du cadre de cette question. Pour continuer avec l'exemple, supposons que nous ayons les implémentations suivantes de l'interface IBehavior:
En utilisant ce modèle, nous pouvons modifier le comportement du contexte au moment de l'exécution, via la
setBehavior
méthode en tant que point d'extension.Ainsi, chaque fois que vous souhaitez étendre la classe de contexte "fermé", faites-le en sous-classant sa dépendance de collaboration "ouverte". Ce n'est clairement pas la même chose que de sous-classer le contexte lui-même mais il s'agit d'OCP. LSP n'en fait aucune mention.
Extension avec Mixins au lieu de l'héritage
Il y a d'autres façons de faire OCP que le sous-classement. Une solution consiste à garder vos classes ouvertes à l'extension grâce à l'utilisation de mixins . Cela est utile, par exemple, dans les langages basés sur des prototypes plutôt que sur des classes. L'idée est d'amender un objet dynamique avec plus de méthodes ou d'attributs en fonction des besoins, autrement dit des objets qui se fondent ou se "mélangent" avec d'autres objets.
Voici un exemple javascript de mixin qui rend un modèle HTML simple pour les ancres:
L'idée est d'étendre les objets de manière dynamique et l'avantage est que les objets peuvent partager des méthodes même s'ils se trouvent dans des domaines complètement différents. Dans le cas ci-dessus, vous pouvez facilement créer d’autres types d’ancres HTML en étendant votre implémentation spécifique avec
LinkMixin
.En termes d'OCP, les "mixins" sont des extensions. Dans l'exemple ci-dessus,
YoutubeLink
notre entité logicielle est fermée pour modification, mais ouverte pour les extensions via l'utilisation de mixins. La hiérarchie des objets est aplatie, ce qui rend impossible la vérification des types. Cependant, ce n'est pas vraiment une mauvaise chose, et j'expliquerai plus loin que la recherche de types est généralement une mauvaise idée et qu'elle rompt avec le polymorphisme.Notez qu'il est possible d'effectuer plusieurs héritages avec cette méthode car la plupart des
extend
implémentations peuvent mélanger plusieurs objets:La seule chose que vous devez garder à l’esprit est de ne pas entrer en collision les noms, c’est-à-dire que les mixins définissent le même nom pour certains attributs ou méthodes lorsqu’ils seront remplacés. Dans mon humble expérience, ceci n’est pas un problème et si cela se produit, c’est une indication d’une conception défectueuse.
Principe de substitution de Liskov (LSP)
Oncle Bob le définit simplement par:
Ce principe est ancien. En fait, la définition de Oncle Bob ne différencie pas les principes, car le LSP est toujours étroitement lié à OCP par le fait que, dans l'exemple de stratégie ci-dessus, le même supertype est utilisé (
IBehavior
). Voyons donc sa définition originale de Barbara Liskov et voyons si nous pouvons trouver autre chose à propos de ce principe qui ressemble à un théorème mathématique:Permet de hausser les épaules pendant un moment, remarquez qu'il ne mentionne pas du tout les cours. En JavaScript, vous pouvez réellement suivre LSP même s'il n'est pas explicitement basé sur les classes. Si votre programme contient une liste d'au moins deux objets JavaScript qui:
... alors les objets sont considérés comme ayant le même "type" et cela n'a pas vraiment d'importance pour le programme. C'est essentiellement du polymorphisme . Au sens générique; vous ne devriez pas avoir besoin de connaître le sous-type réel si vous utilisez son interface. OCP ne dit rien d’explicite à ce sujet. Il identifie également une erreur de conception commise par la plupart des programmeurs débutants:
Chaque fois que vous ressentez le besoin de vérifier le sous-type d'un objet, vous le faites probablement FAUX.
Bien, cela n’est peut-être pas toujours faux, mais si vous avez l’envie de faire une vérification de type avec
instanceof
ou enums, vous ferez peut-être le programme un peu plus compliqué que nécessaire. Mais ce n'est pas toujours le cas. Des astuces rapides et sales pour faire fonctionner les choses sont une concession acceptable à faire dans mon esprit si la solution est suffisamment petite et si vous pratiquez une refactorisation sans merci , elle peut être améliorée une fois que les changements l'exigent.Selon le problème, il existe des moyens de contourner cette "erreur de conception":
Ces deux erreurs sont des "erreurs" de conception de code courantes. Vous pouvez effectuer plusieurs refactorisations différentes, telles que la méthode d'extraction ou la refactorisation d'un modèle tel que le modèle Visiteur .
En fait, j'aime beaucoup le modèle de visiteur car il peut traiter de gros spaghettis avec une instruction if et il est plus simple à mettre en œuvre que ce que l'on pourrait penser du code existant. Disons que nous avons le contexte suivant:
Les résultats de la déclaration if peuvent être traduits en leurs propres visiteurs car chacun dépend d'une décision et d'un code à exécuter. Nous pouvons les extraire comme ceci:
À ce stade, si le programmeur ne connaissait pas le modèle Visitor, il implémenterait plutôt la classe Context pour vérifier si elle est d'un certain type. Etant donné que les classes Visitor ont une
canDo
méthode booléenne , l'implémenteur peut utiliser cet appel de méthode pour déterminer s'il s'agit du bon objet pour effectuer le travail. La classe de contexte peut utiliser tous les visiteurs (et en ajouter de nouveaux) comme ceci:Les deux modèles suivent OCP et LSP, mais ils indiquent tous les deux des choses différentes. Alors, à quoi ressemble le code s'il viole l'un des principes?
Violer un principe mais suivre l'autre
Il existe des moyens de casser l'un des principes tout en laissant suivre l'autre. Les exemples ci-dessous semblent artificiels, pour une bonne raison, mais je les ai effectivement vus apparaître dans le code de production (et même pire):
Suit OCP mais pas LSP
Disons que nous avons le code donné:
Ce morceau de code suit le principe ouvert-fermé. Si nous appelons la
GetPersons
méthode du contexte , nous aurons un groupe de personnes ayant toutes leurs propres implémentations. Cela signifie qu'IPerson est fermé pour modification, mais ouvert pour extension. Cependant, les choses prennent une tournure sombre lorsque nous devons l’utiliser:Vous devez faire la vérification de type et la conversion de type! Rappelez-vous comment j'ai mentionné ci-dessus comment la vérification de type est une mauvaise chose ? Oh non! Mais ne craignez rien, comme mentionné également ci-dessus, soit procéder à une refactorisation en amont, soit mettre en œuvre un modèle de visiteur. Dans ce cas, nous pouvons simplement faire une refactorisation en ajoutant une méthode générale:
L'avantage à présent est que vous n'avez plus besoin de connaître le type exact après le LSP:
Suit le LSP mais pas l'OCP
Regardons un code qui suit LSP mais pas OCP, il est un peu artificiel mais supportez-moi sur celui-ci c'est une erreur très subtile:
Le code utilise LSP car le contexte peut utiliser LiskovBase sans connaître le type réel. Vous penseriez que ce code suit également OCP, mais regardez bien, la classe est-elle vraiment fermée ? Et si la
doStuff
méthode faisait plus que simplement imprimer une ligne?La réponse si elle suit OCP est simplement: NON , ce n'est pas parce que dans cette conception d'objet, nous devons remplacer le code complètement par autre chose. Cela ouvre la boîte de vers et coupe-coller, car vous devez copier le code de la classe de base pour que tout fonctionne. La
doStuff
méthode sure est ouverte à l'extension, mais elle n'a pas été complètement fermée pour modification.Nous pouvons appliquer le modèle de méthode Template à ce sujet. Le modèle de méthode template est si courant dans les frameworks que vous l'avez peut-être utilisé sans le savoir (par exemple, composants java swing, formes et composants c #, etc.). Voici une façon de fermer la
doStuff
méthode de modification et de s’assurer qu’elle reste fermée en la marquant avec lefinal
mot - clé java . Ce mot-clé empêche quiconque de sous-classer davantage la classe (en C #, vous pouvez utilisersealed
pour faire la même chose).Cet exemple suit OCP et semble idiot, ce qui est le cas, mais imaginez-le à plus grande échelle avec plus de code à gérer. Je continue à voir le code déployé en production où les sous-classes écrasent complètement tout et où le code substitué est généralement coupé-collé entre les implémentations. Cela fonctionne, mais comme pour tout code, la duplication est aussi une configuration pour les cauchemars de maintenance.
Conclusion
J'espère que tout cela efface certaines questions concernant OCP et LSP et les différences / similitudes entre eux. Il est facile de les rejeter comme identiques, mais les exemples ci-dessus doivent montrer qu'ils ne le sont pas.
Notez que, recueillant à partir de l'exemple de code ci-dessus:
OCP consiste à verrouiller le code de travail tout en le maintenant ouvert d'une manière ou d'une autre avec une sorte de point d'extension.
Cela permet d'éviter la duplication de code en encapsulant le code qui change comme dans l'exemple de modèle de modèle. Cela permet également d’échouer rapidement car briser les changements est douloureux (c’est-à-dire changer d’un endroit, le casser partout ailleurs). Pour des raisons de maintenance, le concept d'encapsulation du changement est une bonne chose, car les changements se produisent toujours .
LSP consiste à laisser l'utilisateur gérer différents objets qui implémentent un supertype sans vérifier leur type réel. C’est ce qui est fondamentalement le polymorphisme .
Ce principe offre une alternative à la vérification de type et à la conversion de type, qui peut devenir incontrôlable à mesure que le nombre de types augmente, et peut être obtenu par une refactorisation ou une application de modèles tels que Visiteur.
la source
C'est quelque chose qui cause beaucoup de confusion. Je préfère considérer ces principes de manière quelque peu philosophique, car ils ont de nombreux exemples différents, et parfois des exemples concrets ne rendent pas vraiment compte de leur essence.
Ce que OCP essaie de réparer
Supposons que nous devions ajouter des fonctionnalités à un programme donné. La façon la plus simple de s'y prendre, en particulier pour les personnes formées à la procédure, consiste à ajouter une clause if si nécessaire, ou quelque chose du genre.
Les problèmes avec qui sont
Vous pouvez le faire en ajoutant un champ supplémentaire à tous les livres nommés « is_on_sale », et vous pouvez vérifier ce champ lors de l' impression du prix de tout livre, ou encore , vous pouvez instancier livres en vente à partir de la base de données en utilisant un autre type, qui imprime "(EN VENTE)" dans la chaîne de prix (pas un design parfait mais il fournit le point à la maison).
Le problème avec la première solution procédurale est un champ supplémentaire pour chaque livre et une complexité redondante supplémentaire dans de nombreux cas. La deuxième solution oblige uniquement la logique là où elle est réellement requise.
Examinons maintenant le fait qu'il peut exister de nombreux cas dans lesquels des données et une logique différentes sont nécessaires et vous comprendrez pourquoi il est judicieux de garder à l'esprit OCP lors de la conception de vos classes ou de réagir aux modifications des exigences.
Vous devriez maintenant avoir l’idée principale: essayez de vous mettre dans une situation où un nouveau code peut être implémenté sous la forme d’extensions polymorphes, et non de modifications procédurales.
Mais n'ayez jamais peur d'analyser le contexte et de voir si les inconvénients l'emportent sur les avantages, car même un principe tel que OCP peut créer un gâchis de 20 classes sur un programme de 20 lignes s'il n'est pas traité avec soin .
Ce que LSP essaie de réparer
Nous aimons tous la réutilisation du code. Une maladie qui s'ensuit est que de nombreux programmes ne le comprennent pas complètement, au point de facturer aveuglément des lignes de code communes uniquement pour créer des complexités illisibles et un couplage étroit redondant entre des modules qui, à part quelques lignes de code, n'ont rien en commun en ce qui concerne le travail conceptuel à accomplir.
Le meilleur exemple en est la réutilisation des interfaces . Vous l'avez probablement déjà vu vous-même. une classe implémente une interface, non pas parce que c'est une implémentation logique (ou une extension dans le cas de classes de base concrètes), mais parce que les méthodes qu'elle déclare à ce moment-là ont les bonnes signatures en ce qui le concerne.
Mais alors vous rencontrez un problème. Si les classes implémentent des interfaces uniquement en considérant les signatures des méthodes qu'elles déclarent, vous pouvez alors passer des instances de classes d'une fonctionnalité conceptuelle à des emplacements qui exigent des fonctionnalités complètement différentes, qui ne dépendent que de signatures similaires.
Ce n'est pas si horrible, mais cela crée beaucoup de confusion et nous disposons de la technologie pour nous empêcher de commettre de telles erreurs. Ce que nous devons faire, c'est traiter les interfaces comme API + Protocole . L'API apparaît dans les déclarations et le protocole dans les utilisations existantes de l'interface. Si nous avons 2 protocoles conceptuels qui partagent la même API, ils devraient être représentés sous la forme de 2 interfaces différentes. Sinon, nous sommes pris dans le dogmatisme DRY et, paradoxalement, nous ne créons que plus de difficulté à maintenir le code.
Maintenant, vous devriez être capable de comprendre parfaitement la définition. Le LSP dit: Ne pas hériter d'une classe de base et implémenter des fonctionnalités dans ces sous-classes avec lesquelles d'autres emplacements, qui dépendent de la classe de base, ne s'entendent pas.
la source
De ma compréhension:
OCP indique: "Si vous souhaitez ajouter de nouvelles fonctionnalités, créez une nouvelle classe qui étend une classe existante, plutôt que de la modifier."
LSP indique: "Si vous créez une nouvelle classe étendant une classe existante, assurez-vous qu'elle est parfaitement interchangeable avec sa base."
Je pense donc qu'ils se complètent mais qu'ils ne sont pas égaux.
la source
S'il est vrai qu'OCP et LSP ont tous deux à voir avec la modification, le type de modification dont parle OCP n'est pas celui dont parle LSP.
La modification concernant OCP est l'action physique d'un développeur qui écrit du code dans une classe existante.
LSP traite de la modification du comportement apportée par une classe dérivée par rapport à sa classe de base, ainsi que de la modification à l' exécution de l' exécution du programme pouvant être provoquée par l'utilisation de la sous-classe à la place de la superclasse.
Donc, bien qu'ils puissent paraître similaires à distance, OCP! = LSP. En fait, je pense que ce sont peut-être les deux seuls principes SOLID qui ne peuvent pas être compris l'un par l'autre.
la source
C'est faux. LSP déclare que la classe Bar ne doit pas introduire de comportement, ce qui n'est pas prévu lorsque le code utilise Foo, lorsque Bar est dérivé de Foo. Cela n'a rien à voir avec une perte de fonctionnalité. Vous pouvez supprimer des fonctionnalités, mais uniquement lorsque du code utilisant Foo ne dépend pas de cette fonctionnalité.
Mais en fin de compte, cela est généralement difficile à réaliser, car la plupart du temps, le code utilisant Foo dépend de tout son comportement. Donc, l'enlever viole le LSP. Mais simplifier comme ceci n’est qu’une partie du LSP.
la source
À propos des objets susceptibles de violer
Pour comprendre la différence, vous devez comprendre les sujets des deux principes. Ce n'est pas une partie abstraite du code ou une situation qui peut violer ou non un principe. Ce sont toujours des composants spécifiques - fonction, classe ou module - qui peuvent violer OCP ou LSP.
Qui peut violer le LSP
On peut vérifier si le LSP est cassé uniquement lorsqu'il existe une interface avec un contrat et une implémentation de cette interface. Si la mise en œuvre n'est pas conforme à l'interface ou, en général, au contrat, le LSP est cassé.
Exemple le plus simple:
Le contrat stipule clairement que
addObject
doit annexer son argument au conteneur. EtCustomContainer
brise clairement ce contrat. Ainsi, laCustomContainer.addObject
fonction viole LSP. Ainsi, laCustomContainer
classe viole LSP. La conséquence la plus importante est qu’onCustomContainer
ne peut pas la transmettrefillWithRandomNumbers()
.Container
ne peut pas être remplacé parCustomContainer
.Gardez à l'esprit un point très important. Ce n'est pas tout ce code qui casse le LSP, c'est spécifiquement
CustomContainer.addObject
et généralement ceCustomContainer
qui le casse. Lorsque vous déclarez que LSP est violé, vous devez toujours spécifier deux choses:C'est ça. Juste un contrat et sa mise en œuvre. Un code négatif dans le code ne dit rien sur la violation de LSP.
Qui peut violer OCP
On peut vérifier si OCP est violé uniquement lorsqu'il existe un ensemble de données limité et un composant qui gère les valeurs de cet ensemble de données. Si les limites de l'ensemble de données peuvent changer au fil du temps et qu'il faut modifier le code source du composant, celui-ci enfreint OCP.
Cela semble complexe. Essayons un exemple simple:
L'ensemble de données est l'ensemble des plates-formes prises en charge.
PlatformDescriber
est le composant qui gère les valeurs de cet ensemble de données. L'ajout d'une nouvelle plate-forme nécessite la mise à jour du code source dePlatformDescriber
. Ainsi, laPlatformDescriber
classe viole OCP.Un autre exemple:
Le "jeu de données" est l'ensemble des canaux où une entrée de journal doit être ajoutée.
Logger
est le composant responsable de l'ajout d'entrées à tous les canaux. L'ajout d'une prise en charge pour un autre mode de journalisation nécessite la mise à jour du code source deLogger
. Ainsi, laLogger
classe viole OCP.Notez que dans les deux exemples, l'ensemble de données n'est pas quelque chose de fixe sémantiquement. Cela peut changer avec le temps. Une nouvelle plateforme peut émerger. Un nouveau canal de journalisation peut émerger. Si votre composant doit être mis à jour lorsque cela se produit, il viole OCP.
Pousser les limites
Maintenant la partie la plus délicate. Comparez les exemples ci-dessus aux suivants:
Vous pensez peut-être qu’il
translateToRussian
enfreint OCP. Mais en réalité ce n'est pas.GregorianWeekDay
a une limite spécifique de exactement 7 jours de semaine avec les noms exacts. Et l’important est que ces limites sémantiquement ne puissent pas changer avec le temps. Il y aura toujours 7 jours dans la semaine grégorienne. Il y aura toujours lundi, mardi, etc. Cet ensemble de données est fixé sémantiquement. Il n'est pas possible que cetranslateToRussian
code source nécessite des modifications. Ainsi, OCP n'est pas violé.Maintenant, il devrait être clair qu'une
switch
déclaration épuisante n'est pas toujours une indication d'un OCP cassé.La différence
Maintenant sentez la différence:
Ces conditions sont complètement orthogonales.
Exemples
Dans @ la réponse de Spoike le Violer un principe mais après l'autre partie est tout à fait tort.
Dans le premier exemple, la
for
partie -loop viole clairement OCP car elle n'est pas extensible sans modification. Mais il n'y a aucune indication de violation de LSP. Et il n'est même pas clair si leContext
contrat permet à getPersons de renvoyer quoi que ce soit saufBoss
ouPeon
. Même en supposant qu'un contrat autorisant leIPerson
renvoi de n'importe quelle sous-classe, aucune classe ne remplace cette conditionnalité et ne la viole pas. De plus, si getPersons retourne une instance d'une troisième classe, lefor
-loop fera son travail sans échec. Mais ce fait n'a rien à voir avec le LSP.Prochain. Dans le deuxième exemple, ni LSP, ni OCP ne sont violés. Encore une fois, la
Context
partie n’a rien à voir avec le LSP - pas de contrat défini, pas de sous-classement, pas de dérogation prioritaire. Ce n'est pas àContext
qui devrait obéir le LSP, c'est àLiskovSub
ne pas rompre le contrat de sa base. En ce qui concerne OCP, la classe est-elle vraiment fermée? - Oui, ça l'est. Aucune modification n'est nécessaire pour l'étendre. Évidemment, le nom du point d’extension indique Faites tout ce que vous voulez, sans limite . L'exemple n'est pas très utile dans la vie réelle, mais il ne viole manifestement pas OCP.Essayons de faire quelques exemples corrects avec une véritable violation de OCP ou LSP.
Suivez OCP mais pas LSP
Ici,
HumanReadablePlatformSerializer
ne nécessite aucune modification quand une nouvelle plate-forme est ajoutée. Ainsi, il suit OCP.Mais le contrat exige que vous
toJson
devez renvoyer un JSON correctement formaté. La classe ne fait pas ça. De ce fait, il ne peut pas être transmis à un composant qui utilisePlatformSerializer
pour formater le corps d'une requête réseau.HumanReadablePlatformSerializer
Viole ainsi le LSP.Suivez LSP mais pas OCP
Quelques modifications à l'exemple précédent:
Le sérialiseur renvoie une chaîne JSON correctement formatée. Donc, pas de violation de LSP ici.
Toutefois, si la plate-forme est le plus largement utilisée, il doit exister une indication correspondante en JSON. Dans cet exemple, OCP ne respecte pas les
HumanReadablePlatformSerializer.isMostPopular
fonctions car un jour, iOS devient la plate-forme la plus populaire. Formellement, cela signifie que l'ensemble des plates-formes les plus utilisées est défini pour le moment comme "Android" et neisMostPopular
gère pas correctement cet ensemble de données. L'ensemble de données n'est pas sémantiquement fixé et peut changer librement dans le temps.HumanReadablePlatformSerializer
Le code source de doit être mis à jour en cas de changement.Vous pouvez également remarquer une violation de la responsabilité unique dans cet exemple. Je l'ai fait intentionnellement pour pouvoir démontrer les deux principes sur la même entité. Pour corriger SRP, vous pouvez extraire la
isMostPopular
fonction sur certains externesHelper
et ajouter un paramètre àPlatformSerializer.toJson
. Mais c'est une autre histoire.la source
LSP et OCP ne sont pas identiques.
LSP parle de l'exactitude du programme tel qu'il se présente . Si une instance d'un sous-type pourrait altérer l'exactitude du programme lorsqu'elle est substituée dans le code pour les types ancêtres, vous avez démontré une violation du LSP. Vous devrez peut-être simuler un test pour montrer cela, mais vous ne devrez pas changer la base de code sous-jacente. Vous validez le programme lui-même pour voir s'il est conforme à LSP.
OCP parle de l'exactitude des modifications apportées au code du programme, le delta d'une version source à une autre. Le comportement ne doit pas être modifié. Il devrait seulement être étendu. L'exemple classique est l'ajout de champs. Tous les champs existants continuent à fonctionner comme avant. Le nouveau champ ajoute simplement des fonctionnalités. La suppression d'un champ constitue toutefois une violation d'OCP. Ici, vous validez le delta de version du programme pour voir s’il répond à OCP.
Voilà donc la principale différence entre LSP et OCP. Le premier ne valide que la base de code en l'état , le second ne valide que le delta de base de code d'une version à l'autre . En tant que tels, ils ne peuvent pas être la même chose, ils sont définis comme valider des choses différentes.
Je vais vous donner une preuve plus formelle: Dire "LSP implique OCP" signifierait un delta (car OCP en requiert un autre que dans le cas trivial), mais LSP n'en exige pas un. Donc, c'est clairement faux. Inversement, nous pouvons réfuter "OCP implique LSP" en disant simplement qu'OCP est une déclaration sur les deltas et qu'il ne dit donc rien sur une déclaration sur un programme en place. Cela découle du fait que vous pouvez créer TOUT delta en commençant par TOUT programme en place. Ils sont totalement indépendants.
la source
Je regarderais cela du point de vue du client. si le client utilise les fonctionnalités d'une interface et que cette fonctionnalité a été implémentée en interne par la classe A. Supposons qu'il existe une classe B qui étend la classe A, alors demain si je supprime la classe A de cette interface et mets la classe B, alors la classe B devrait fournir également les mêmes fonctionnalités au client. L'exemple standard est une classe de canard qui nage, et si ToyDuck étend Duck, il doit également nager et ne se plaint pas de l'impossibilité de nager, sinon ToyDuck ne devrait pas avoir prolongé la classe de canard.
la source