Pouvez-vous expliquer le principe de substitution de Liskov (Le «L» de SOLID) avec un bon exemple C # couvrant tous les aspects du principe de manière simplifiée? Si c'est vraiment possible.
c#
.net
oop
solid-principles
liskov-substitution-principle
crayonGâteau
la source
la source
Réponses:
(Cette réponse a été réécrite 2013-05-13, lisez la discussion au bas des commentaires)
LSP consiste à suivre le contrat de la classe de base.
Vous pouvez par exemple ne pas lancer de nouvelles exceptions dans les sous-classes car celle qui utilise la classe de base ne s'y attendrait pas. Il en va de même si la classe de base se lance
ArgumentNullException
si un argument est manquant et que la sous-classe autorise l'argument à être nul, également une violation LSP.Voici un exemple de structure de classe qui viole LSP:
Et le code d'appel
Comme vous pouvez le voir, il existe deux exemples de canards. Un canard bio et un canard électrique. Le canard électrique ne peut nager que s'il est allumé. Cela rompt le principe du LSP car il doit être activé pour pouvoir nager car le
IsSwimming
(qui fait également partie du contrat) ne sera pas défini comme dans la classe de base.Vous pouvez bien sûr le résoudre en faisant quelque chose comme ça
Mais cela briserait le principe Open / Closed et doit être implémenté partout (et donc génère toujours du code instable).
La solution appropriée serait d'allumer automatiquement le canard dans la
Swim
méthode et, ce faisant, de faire en sorte que le canard électrique se comporte exactement comme défini par l'IDuck
interfaceMettre à jour
Quelqu'un a ajouté un commentaire et l'a supprimé. Il y avait un point valable que j'aimerais aborder:
La solution
Swim
consistant à activer le canard à l'intérieur de la méthode peut avoir des effets secondaires lorsque vous travaillez avec l'implémentation réelle (ElectricDuck
). Mais cela peut être résolu en utilisant une implémentation d'interface explicite . à mon humble avis, il est plus probable que vous rencontriez des problèmes en ne l'allumant PASSwim
car on s'attend à ce qu'il nage lors de l'utilisation de l'IDuck
interfaceMise à jour 2
Reformulé certaines parties pour le rendre plus clair.
la source
if duck is ElectricDuck
pièce. J'ai eu un séminaire sur SOLID jeudi dernier :)as
mot - clé, ce qui les évite en fait de nombreuses vérifications de type. Je pense à quelque chose comme ceci:if var electricDuck = duck as ElectricDuck; if(electricDuck != null) electricDuck.TurnOn();
if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program (e.g., correctness).
LSP une approche pratique
Partout où je cherche des exemples C # de LSP, les gens ont utilisé des classes et des interfaces imaginaires. Voici l'implémentation pratique de LSP que j'ai implémentée dans l'un de nos systèmes.
Scénario: Supposons que nous ayons 3 bases de données (clients hypothécaires, clients des comptes courants et clients des comptes d'épargne) qui fournissent des données client et que nous ayons besoin des détails du client pour le nom de famille du client donné. Maintenant, nous pouvons obtenir plus d'un détail client de ces 3 bases de données par rapport au nom de famille donné.
La mise en oeuvre:
COUCHE DE MODÈLE D'AFFAIRES:
COUCHE D'ACCÈS AUX DONNÉES:
L'interface ci-dessus est implémentée par la classe abstraite
Cette classe abstraite a une méthode commune «GetDetails» pour les 3 bases de données qui est étendue par chacune des classes de base de données comme indiqué ci-dessous
ACCÈS AUX DONNÉES DU CLIENT HYPOTHÉCAIRE:
ACCÈS AUX DONNÉES CLIENT DU COMPTE ACTUEL:
ACCÈS AUX DONNÉES CLIENT DU COMPTE D'ÉPARGNE:
Une fois ces 3 classes d'accès aux données définies, nous attirons maintenant notre attention sur le client. Dans la couche Business, nous avons la classe CustomerServiceManager qui renvoie les détails du client à ses clients.
COUCHE D'AFFAIRES:
Je n'ai pas montré l'injection de dépendances pour rester simple car cela se complique déjà maintenant.
Maintenant, si nous avons une nouvelle base de données de détails client, nous pouvons simplement ajouter une nouvelle classe qui étend BaseDataAccess et fournit son objet de base de données.
Bien entendu, nous avons besoin de procédures stockées identiques dans toutes les bases de données participantes.
Enfin, le client pour la
CustomerServiceManager
classe appellera uniquement la méthode GetCustomerDetails, transmettra le lastName et ne devrait pas se soucier de la provenance et de la provenance des données.J'espère que cela vous donnera une approche pratique pour comprendre LSP.
la source
Voici le code pour appliquer le principe de remplacement de Liskov.
LSV déclare: "Les classes dérivées doivent être substituables à leurs classes de base (ou interfaces)" & "Les méthodes qui utilisent des références aux classes de base (ou interfaces) doivent pouvoir utiliser les méthodes des classes dérivées sans le savoir ni connaître les détails . "
la source