Können Sie das Liskov-Substitutionsprinzip (Das 'L' von SOLID) anhand eines guten C # -Beispiels erklären, das alle Aspekte des Prinzips auf vereinfachte Weise abdeckt? Wenn es wirklich möglich ist.
c#
.net
oop
solid-principles
liskov-substitution-principle
Bleistiftkuchen
quelle
quelle
Antworten:
(Diese Antwort wurde am 13.05.2013 umgeschrieben. Lesen Sie die Diskussion unten in den Kommentaren.)
Bei LSP geht es darum, dem Vertrag der Basisklasse zu folgen.
Sie können beispielsweise keine neuen Ausnahmen in den Unterklassen auslösen, da diejenige, die die Basisklasse verwendet, dies nicht erwarten würde. Gleiches gilt, wenn die Basisklasse auslöst,
ArgumentNullException
wenn ein Argument fehlt und die Unterklasse zulässt, dass das Argument null ist, was ebenfalls eine LSP-Verletzung darstellt.Hier ist ein Beispiel für eine Klassenstruktur, die gegen LSP verstößt:
Und der aufrufende Code
Wie Sie sehen können, gibt es zwei Beispiele für Enten. Eine Bio-Ente und eine elektrische Ente. Die elektrische Ente kann nur schwimmen, wenn sie eingeschaltet ist. Dies verstößt gegen das LSP-Prinzip, da es eingeschaltet sein muss, um als schwimmen zu können
IsSwimming
(was ebenfalls Bestandteil des Vertrags ist) nicht wie in der Basisklasse festgelegt wird.Sie können es natürlich lösen, indem Sie so etwas tun
Dies würde jedoch das Open / Closed-Prinzip brechen und muss überall implementiert werden (und generiert daher immer noch instabilen Code).
Die richtige Lösung wäre, die Ente in der
Swim
Methode automatisch einzuschalten und dadurch die elektrische Ente genau so zu verhalten, wie sie von derIDuck
Schnittstelle definiert wirdAktualisieren
Jemand hat einen Kommentar hinzugefügt und ihn entfernt. Es gab einen gültigen Punkt, den ich ansprechen möchte:
Die Lösung mit dem Einschalten der Ente innerhalb der
Swim
Methode kann Nebenwirkungen haben, wenn mit der tatsächlichen Implementierung gearbeitet wird (ElectricDuck
). Dies kann jedoch mithilfe einer expliziten Schnittstellenimplementierung gelöst werden . Imho ist es wahrscheinlicher, dass Sie Probleme bekommen, wenn Sie es NICHT einschalten,Swim
da erwartet wird, dass es schwimmt, wenn Sie dieIDuck
Schnittstelle verwendenUpdate 2
Einige Teile wurden umformuliert, um es klarer zu machen.
quelle
if duck is ElectricDuck
Teil beziehen . Ich hatte letzten Donnerstag ein Seminar über SOLID :)as
Schlüsselwort nicht, was sie vor vielen Typprüfungen bewahrt. Ich denke so etwas wie das Folgende: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 ein praktischer Ansatz
Überall, wo ich nach LSPs C # -Beispielen suche, haben Leute imaginäre Klassen und Schnittstellen verwendet. Hier ist die praktische Implementierung von LSP, die ich in einem unserer Systeme implementiert habe.
Szenario: Angenommen, wir haben 3 Datenbanken (Hypothekenkunden, Girokontokunden und Sparkontokunden), die Kundendaten bereitstellen, und wir benötigen Kundendaten für den Nachnamen des Kunden. Jetzt erhalten wir möglicherweise mehr als 1 Kundendetail aus diesen 3 Datenbanken gegen den angegebenen Nachnamen.
Implementierung:
GESCHÄFTSMODELLSCHICHT:
Datenzugriffsschicht:
Die obige Schnittstelle wird von der abstrakten Klasse implementiert
Diese abstrakte Klasse hat eine gemeinsame Methode "GetDetails" für alle 3 Datenbanken, die wie unten gezeigt um jede der Datenbankklassen erweitert wird
MORTGAGE KUNDENDATENZUGRIFF:
AKTUELLER KONTO KUNDENDATENZUGRIFF:
SAVINGS ACCOUNT KUNDENDATENZUGRIFF:
Sobald diese 3 Datenzugriffsklassen festgelegt sind, lenken wir unsere Aufmerksamkeit auf den Client. In der Business-Schicht haben wir die CustomerServiceManager-Klasse, die die Kundendaten an ihre Kunden zurückgibt.
Geschäftsschicht:
Ich habe die Abhängigkeitsinjektion nicht gezeigt, um sie einfach zu halten, da sie jetzt bereits kompliziert wird.
Wenn wir jetzt eine neue Kundendatenbank haben, können wir einfach eine neue Klasse hinzufügen, die BaseDataAccess erweitert und das Datenbankobjekt bereitstellt.
Natürlich benötigen wir in allen teilnehmenden Datenbanken identische gespeicherte Prozeduren.
Schließlich
CustomerServiceManager
ruft der Client für die Klasse nur die GetCustomerDetails-Methode auf, übergibt den Nachnamen und sollte sich nicht darum kümmern, wie und woher die Daten stammen.Ich hoffe, dies gibt Ihnen einen praktischen Ansatz zum Verständnis von LSP.
quelle
Hier ist der Code für die Anwendung des Liskov-Ersatzprinzips.
LSV-Zustände: "Abgeleitete Klassen sollten ihre Basisklassen (oder Schnittstellen) ersetzen können" & "Methoden, die Verweise auf Basisklassen (oder Schnittstellen) verwenden, müssen in der Lage sein, Methoden der abgeleiteten Klassen zu verwenden, ohne davon zu wissen oder die Details zu kennen . "
quelle