Il "trucco" del classico problema di Square-Rectangle LSP è che se definiamo un rettangolo come un oggetto che deve soddisfare il contratto ...
rectangle.setWidth(40);
rectangle.setHeight(50);
assert(rectangle.getWidth() == 40);
... quindi estendilo a un quadrato, quindi violiamo quel contratto. Questo perché stiamo mescolando una definizione personalizzata di un rettangolo e una definizione classica. In quanto tale, stiamo cercando di far rientrare la definizione classica in una definizione personalizzata, che non funziona. In sostanza, è un trucco da salotto inteso a far pensare a comportamenti di sottotipo e obblighi contrattuali. Potremmo facilmente mostrare che la suddetta affermazione di un oggetto Rectangle fallisce anche per un rettangolo con qualche riscrittura astuta:
rectangle.setTopEdgeLength(40);
rectangle.setBottomEdgeLength(50);
assert(rectangle.getTopEdgeLength() == 40);
Ad ogni modo, abbastanza enigmi, diamo un'occhiata a ...
Definizione classica
Mettere correttamente, quadrati e rettangoli sono entrambi tipi di quadrilateri, un quadrilatero essendo un poligono con 4 spigoli e 4 vertici. Classicamente, un quadrato è un rettangolo, ma diamo un'occhiata alle definizioni di Wikipedia di un rettangolo e un quadrato:
-
Rettangolo : un quadrilatero con quattro angoli retti
-
Square : un quadrilatero con quattro angoli retti e quattro lati uguali
Notare qualcosa lì? Gli unici punti in comune tra un rettangolo e un quadrato sono che entrambi sono quadrilateri, ed entrambi hanno quattro angoli retti. Nessuna menzione riguarda la lunghezza dei lati, poiché questi sono derivati dal contratto, non parte del contratto. Possiamo dimostrarlo con alcune semplici affermazioni logiche:
- se una forma è un quadrilatero e la forma ha quattro angoli retti, i lati opposti avranno la stessa lunghezza
- se una forma è quadrilatera e la forma ha quattro angoli retti e quattro lati uguali, la lunghezza di ogni lato sarà uguale alla lunghezza di qualsiasi altro lato (questa è in realtà una tautologia)
Se vogliamo mantenere la definizione classica di rettangoli e quadrati e rappresentarli in codice con i metodi setWidth()
e setHeight()
in posizione, il contratto deve essere:
square.setWidth(40);
square.setHeight(50);
assert(square.isQuadrilateral());
assert(square.hasFourRightAngles());
Se la larghezza è ancora quella che impostiamo nella riga 1 è irrilevante, poiché tale regola non fa parte degli obblighi contrattuali dell'oggetto principale Rectangle.
Per riassumere, il problema iniziale è una delle definizioni contrattuali. Non sto dicendo che non dovresti avere un oggetto Rectangle che soddisfi rectangle.setWidth(40); rectangle.setHeight(50); assert(rectangle.getWidth() == 40);
, ma se definisci il tuo oggetto Rectangle in quel modo, quando lo estendi devi assicurarti che il contratto sia ancora valido, altrimenti violerai LSP .