Zasada podstawienia Liskov

Z Wikipedii, wolnej encyklopedii
To jest stara wersja tej strony, edytowana przez Paweł Ziemian BOT (dyskusja | edycje) o 22:56, 17 kwi 2020. Może się ona znacząco różnić od aktualnej wersji.

Zasada podstawienia Liskov (ang. Liskov substitution principle) – zasada mówiąca, że:

Funkcje które używają wskaźników lub referencji do klas bazowych, muszą być w stanie używać również obiektów klas dziedziczących po klasach bazowych, bez dokładnej znajomości tych obiektów[1].
Inaczej mówiąc, klasa dziedzicząca powinna tylko rozszerzać możliwości klasy bazowej i w pewnym sensie nie zmieniać tego, co ona robiła już wcześniej. Mówiąc jeszcze inaczej — jeśli będziemy tworzyć egzemplarz klasy potomnej, to niezależnie od tego, co znajdzie się we wskaźniku na zmienną, wywoływanie metody, którą pierwotnie zdefiniowano w klasie bazowej, powinno dać te same rezultaty.

Zasada została sformułowana po raz pierwszy przez Barbarę Liskov w książce Data Abstraction and Hierarchy[2], a spopularyzowana i podana w obecnym brzmieniu przez Roberta C. Martina w artykule "Principles of Object Oriented Design"[3] oraz książce "Agile Software Development: Principles, Patterns, and Practices"

Przykład naruszenia zasady

Załóżmy, że dana jest hierarchia dziedziczenia jak na diagramie poniżej.

Typowym przykładem naruszenia zasady LSP byłoby zastosowanie C++ Run-Time Type Information (RTTI) w celu rozpoznania typu obiektu, a następnie wywołanie funkcji, która wykonuje operację na danym obiekcie:

void przetwarzajFigurę(Figura& iFigura)
{
  if(typeid(iFigura) == typeid(Prostokąt))
    przetwarzajProstokąt(static_cast<Prostokąt&>(iFigura));

  else if(typeid(iFigura) == typeid(Okrąg))
    przetwarzajOkrąg(static_cast<Okrąg&>(iFigura));
  
  else if(typeid(iFigura) == typeid(Kwadrat))
    przetwarzajKwadrat(static_cast<Kwadrat&>(iFigura));
}

Funkcja przetwarzajFigurę() wprowadza dodatkowe zależności w kodzie, ponieważ musi ona znać wszystkie klasy dziedziczące po klasie Figura. Oznacza to, że każde utworzenie nowej klasy dziedziczącej po klasie Figura, będzie prawdopodobnie wiązało się ze zmianą tej funkcji.

Jeśli spojrzymy na klasy Prostokąt oraz Kwadrat, można zauważyć, że klasa Kwadrat dziedziczy po klasie Prostokąt metody do ustawiania/pobierania wysokości oraz szerokości. W przypadku klasy Prostokąt obecność tych metod jest naturalna (każdy prostokąt może mieć wysokość różną od szerokości). Nie jest tak w przypadku klasy kwadrat, ponieważ zmiana jego szerokości wiąże się ze zmianą wysokości. Oznacza to, że wywołanie metody ustawSzerokość na klasie Kwadrat spowodowałoby zmianę wartości zwracanej przez metodę pobierzWysokosc. W takim przypadku, oprogramowanie musiałoby sprawdzać typ obiektu który zmienia, żeby wiedzieć w jaki sposób obiekt będzie się zachowywał po wprowadzeniu zmian. Taki styl programowania wprowadza dodatkowe zależności w kodzie i utrudnia jego późniejsze utrzymanie i rozwijanie.

Zobacz też

Przypisy

  1. Robert C. Martin: The Liskov Substitution Principle. [dostęp 2010-02-20]. [zarchiwizowane z tego adresu (2015-11-28)]. (ang.).
  2. Barbara Liskov: Data Abstraction and Hierarchy. SIGPLAN Notices, 1988.
  3. Robert C. Martin: The Principles of Object Oriented Design. [dostęp 2010-02-20]. (ang.).

Linki zewnętrzne