Kwadrat jest prostokątem czyli Liskov Substitution Principle (LSP)

Od młodego uczą nas, że każdy kwadrat jest prostokątem. Później uczymy się programować i zaczyna się tragedia. Matematycznie kwadrat jest specyficznym  przypadkiem prostokąta programistycznie już nie bardzo. Metoda ustawiająca szerokość wywołana dla prostokąta powinno ustawić jego szerokość a w przypadku kwadratu? W przypadku kwadratu oczekujemy, że ustawienie szerokości ustawi również wysokość. Zobaczmy zatem taki kod:

Prostokąt:

[csharp]
public class Rectangle
{
internal double _width;
internal double _height;

public double Width
{
get { return _width; }
set { _width = value; }
}

public double Height
{
get { return _height; }
set { _height = value; }
}
}

[/csharp]

i kwadrat dziedziczący z prostokąta (wszak każdy kwadrat jest prostokątem):

[csharp]
public class Square : Rectangle
{
public new double Width
{
get { return _width; }
set
{
_width = value;
_height = value;
}
}
}
[/csharp]

Za pomocą operatora new nadpisaliśmy property Width tak aby pasowała do naszej teorii. Wszystko ładnie i pięknie – należało by jeszcze tak samo potraktować Height i już jest pięknie….

Barbara Liskov mówi, że nie. Powyższy kod jest książkowym przykładem na złamanie zasady podstawiania – Liskov Substitution Principle (LSP). Zasada ta mówi, że funkcje, które używają referencji do klas bazowych, muszą być wstanie użyć również klas dziedziczących z klasy bazowej bez dokładnej znajomości tych obiektów. Po ludzku oznacza to tyle, że funkcja powinna działać przewidywalnie bez względu na to czy jako parametr przekażemy klasę bazową czy też klasę, która po niej dziedziczy.

Dlaczego to jest ważne? Co zrobi poniższy kod?

[csharp]

Rectangle rectangle = new Square();

rectangle.Width = 10;
[/csharp]

Ustawiając szerokość spodziewałbym się, że wysokość również będzie 10 (w końcu mamy new Square()) ale nie…

Złamanie zasady LSP powoduje, że pośród innych powodujemy sporo trudnych do znalezienia błędów. Jeżeli chcemy pracować szybko i wygodnie to niestety lepiej nie łamać tej zasady a kwadraty i prostokąty nie powinny dziedziczyć z siebie a z bardziej ogólnego typu np.: kształt.