Zasada podstawienia Liskov () – zasada projektowania w programowaniu obiektowym 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. 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ć kompatybilne rezultaty.
Zasada ta jest jednym z principiów SOLID i oznacza że klasa dziecka (dziedzicząca) powinna w logiczny i kompatybilny sposób implementować wszystkie metody klasy/interfejsu rodzica (błędem jest np. implementacja tylko części metod interfejsu i rzucanie wyjątków w pozostałych metodach).
Zasada została sformułowana po raz pierwszy przez Barbarę Liskov w książce „Data Abstraction and Hierarchy", a spopularyzowana i podana w obecnym brzmieniu przez Roberta C. Martina w artykule „Principles of Object Oriented Design" oraz książce „Agile Software Development: Principles, Patterns, and Practices". Przykład naruszenia zasady
Załóżmy, że mamy klasę abstrakcyjną (lub interfejs) MediaAbstract zawierającą m.in. metody getTitle() oraz toPdf(). Załóżmy, że chcemy dziedziczyć tę klasę w klasach Article oraz Movie. Klasa Article może prawidłowo zaimplementować te metody, ale klasa Movie nie „ma jak" wygenerować pdf więc w implementacji metody toPdf() rzuca wyjątek „Z filmu nie da się wygenerować pdf".
Jest to naruszenie zasady podstawienia Liskov, ponieważ zewnętrzny kod, który operuje na obiektach MediaAbstract, w trakcie normalnej pracy na nich, nagle zatrzyma się i wyrzuci wyjątek, gdy natrafi na obiekt typu Movie. Naprawa może powodować konieczność obsłużenia wyjątku w zewnętrznym kodzie, a więc wymusi jego zmianę (od czego ma chronić właśnie stosowanie zasady Liskov).
Jednym z możliwych rozwiązań tego problemu jest np. wydzielenie z klasy/interfejsu MediaAbstract metody toPdf() do osobnej klasy/interfejsu PdfExportable. Wówczas klasa Article będzie implementowała MediaAbstract oraz PdfExportable, natomiast klasa Movie tylko MediaAbstract.
Przykład drugi, naruszenia zasady Załóżmy, że dana jest hierarchia dziedziczenia jak na diagramie poniżej.
thumb|center|400px
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 obiekcie klasy 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 oraz rozwijanie.
Zobacz też SOLID
Przypisy
Linki zewnętrzne Dyskusja na temat zasady Inny sposób wytłumaczenia
Kategoria:Programowanie obiektowe Kategoria:Praktyki programistyczne