2013-02-03 3 views
6

In einem Projekt, das ich vor kurzem arbeitete, bemerken, dass einige Methoden, die eine Klasse wurden zu akzeptieren, die zu einer Hierarchie gehört, Code hatten ähnlich wie folgt vor:Klasse Verwendung Gefahren brechen Liskov Substitutionsprinzip

public void Process(Animal animal) { 
    if(animal.GetType() == typeof(Dog)) { 
     Console.WriteLine("We have a dog"); 
    } 
    else { 
     Console.WriteLine("not a dog"); 
    } 
} 

Nun, das schlug mich als eklatante Verletzung von LSP, denn jetzt , wenn Sie eine Unterklasse eines Hundes, entweder in Produktionscode, Unit-Test-Mock, oder als Teil der Abhängigkeits-Injektion Interceptor dieser Code wird nicht auf die gleiche Weise arbeiten. Ich glaube, diese speziellen Szenario leicht behoben werden kann, durch an sich ändernde Bedingung:

if (animal is Dog) 

Das hat mich allerdings bekam denken:

Gibt es noch andere Gefahren für das aussehen kann LSP brechen in Client-Code?

UPDATE

Nur um zu klären, ich bin für mögliche Gefahren im Code suchen, der die Klasse in einer Hierarchie verwendet. Ich bin mir bewusst, und ich suche keine Probleme mit schlecht konstituierten Hierarchien (z. B. Rechteck - Quadrat - Problem). Ich versuche herauszufinden, nach was ich suchen soll, um sicherzustellen, dass der Code dynamische Mocks, Interceptors, dekorierte Klassen (z. B. LoggingDog) genauso unterstützt wie die ursprüngliche Klasse.

So weit nach dem Durchlaufen der Antworten und Links kann ich sehen, dass die einzige Falle wäre, Typ der Klasse direkt zu verwenden - das ist GetType() Methode direkt oder durch eine andere Technologie zu verwenden. Trotz einiger Kommentare hier is und as Operatoren, und sogar Gießen zu Basistyp wird LSP in diesem Fall nicht brechen, wie Unterklassen werden auf die gleiche Weise wie die ursprüngliche Klasse würde.

+3

ich sehe nicht, was C++ mit etwas zu tun hat, würden Sie das gleiche Problem mit C++ Code haben (obwohl offensichtlich würden Sie verwenden typeof für beide, da C++ keinen Objektbasistyp hat). Es kann auch sein, dass der Code tatsächlich nicht etwas tun möchte, wenn es sich um eine abgeleitete Version von Hund handelt. Dies kann beabsichtigt sein. –

+0

Ich erwähnte C++, weil ich dachte, dass dies eine Gewohnheit aus RTTI in C++ war. Ich bin mir ziemlich sicher, dass es in diesem Fall nicht beabsichtigt war, obwohl ich Ihren Standpunkt verstehe. –

+0

Ich habe die Frage aktualisiert, sorry, vielleicht war ich nicht so klar. Ich wollte nur wissen, wie Client-Code zu identifizieren, die vielleicht LSP brechen –

Antwort

4

Wenn wir für den Moment ignorieren, dass es tatsächlich Fälle, in denen Sie auf eine bestimmte Art werfen müssen in den meisten Fällen das Problem durch eine Änderung der Konstruktion gelöst werden können:

public class Animal 
{ 
    public virtual void Process() 
    { 
     Console.WriteLine("Animal (not a dog)"); 
    } 
} 

public class Dog : Animal 
{ 
    public override void Process() 
    { 
     Console.WriteLine("We have a dog"); 
    } 
} 

Durch diesen Entwurf mit vermeiden wir die Notwendigkeit, in den Code der Verarbeitung der Tiere zu werfen:

var animal = ...; // maybe a Dog, maybe not 
animal.Process(); 
+0

Sie sind richtig. In meinem Fall entspricht dieser Code jedoch einem Code, der auf der Grundlage des Typs des Domänenobjekttyps einen geeigneten Assistenten im UI-Layer startet. Ich kann es nicht wirklich ändern, und auch dieser Code würde nicht in Domain-Objekte gehören, da es sich um UI-Logik handelt. –

+0

Offensichtlich einige davon, da es vom Domain-Objekt abhängt. Sie könnten einfach einen 'IWizardLauncher' an die' Process'-Methode übergeben und das Domänenobjekt aufrufen lassen, wenn es seinen Teil getan hat. Ich sage nur, dass es richtige Lösungen gibt, die Ihre Architektur nicht durchbrechen und trotzdem kein Casting erfordern. –

Verwandte Themen