Il metodo che sovrascrive è sempre una violazione del principio di sostituzione di Liskov? [duplicare]

12

Sovrascrivere un metodo originariamente definito nella super classe, per definizione significa che questo metodo farà cose diverse quando viene invocato su un oggetto della classe base o su un oggetto della sottoclasse.

Quindi questo significa che i metodi di override always significano violare LSP? O ci sono casi in cui non succede?

    
posta Aviv Cohn 12.06.2014 - 12:36
fonte

5 risposte

25

LSP vieta di violare i contratti di un supertipo in un sottotipo, non vieta di cambiare il comportamento di metodo (entro i limiti di tale contratto).

Ad esempio, supponiamo di avere una superclasse Report , associata a un certo oggetto Foo , con un metodo ToString() , e sottotipi HTMLReport , XMLReport e TextReport . Supponiamo inoltre che Report non sia astratto e l'implementazione predefinita di ToString() è di restituire la stringa vuota. Ora definisci un contratto nel modo seguente:

  • ToString() deve consegnare una stringa con una rappresentazione testuale di Foo in un determinato formato di testo (e la stringa vuota se il formato non è stato definito fino ad ora).
  • ToString() non muta l'oggetto Report
  • ToString() non deve mai generare un'eccezione

Quindi le sottoclassi possono facilmente sovrascrivere il metodo ToString , ognuno dei quali implementa un comportamento diverso, ma tutti seguono perfettamente l'LSP.

D'altra parte, se il tuo contratto sarebbe

  • ToString() deve sempre restituire la stringa vuota

quindi sovrascrivendolo e restituisci qualcosa di diverso viola l'LSP - ma un simile contratto ovviamente non avrebbe molto senso per qualsiasi scenario del mondo reale.

    
risposta data 12.06.2014 - 13:20
fonte
1

Primo caso semplice: aggiungere il caching a un'operazione costosa. non c'è alcuna differenza funzionale tra l'originale e la nuova funzione.

Anche la funzione originale potrebbe essere documentata per fare qualcosa e fintanto che la funzione di override si adatta ancora a quella documentazione allora non c'è alcun problema (il nucleo del principio sostituzione ). Ricorda che dovresti codificare l'API non l'implementazione.

    
risposta data 12.06.2014 - 12:46
fonte
0

Per definizione cose diverse? Whaddayoumean?

Liskov dice che non dovresti "andare contro" il comportamento del tuo antenato. Ciò non significa che non puoi estenderlo o aumentarlo facendo cose in aggiunta a quel comportamento di base.

Nel metodo sottoposto a override chiamate inherited per eseguire il comportamento dell'antenato (onorando Liskov) e poi puoi fare altre cose oltre a quello.

Non chiamare affatto inherited (*) sarebbe una violazione di Liskov (a meno che non si ri-codifichi l'implementazione della base, cosa a cui sconsiglio vivamente). Aggiornamento @Ratchetfreak ha in realtà un esempio molto carino in cui un discendente chiamerebbe ereditato solo se non avesse già memorizzato nella cache un risultato per un calcolo altrimenti "costoso".

(*) si noti che sono abituato a una lingua in cui è possibile chiamare ereditato indipendentemente dal fatto che l'antenato o il metodo dell'antenato sia contrassegnato come astratto. Il compilatore prende la chiamata.

    
risposta data 12.06.2014 - 12:41
fonte
0

Il Principio di sostituzione di Liskov dice solo che le sottoclassi non dovrebbero violare le proprietà dimostrabili del supertipo. Le proprietà dimostrabili sono fondamentalmente le firme dei tipi dei membri. Quindi se un metodo su una superclasse viene dichiarato per restituire un numero intero, allora non dovrebbe essere sovrascritto in sottoclasse per restituire una stringa.

I sistemi di tipo nei moderni linguaggi OO tipicamente staticamente impediscono che ciò accada, quindi come un normale sviluppatore di codice C # non dovresti essere troppo preoccupato per rompere il LSP (anche se credo che ci siano dei punti deboli nel sistema dei tipi per quanto riguarda gli array che consente di rompere LSP).

Il principio è spesso inteso in senso più ampio (e vagamente) secondo cui le sottoclassi non dovrebbero "rompere le aspettative", che è sicuramente un buon principio di progettazione, ma non proprio quello che dice il LSP.

    
risposta data 29.06.2015 - 15:41
fonte
0

Ad esempio, se hai una classe con istanze che rappresentano un elemento dell'interfaccia utente, e quella classe ha un metodo "draw", allora la specifica del metodo "draw" non dirà esattamente cosa verrà disegnato. Dirà invece "l'elemento dell'interfaccia utente verrà disegnato in modo appropriato per informare l'utente su tutto lo stato dell'elemento dell'interfaccia utente che è importante per l'utente, in modo intuitivo e di bell'aspetto". Se si sottoclasse quell'elemento dell'interfaccia utente, il metodo di estrazione sottoclasse dovrebbe e probabilmente agirà esattamente in base alle specifiche.

Come principio generale, se il Principio di sostituzione di Liskov rendesse impossibile la sottoclasse, sarebbe un vero e proprio stupido, e i principi piuttosto stupidi non ricevono nomi e voci di Wikipedia e vengono rapidamente dimenticati: -)

(a proposito, è un'arte che troppo spesso dimentica di scrivere una specifica adeguata per un metodo che dovrebbe essere sovrascritto in una sottoclasse, perché qualsiasi comportamento esistente non descrive la specifica, solo un caso speciale di esso).

    
risposta data 29.06.2015 - 16:58
fonte

Leggi altre domande sui tag