Menu Zamknij

SOLID: Zasada podstawienia Liskov

Zasada podstawienia Liskov sformułowała przez Barbarę Liskov w roku 1988. Można ją przedstawić następująco:

zasada podstawienia Liskov

Funkcje, które używają wskaźników lub referencji do obiektów klas bazowych, muszą być w stanie używać również obiektów klas dziedziczących po klasach bazowych, bez dokładnej znajomości tych klas.

Inna definicja tej zasady przestawia ją w ten sposób:

Typ A jest podtypem typu B, jeśli wymaga nie więcej niż on i zapewnia nie mniej.

Niepisany kontrakt.

Ważnym pojęciem, przy omawianiu tej zasady jest kontrakt. Funkcjonalność używająca obiektu jakiejś klasy ma zawarty z nią swego rodzaju kontrakt. Oprócz spraw oczywistych, takich jak zestaw publicznych metod, opis argumentów itp. Istnieje, też niejawny kontrakt. Na ten niejawny kontrakt, chce nam zwrócić uwagę, zasada podstawienia Liskov.

Przystosowanie do środowiska.

Dla identycznych warunków systemu obiekt klasy dziedziczącej powinien, funkcjonować tak samo poprawnie, jak obiekt klasy bazowej. Z tego wynika, że wymagania wobec stanu systemu, pozwalające na poprawne funkcjonowanie takiego obiektu nie mogą być większe niż, wymagania wobec obiektu klasy bazowej. Po prostu nie może wymagać od otoczenia „lepszego traktowania” niż obiekt podstawowego typu.

Zasada podstawienia Liskov

Dziedziczymy również zobowiązania.

Zobowiązania, jakie dla systemu realizuje obiekt klasy dziedziczącej, nie mogą być węższe niż w przypadku obiektu klasy bazowej. Obiekt powinien, móc realizować, wszystkie te zadania jakie, otoczenie stawiało wobec obiektów jego klasy bazowej.

Czego nie wolno robić?

Dziedziczyć i realizować mniej funkcjonalności niż obiekt klasy bazowej.
Nadpisywać lub implementować jakąś metodę, aby ją zablokować przez rzucanie wyjątku.
Wyrzucać wyjątków, których klienci nie spodziewaliby się po klasie bazowej. Mogą to być wyjątki, które dziedziczą ze swoich poprzedników w klasie bazowej.
Zmieniać intencję. Metoda, która w klasie bazowej pobierała informacje, nie może teraz zmieniać stan obiektu.
Fabrykować wyniki. Wyniki nie mogą sugerować, klientom poprawności zmian w stanie systemu, podczas gdy, metoda jedynie fabrykuje fałszywą odpowiedź.

Niezmienniki muszą być zachowane.

Obiekty klas dziedziczących nie mogą zaskakiwać swoich klientów, niespodziewanym zachowaniem. Klienci, spodziewają się pewnych zasad działania, które się nie zmieniają, bez względu implementację.

Funkcjonalności obiektu muszą realizować cel, w jakim używają go jego klienci, a to ten sam cel, który realizuje klasa bazowa.

Dziedziczenie to nie tylko exteneds i @Override

Dziedziczenie nie sprowadza się tylko do implementacji tych samych metod, nie może się objawiać tylko zewnętrznymi cechami, rozszerzana klasa musi również zawierać pewną wewnętrzną spójność podobną do klasy bazowej. Obiekty takiej klasy powinny podobnie reagować na sygnały z otoczenia. Jeżeli, jest inaczej, to całkiem prawdopodobne, że dziedziczenie w tym przypadku, będzie sztuczne.

Polimorfizm

Tam, gdzie dziedziczenie, jest robione dla polimorfizmu podtypów, przestrzeganie zasady podstawienia Liskov jest bardzo ważne. Jeżeli jeden z typów złamie kontrakt, może to sporo namieszać, może rozpocząć się „hardcoding” przy pomocy if oraz instanceof.

Jeżeli nie potrzebujemy dziedziczyć ze względu na polimorfizm, to lepiej zastosować jakiegoś rodzaju kompozycje.

Polimorfizm nie pozostawia miejsca dla obiektów „specjalnej troski”. Jeżeli jakiś obiekt nie potrafi się dostosować do kontraktu to, coś jest z nim nie tak. W takim przypadku będzie to fałszywe dziedziczenie, czyli obiekt nie był tworzony z zamysłem realizacji tych samych celów co obiekt klasy bazowej, lub jego zasady funkcjonowania, różnią się z intencją klientów.

Czy warto stosować tą zasadę?

Sama świadomość problemu może ustrzec nas przed popełnieniem błędów, które mogą zaburzyć skuteczne używanie polimorfizmu. Zastanowimy się, czy dziedziczenie dla danego przypadku będzie miało sens, czy jednak jest zbędne i prowadzi do nieporozumień. Nie będziemy denerwować kolegów z pracy wyjątki typu NotImplementedException, a logiki w kodzie nie będą zaburzać nam, takie śmieci jak instanceof.

Please follow and like us:
Skuteczna refaktoryzacja w 10 krokach!

Odbierz Darmowy Poradnik o Refaktoryzacji!

Poznaj kilka prostych technik i wprowadź nową jakość w swoim projekcie.

Dzięki za dołączenie do mojej listy.

Coś poszło nie tak :( Spróbuj jeszcze raz.