Подстановочный принцип Барборы Лисков
Введение
Принцип подстановки Барбары Лисков (Liskov Substitution Principle, LSP) является одним из пяти принципов SOLID в объектно-ориентированном программировании. Он был сформулирован Барбарой Лисков в 1987 году и гласит, что объекты подклассов должны быть взаимозаменяемы с объектами их базовых классов.
Основные концепции
Определение LSP
Принцип подстановки Лисков гласит:
“Функции, использующие указатели или ссылки на базовые классы, должны иметь возможность использовать объекты производных классов, не зная об этом.”
Важность LSP
LSP обеспечивает гибкость и повторное использование кода. Соблюдение этого принципа позволяет создавать более устойчивые и поддерживаемые системы.
Примеры
Пример 1: Корректное использование LSP
Базовый класс
class Rectangle {public: virtual void setWidth(double width) { this->width = width; }
virtual void setHeight(double height) { this->height = height; }
virtual double getWidth() const { return width; }
virtual double getHeight() const { return height; }
virtual double area() const { return width * height; }
protected: double width; double height;};
Производный класс
class Square : public Rectangle {public: void setWidth(double side) override { this->width = side; this->height = side; }
void setHeight(double side) override { this->width = side; this->height = side; }};
Пример использования
void process(Rectangle& rect) { rect.setWidth(5); rect.setHeight(10); std::cout << "Area: " << rect.area() << std::endl;}
int main() { Rectangle rect; Square sq;
process(rect); // Корректный вывод: Area: 50 process(sq); // Корректный вывод: Area: 100
return 0;}
Пример 2: Нарушение LSP
Базовый класс
class Bird {public: virtual void fly() { std::cout << "Bird is flying" << std::endl; }};
Производный класс
class Penguin : public Bird {public: void fly() override { std::cout << "Penguins cannot fly" << std::endl; }};
Пример использования
void letBirdFly(Bird& bird) { bird.fly();}
int main() { Bird bird; Penguin penguin;
letBirdFly(bird); // Корректный вывод: Bird is flying letBirdFly(penguin); // Некорректный вывод: Penguins cannot fly
return 0;}
В данном примере, передача объекта Penguin
в функцию letBirdFly
нарушает принцип подстановки Лисков, так как Penguin
не соответствует ожиданиям базового класса Bird
.
Как соблюдать LSP
Контрактный дизайн
- Базовый и производный классы должны соблюдать общий контракт, что означает выполнение всех ожидаемых операций.
Избегайте нарушения предусловий и постусловий
- Предусловия не должны быть усилены в подклассе, а постусловия не должны быть ослаблены.
Инварианты
- Подклассы должны сохранять инварианты базовых классов.
Резюме
- Принцип подстановки Лисков гласит, что объекты подклассов должны быть взаимозаменяемы с объектами их базовых классов.
- Соблюдение LSP улучшает гибкость, повторное использование кода и поддерживаемость системы.
- Корректное использование LSP: подклассы сохраняют поведение базового класса.
- Нарушение LSP: подклассы изменяют поведение базового класса, что может привести к неожиданным результатам.
- Рекомендации: соблюдать контрактный дизайн, предусловия, постусловия и инварианты базовых классов.