2013-02-03 9 views
6

In un progetto ho lavorato di recente, notato che alcuni metodi che accettavano una classe che appartiene a una gerarchia, avevano il codice simile al seguente:insidie ​​di utilizzo Classe rottura Liskov principio di sostituzione

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

Beh, mi ha colpito come una palese violazione di LSP, perché ora se si utilizza una sottoclasse di un cane, nel codice di produzione, test di unità di simulazione o come parte dell'intercettore di intercettazione delle dipendenze questo codice non funzionerà allo stesso modo. Credo che questo particolare scenario può essere risolto facilmente, modificando condizione:

if (animal is Dog) 

che mi ha fatto pensare però:

Ci sono altre insidie ​​per cercare che può rompere LSP nel codice cliente?

UPDATE

Giusto per chiarire, io sono alla ricerca di possibili insidie ​​nel codice che utilizza la classe in una gerarchia. Sono consapevole e non sto cercando problemi con gerarchie mal gestite - (ad esempio rettangolo - problema quadrato). Sto cercando di scoprire cosa cercare, per assicurarmi che il codice supporti mock dinamici, interceptor, classi decorate (ad esempio LoggingDog) nello stesso modo in cui gestirà la classe originale.

Finora, dopo aver esaminato le risposte e i collegamenti, posso vedere che l'unica trappola sarebbe utilizzare direttamente il tipo di classe, ovvero utilizzare il metodo GetType() direttamente o tramite un'altra tecnologia. Nonostante alcuni commenti qui gli operatori is e as, e anche il casting per il tipo di base non interromperà LSP in questo caso, in quanto le sottoclassi valuteranno allo stesso modo della classe originale.

+3

io non vedere ciò che C++ ha a che fare con tutto ciò, si avrebbe lo stesso problema con il codice C++ (anche se, ovviamente, che ci si stia utilizzando typeof per entrambi come C++ non ha un tipo di base dell'oggetto). Può anche darsi che il codice in realtà non voglia fare qualcosa se si tratta di una versione derivata del cane. Questo potrebbe essere intenzionale. –

+0

Ho citato C++, come ho immaginato che questa fosse un'abitudine di RTTI in C++. Sono abbastanza sicuro che in questo caso non è stato intenzionale, anche se vedo il tuo punto. –

+0

Ho aggiornato la domanda, mi dispiace, forse non ero troppo chiaro. Volevo solo sapere come identificare il codice cliente che forse rompe LSP –

risposta

4

Se ignoriamo per il momento che ci sono infatti i casi in cui può essere necessario lanciare a un tipo specifico, nella maggior parte dei casi il problema può essere risolto modificando il progetto:

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

Utilizzando questo disegno , evitiamo la necessità di lanciare nel codice di elaborazione gli animali:

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

hai ragione. Nel mio caso, tuttavia, questo codice corrisponde a un pezzo di codice che avvia una procedura guidata appropriata nel livello dell'interfaccia utente in base al tipo del tipo di oggetto dominio. Non posso davvero cambiarlo, e anche questo codice non apparterrebbe all'oggetto Dominio in quanto è una logica dell'interfaccia utente. –

+0

Chiaramente alcuni lo fanno, in quanto dipende dall'oggetto dominio. Potresti semplicemente passare un 'IWizardLauncher' al metodo' Process' e far richiamare l'oggetto dominio quando ha fatto la sua parte. Tutto quello che sto dicendo è che ci sono soluzioni adeguate che non infrangono la tua architettura e che non richiedono ancora il casting. –

Problemi correlati