Pora na kolejny – trzeci już – artykuł o zasadach SOLID. Pod trzecią literą tego skrótu kryje się Liskov Substitution Principle (LSP), czyli Zasada podstawienia Liskov. Reguła ta została po raz pierwszy sformułowana przez Barbarę Liskov, amerykańską programistkę, a brzmi ona:
Niech ɸ(𝑥) (funkcja obiektu x) będzie istniejącą i dowiedzioną właściwością obiektu x typu T. Wtedy również ɸ(𝒚) dla obiektu y typu S jest prawdą, jeżeli S jest podtypem T.
Barbara Liskov
Jest również prostsza i myślę, że o wiele bardziej przystępna definicja:
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.
Robert C. 'Uncle Bob’ Martin
O co tutaj chodzi? Jeżeli mam jakąś klasę bazową, to w jej miejsce mogę podstawić dowolną klasę podrzędną, przy czym taka podmiana nie powinna mieć wpływu na poprawność działania programu. Innymi słowy, aplikacja powinna działać prawidłowo zarówno po zastosowaniu referencji do klasy nadrzędnej, jak i takiej, która po niej dziedziczy.
LSP — po co i dlaczego?
Jeśli coś wygląda jak kaczka, kwacze jak kaczka, ale potrzebuje baterii, to prawdopodobnie masz złą abstrakcję
Znalezione gdzieś w sieci
Podstawą LSP jest prawidłowo zaprojektowane dziedziczenie. Gdy w miejsce klasy bazowej nie możemy podstawić danej podklasy (bo wtedy np. program mógłby działać w inny niż zamierzony sposób lub wystąpiłby błąd), to mechanizm dziedziczenia został zaprojektowany nieprawidłowo.
Dziedziczenie wg LSP powinno przebiegać na takiej zasadzie, że klasy podrzędne rozszerzają możliwości klas bazowych bez nadpisywania ich już istniejących zachowań. Stąd też zasada podstawienia Liskov jest powiązana z zasadą otwarte/zamknięte (Open/Closed Principle – OCP), o której możecie przeczytać w poprzednim artykule.
Gdy to podejście zastosujemy w swoich projektach, możemy zmniejszyć ryzyko wystąpienia niepożądanego działania programu.
LSP na przykładzie
Utworzyłem klasę Cat, czyli kot. Kot, jak wiadomo może zamiauczeć i zamruczeć – zarówno domowy kot, jak i wielki, dziko żyjący drapieżnik, ale czy aby na pewno każdy? Np. taki tygrys czy lew już miauczeć ani mruczeć nie potrafi. Może co najwyżej zaryczeć albo zawyć.
public abstract class Cat {
public void meow() {
System.out.println("Meow!");
}
public void move() {
System.out.println("Cat is moving");
}
}
public class DomesticCat extends Cat {
//miejsce na reszę kodu...
}
public class Puma extends Cat {
//miejsce na resztę kodu...
}
public class Tiger extends Cat {
@Override
public void meow() {
throw new RuntimeException("Tigers don't meow!");
}
}
Z racji, iż tygrysy nie potrafią miauczeć, program nie zadziała prawidłowo. Mało tego, nawet wyskoczy błąd z komunikatem, że ten gatunek kota nie miauczy.
Z pomocą przychodzi LSP.
public abstract class Cat {
public void move() {
System.out.println("Cat is moving");
}
}
public class MeowingCat extends Cat {
public void meow() {
System.out.println("Meow!");
}
}
public class YowlingCat extends Cat {
public void yowl() {
System.out.println("Yowl!");
}
}
public class DomesticCat extends MeowingCat {
//miejsce na reszę kodu...
}
public class Puma extends MeowingCat {
//miejsce na resztę kodu...
}
public class Tiger extends YowlingCat {
//miejsce na resztę kodu...
}
Utworzyłem dwie dodatkowe klasy bezpośrednio dziedziczące po klasie Cat — MeowingCat i YowlingCat, po których odpowiednio dziedziczą koty miauczące i wyjące. Teraz każdy kot może wydawać odpowiedni dla siebie dźwięk 🙂.
Podsumowanie
Dzięki zastosowaniu LSP możemy zwiększyć pewność, że nasz program rzeczywiście zadziała, tak jak ma działać. Trzeba jednak mieć na uwadze fakt, że wdrażając tę zasadę, nie powinniśmy stosować żadnych bloków warunkowych, aby umożliwić prawidłowe działanie programu. Ponadto zasada ta bywa nieraz mylona przez niektórych programistów z OCP i ISP (Interface Segregation Principle — Zasada segregacji interfejsów). Przede wszystkim trzeba tutaj zapamiętać, że powinniśmy móc podstawić dowolny obiekt podklasy w miejsce obiektu klasy bazowej bez pytania, jakiej klasy jest to obiekt.
4 komentarze
RSS News · 2 marca 2024 o 17:32
Skoro twoje treści są często bogate w wiedzę, ciekawi mnie, czy korzystasz z nietypowych źródeł informacji, które mogą być pominięte przez innych, a jednocześnie przynoszą wartość dodaną do treści?
admin · 4 marca 2024 o 18:40
Nietypowe źródła, czyli jakie na przykład? Pisząc artykuły n.t. dobrych praktyk w programowaniu korzystałem głównie z artykułów innych programistów i odpowiedniej literatury, rekomendowanej przez doświadczonych specjalistów z branży IT, więc raczej żadnego ze źródeł nie przypisałbym do „nietypowych”
Zdrowie · 16 kwietnia 2024 o 12:53
Ten blog jest po prostu niesamowity – jego różnorodność, głębia treści i profesjonalne podejście do każdego tematu sprawiają, że nie tylko uczę się nowych rzeczy, ale także czuję się częścią społeczności, która stara się wspólnie rozwijać i dzielić się wiedzą oraz doświadczeniem, co jest dla mnie niezwykle inspirujące i motywujące!
Jerzy Biedroński · 29 maja 2024 o 18:35
Treść jest klarowna, dobrze zorganizowana i pełna przydatnych wskazówek. Autor doskonale wyjaśnia kluczowe zagadnienia. Może warto by dodać więcej przykładów praktycznych. Pomimo tego, tekst jest niezwykle edukacyjny i inspirujący.