Sto violando LSP se la condizione può essere verificata?

1

Questa classe base per alcune forme che ho nel mio gioco assomiglia a questo. Alcune delle forme possono essere ridimensionate, alcune non possono.

   private Shape shape;

    public virtual void SetSizeOfShape(int x, int y)
    {
        if (CanResize()){
            shape.Width = x;
            shape.Height = y;
        }
        else {
            throw new Exception("You cannot resize this shape");
        }

    }
    public virtual bool CanResize()
    {
        return true;
    }

In una sottoclasse di una forma che non desidero mai ridimensionare, sto sovrascrivendo il metodo CanResize() in modo che un pezzo di codice client possa controllare prima di chiamare il metodo SetSizeOfShape() .

    public override bool CanResize()
    {
        return false;
    }

Ecco come potrebbe apparire nel mio codice cliente:

    public void DoSomething(Shape s)
    {
        if(s.CanResize()){
            s.SetSizeOfShape(50, 70);
        }
    }

Questo LSP sta violando?

    
posta William 22.08.2014 - 18:42
fonte

3 risposte

3

In poche parole, il Principio di sostituzione di Liskov afferma che, dato un numero di oggetti conformi ad una particolare interfaccia, dovresti essere in grado di scambiarli senza causare errori nel tuo programma.

Se puoi farlo, allora le tue classi non violano LSP.

L' esempio fornito nell'articolo LSP su Wikipedia è abbastanza illuminante. Dice:

A typical example that violates LSP is a Square class that derives from a Rectangle class, assuming getter and setter methods exist for both width and height. The Square class always assumes that the width is equal with the height. If a Square object is used in a context where a Rectangle is expected, unexpected behavior may occur because the dimensions of a Square cannot (or rather should not) be modified independently.

This problem cannot be easily fixed: if we can modify the setter methods in the Square class so that they preserve the Square invariant (i.e., keep the dimensions equal), then these methods will weaken (violate) the postconditions for the Rectangle setters, which state that dimensions can be modified independently.

Violations of LSP, like this one, may or may not be a problem in practice, depending on the postconditions or invariants that are actually expected by the code that uses classes violating LSP. Mutability is a key issue here. If Square and Rectangle had only getter methods (i.e., they were immutable objects), then no violation of LSP could occur.

    
risposta data 22.08.2014 - 18:53
fonte
3

No, è ancora sostituibile, perché hai la stessa pre-condizione di richiedere CanResize per essere vero per ogni classe. Il problema è che non stai permettendo al sistema di tipi di aiutarti a far rispettare questa condizione in fase di compilazione, e stai costringendo le forme non resecabili a preoccuparti del ridimensionamento delle API, che è una violazione della segregazione dell'interfaccia.

La creazione di un'interfaccia separata per le forme ridimensionabili sembra essere più utile ora, ma renderà più semplice lavorare a lungo termine, perché ti consente di eliminare il codice if (CanResize()) ... else ... in tutta la posizione.

    
risposta data 22.08.2014 - 19:29
fonte
0

Penso che includendo "canResize" non si stia violando LSP perché il codice client funziona con implementazioni concrete che possono essere ridimensionate e con implementazioni che non possono essere ridimensionate. Puoi cambiare diverse implementazioni senza problemi.

Un'altra opzione per implementare qualcosa del genere è restituire o meno un oggetto per eseguire il ridimensionamento usando forse la monade:

shape.getResizor().IfNotNull(resizor => ...)

È meglio documentare la tua API e dire "ricorda prima la chiamata a canResize".

    
risposta data 22.08.2014 - 20:07
fonte

Leggi altre domande sui tag