2013-02-03 14 views
6

W projekcie pracowałem ostatnio zauważyłem, że niektóre metody, które zostały przyjmowanie klasę, która należy do hierarchii, miał kodu podobnego do następującego:użytkowania Klasa pułapek łamanie zasada podstawienia liskov

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

Dobrze, że uderzyło mnie jako rażące naruszenie LSP, ponieważ teraz , jeśli używasz podklasy psa, albo w kodzie produkcyjnym, próbnym testowaniu jednostkowym, albo jako część przechwytującego zależność wtrysku ten kod nie będzie działał w ten sam sposób. Wierzę, że ten konkretny scenariusz może być ustalona z łatwością, zmieniając warunek:

if (animal is Dog) 

To dało mi do myślenia choć:

Czy są jakieś inne pułapki do poszukiwania, które mogą złamać LSP w kodzie klienta?

UPDATE

Właśnie w celu wyjaśnienia, szukam możliwych pułapek w kodzie, który używa klasy w hierarchii. Jestem świadomy i nie szukam problemów z źle skonstruowanymi hierarchiami (np. Prostokąt - kwadratowy problem). Próbuję dowiedzieć się, czego szukać, aby upewnić się, że kod będzie obsługiwał dynamiczne makiety, przechwytywacze, urządzone klasy (na przykład LoggingDog) w taki sam sposób, w jaki obsługuje klasę oryginalną.

Do tej pory po przejrzeniu odpowiedzi i linków widzę, że jedyną pułapką byłoby użycie typu klasy bezpośrednio - to jest metoda GetType() bezpośrednio lub za pomocą innej technologii. Pomimo pewnych komentarzy tutaj operatory, a nawet rzutowanie na typ bazowy nie złamie LSP w tym przypadku, ponieważ podklasy będą oceniać w ten sam sposób co oryginalna klasa.

+3

Nie widzę, co C++ ma do czynienia z czymkolwiek, miałbyś ten sam problem z kodem C++ (chociaż oczywiście używałbyś typeof dla obu, ponieważ C++ nie ma typu podstawowego obiektu). Możliwe też, że kod faktycznie nie chce czegoś zrobić, jeśli jest to wyprowadzona wersja psa. Może to być celowe. –

+0

Wspomniałem o C++, ponieważ uznałem, że był to zwyczaj z RTTI w C++. Jestem prawie pewien, że w tym przypadku nie było to zamierzone, chociaż widzę twoją sprawę. –

+0

Zaktualizowałem pytanie, przepraszam, może nie było to zbyt jasne. Chciałem tylko wiedzieć, jak zidentyfikować kod klienta, który może zepsuć LSP –

Odpowiedz

4

Jeżeli zignorujemy na chwilę, że faktycznie istnieją przypadki, w których może być konieczne, aby rzucić się do konkretnego typu, w większości przypadków problem można rozwiązać poprzez zmianę motywu:

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"); 
    } 
} 

Stosując ten projekt , unikamy potrzeby wrzucania kodu do przetwarzania zwierząt:

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

masz rację. Jednak w moim przypadku kod ten odpowiada fragmentowi kodu uruchamiającemu odpowiedni kreator w warstwie interfejsu użytkownika na podstawie typu obiektu domeny. Naprawdę nie mogę go zmienić, a także ten kod nie będzie należał do obiektu domeny, ponieważ jest to logika interfejsu użytkownika. –

+0

Oczywiście niektóre z nich to robi, ponieważ zależy to od obiektu domeny. Możesz po prostu przekazać "IWizardLauncher" do metody "Process" i wywołać obiekt domeny, gdy wykona swoją część. Mówię tylko, że istnieją odpowiednie rozwiązania, które nie łamią Twojej architektury i nadal nie wymagają przesyłania. –