Questa implementazione di Decorator viola il Principio di sostituzione di Liskov?

4

Considerare la seguente implementazione del modello di progettazione Decorator:

WordBank memorizza le stringhe e le restituisce al client tramite il metodo getWords() .

La classe decoratore, WordSorter , è una sottoclasse di WordBank (come nel modello Decoratore). Tuttavia, l'implementazione di getWords() ordina alcune stringhe e le rimuove, prima di restituire l'array al client.

Ad esempio un WordSorter potrebbe eliminare tutte le stringhe nel banco che iniziano con la lettera 'a', e solo dopo di ciò restituire l'array al client.

Questo viola il Principio di sostituzione di Liskov ? Poiché alcune implementazioni di WordBank restituiscono le stringhe mentre altre prima ordinano le stringhe e ne restituiscono solo alcune, non sono sicuro che sia sicuro dire che un WordSorter può essere utilizzato ovunque sia utilizzato qualsiasi altro WordBank . O sto capendo che questo principio è sbagliato?

    
posta Aviv Cohn 23.05.2014 - 23:19
fonte

3 risposte

11

A WordSorter è-a WordBank , quindi il codice che funziona con WordBank dovrebbe funzionare anche quando viene utilizzato un WordSorter invece di WorkBank . D'altra parte, un WordSorter è-non-un SomeWordBank . Il compilatore non ti consente nemmeno di utilizzare WordSorter al posto di SomeWordBank , quindi il problema non inizia nemmeno a verificarsi.

Ci potrebbe essere una violazione LSP, ma non sembra provenire dalle specifiche minime che hai fornito. WordSorter garantisce, ad esempio, che si possano aggiungere stringhe arbitrarie e recuperarle tutte nello stesso ordine in un secondo momento? Quindi l'ordinamento delle parole interromperà effettivamente il contratto e il codice che funziona per "corretto" WordBank s può essere interrotto sostituendo un WordSorter . Se c'è una tale garanzia è impossibile da capire dal diagramma UML minimo che hai mostrato. Se, ad esempio, il contratto di WordBank dice che tutte le parole che sono state aggiunte sono incluse nel risultato di getWords , quindi:

bank.add(w);
AssertContains(bank.getWords(), w);

dovrebbe sempre funzionare. Quel codice si interromperebbe se bank era un WordSorter , ed è colpa di WordSorter per aver violato il contratto e quindi violare l'LSP. Ma se WordBank non offre tale garanzia, allora il codice sopra riportato è sbagliato (nello stesso modo asser x*y == 42 di solito fallirà) e WordSorter è una sottoclasse perfettamente ben gestita.

    
risposta data 23.05.2014 - 23:39
fonte
1

Dalla descrizione generale fornita, sembra che i tipi concreti WordBank e WordSorter (potrebbe essere meglio chiamato WordFilter ) debbano essere tipi separati sia implementati da un'interfaccia comune IWordSource , sia / o erediti da una comune classe di base astratta WordSourceBase che lo fa, piuttosto che avere un tipo concreto ereditato dall'altro (mettendo da parte la questione di cosa dovrebbe significare quando un metodo restituisce un array). Il codice che vuole essere in grado di memorizzare le cose in un word holder e sapere che gli articoli verranno restituiti nell'ordine fornito dovrebbe richiedere un WordBank . Codice che vuole qualcosa che possa fornire una lista di parole, ma non importa come viene prodotto tale elenco dovrebbe richiedere un IWordSource . Il codice che vuole qualcosa che può essere dato al codice che si aspetta un IWordSource ma filtra i dati forniti per primo dovrebbe creare un WordSorter / WordFilter .

Anche se non mi spingerei così lontano da suggerire che le classi istanziabili dovrebbero sempre essere sigillate, suggerirei che molte situazioni in cui si potrebbe considerare l'ereditarietà da una classe istanziabile sarebbero meglio gestite avendo la classe originale istanziabile ereditata da un abstract base e il tipo derivativo proposto eredita anche dalla stessa base. Il codice che riguarda i dettagli precisi di come funziona la classe istanziabile originale (che potrebbe essere diversa nel tipo derivato) può richiedere un riferimento di quel tipo, mentre il codice che è interessato solo ai comportamenti astratti può accettare un riferimento di quel tipo di base (che potrebbe quindi identificare il tipo originale o una derivata).

    
risposta data 17.11.2014 - 20:59
fonte
-2

Sì. Liskov è arrabbiato. Ma possiamo sopravvivere. L'impatto maggiore non si farà sentire fino a quando non verranno costruiti altri due livelli di complessità su questi modelli. E anche allora sarà misurato nel tempo e nell'efficienza persi. Si sentirà nello spazio negativo di ciò che possiamo fare con le nostre menti date un tempo limitato su questa terra. In altre parole, nessuno ti chiamerà su di esso .....

    
risposta data 17.01.2017 - 22:31
fonte