Come verificare il principio di sostituzione di Liskov in una gerarchia ereditaria?

14

Ispirato alla risposta :

Liskov Substitution Principle requires that

  • Preconditions cannot be strengthened in a subtype.
  • Postconditions cannot be weakened in a subtype.
  • Invariants of the supertype must be preserved in a subtype.
  • History constraint (the "history rule"). Objects are regarded as being modifiable only through their methods (encapsulation). Since subtypes may introduce methods that are not present in the supertype, the introduction of these methods may allow state changes in the subtype that are not permissible in the supertype. The history constraint prohibits this.

Speravo che qualcuno pubblicasse una gerarchia di classi che violasse questi 4 punti e come risolverli di conseguenza.
Sto cercando una spiegazione elaborata a fini didattici su come identificare ciascuno dei 4 punti nella gerarchia e il modo migliore per risolverlo.

Nota:
Speravo di pubblicare un esempio di codice per le persone su cui lavorare, ma la domanda in sé riguarda come identificare le gerarchie difettose:)

    
posta Songo 17.10.2012 - 12:45
fonte

1 risposta

16

È molto più semplice di quanto la citazione lo renda sano, accurato così com'è.

Quando si osserva una gerarchia di ereditarietà, si immagini un metodo che riceve un oggetto della classe base. Ora chiediti, ci sono delle ipotesi che qualcuno che modifica questo metodo potrebbe rendere ciò che non sarebbe valido per quella classe.

Ad esempio ( originariamente visto sul sito di Uncle Bob ):

public class Square : Rectangle
{
    public Square(double width) : base(width, width)
    {
    }

    public override double Width
    {
        set
        {
            base.Width = value;
            base.Height = value;
        }
        get
        {
            return base.Width;
        }
    }

    public override double Height
    {
        set
        {
            base.Width = value;
            base.Height = value;
        }
        get
        {
            return base.Height;
        }
    }
}

Sembra abbastanza giusto, giusto? Ho creato un tipo di rettangolo specialistico chiamato Square, che sostiene che la larghezza deve essere uguale all'altezza in ogni momento. Un quadrato è un rettangolo, quindi si adatta ai principi OO, non è vero?

Ma aspetta, e se qualcuno ora scrive questo metodo:

public void Enlarge(Rectangle rect, double factor)
{
    rect.Width *= factor;
    rect.Height *= factor;
}

Non fantastico. Ma non c'è ragione per cui l'autore di questo metodo avrebbe dovuto sapere che potrebbe esserci un potenziale problema.

Ogni volta che diventi una classe da un'altra, pensa alla classe base e a ciò che le persone potrebbero assumere al riguardo (come "ha una larghezza e un'altezza e sarebbero entrambe indipendenti"). Quindi pensa "queste ipotesi rimangono valide nella mia sottoclasse?" In caso contrario, ripensa il tuo progetto.

    
risposta data 17.10.2012 - 13:03
fonte