Liskov substitution principle (tsz. Liskov substitution principles)
Az elv a következő formális megfogalmazásban hangzik el:
„Ha S típus származik T típusból, akkor minden olyan helyen, ahol T típust használnánk, S típust is használhatunk anélkül, hogy a program helyes működését befolyásolná.”
Másképpen mondva: Ha egy kód egy bázisosztály (T) példányával helyesen működik, akkor az leszármazott osztályok (S) példányaival is helyesen kell működnie — anélkül, hogy a kódot módosítani kellene.
Az OOP egyik fő előnye a polimorfizmus: készíthetünk olyan kódot, amely bázisosztályokra íródik, de a konkrét példányok lehetnek leszármazott osztályok példányai is.
Az LSP azt mondja ki, hogy ennek a polimorfizmusnak „átlátszóan” kell működnie: ha lecserélek egy T
-t egy S
-re, a program viselkedése nem válhat hibássá vagy értelmét vesztetté.
class Bird {
public:
virtual void fly() {
std::cout << "Flying..." << std::endl;
}
};
class Sparrow : public Bird {
public:
void fly() override {
std::cout << "Sparrow flying..." << std::endl;
}
};
Itt Bird osztályt használhatok bármilyen Bird-ként viselkedő objektummal. Ha a függvény Bird*
-ot vár, és kap egy Sparrow*
-t, helyesen fog működni.
void makeBirdFly(Bird* b) {
b->fly();
}
LSP nem sérül.
class Bird {
public:
virtual void fly() {
std::cout << "Flying..." << std::endl;
}
};
class Penguin : public Bird {
public:
void fly() override {
throw std::runtime_error("Penguins can't fly!");
}
};
Ha most egy makeBirdFly()
függvény Bird*
-ot vár, és kap egy Penguin*
-t:
Bird* bird = new Penguin();
makeBirdFly(bird); // program kivételt dob
Ez már sérti az LSP-t: a Penguin
nem viselkedik helyettesítőként a Bird
helyett, mivel nem tud “repülni”.
👉 Ilyenkor a tervezés hibás — talán nem is kéne a Penguin
-t Bird
-ből származtatni, vagy az fly()
metódus nem minden Bird
számára értelmes.
A leszármazott típus viselkedése nem sértheti a bázistípus által elvárt invariánsokat.
Konkrétan:
Az LSP a híres SOLID elvsorozat L betűje:
A SOLID elvek célja: robosztus, jól tervezett, könnyen bővíthető OOP rendszerek készítése. Az LSP biztosítja, hogy a polimorfizmus megbízható legyen.
Liskov substitution principle (LSP) |
---|
Minden leszármazott osztály helyettesíthető legyen a bázisosztályával. |
Ne sértsük meg az elvárt szerződést. |
Prekondíciókat ne erősítsünk. |
Posztkondíciókat ne gyengítsünk. |
Invariánsokat tartsuk meg. |
Ha LSP sérül, a kód törékeny, nehezen karbantartható lesz. |