← Назад к вопросам
Что такое принцип подстановки Барбары Лисков?
1.3 Junior🔥 121 комментариев
#ООП и паттерны проектирования
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Принцип подстановки Барбары Лисков (Liskov Substitution Principle)
Принцип подстановки Барбары Лисков (LSP) — один из пяти принципов SOLID, который гласит: объекты в программе должны быть заменяемы на объекты своих подтипов без нарушения корректности этой программы. Другими словами, если класс S является подтипом класса T, то объекты типа T в программе можно заменять объектами типа S без каких-либо нежелательных побочных эффектов.
Суть принципа
LSP требует, чтобы подклассы расширяли функциональность родителя, но не нарушали его контракт. Это означает:
- Не ослабляй предусловия — подкласс не должен требовать больше, чем родитель
- Не усиливай постусловия — подкласс не должен обещать меньше, чем родитель
- Не выбрасывай новые исключения — которые не объявлены в родителе
- Сохраняй инварианты — логические ограничения класса
Антипаттерн: нарушение LSP
// ❌ Плохо — нарушение LSP
public abstract class Bird
{
public abstract void Fly();
}
public class Penguin : Bird
{
public override void Fly()
{
throw new NotImplementedException("Пингвины не летают!");
}
}
// Использование
Bird bird = new Penguin();
bird.Fly(); // Выброс исключения — нарушение контракта Bird
Проблема: клиент ожидает, что любая Bird может летать, но Penguin нарушает этот контракт.
Правильное решение
// ✅ Хорошо — правильная иерархия
public abstract class Bird
{
public abstract void Move();
}
public class FlyingBird : Bird
{
public override void Move()
{
Console.WriteLine("Летит");
}
public virtual void Fly()
{
// Реализация полета
}
}
public class SwimmingBird : Bird
{
public override void Move()
{
Console.WriteLine("Плывет");
}
public virtual void Swim()
{
// Реализация плавания
}
}
public class Penguin : SwimmingBird
{
public override void Move()
{
Swim();
}
}
// Использование
Bird bird = new Penguin();
bird.Move(); // Работает корректно
Ещё один пример: иерархия фигур
// ❌ Нарушение LSP
public class Rectangle
{
public virtual int Width { get; set; }
public virtual int Height { get; set; }
public virtual int GetArea() => Width * Height;
}
public class Square : Rectangle
{
public override int Width
{
set { base.Width = base.Height = value; }
}
public override int Height
{
set { base.Width = base.Height = value; }
}
}
// Проблема
Rectangle rect = new Square();
rect.Width = 5;
rect.Height = 10;
Console.WriteLine(rect.GetArea()); // 100 вместо 50
Правильный подход
// ✅ Правильно — композиция вместо наследования
public interface IShape
{
int GetArea();
}
public class Rectangle : IShape
{
public int Width { get; set; }
public int Height { get; set; }
public int GetArea() => Width * Height;
}
public class Square : IShape
{
public int Side { get; set; }
public int GetArea() => Side * Side;
}
Причины нарушения LSP
- Неправильная иерархия классов — выбор неправильного родителя
- Дополнительные ограничения — подкласс добавляет ограничения, которые родитель не имел
- Разные поведение — подкласс кардинально меняет поведение методов
- Невалидные состояния — подкласс не гарантирует инварианты родителя
Практическое значение
Solidный код, соответствующий LSP:
- Предсказуем и безопасен для подстановки
- Упрощает тестирование (полиморфизм работает как ожидается)
- Облегчает расширение функционала
- Предотвращает скрытые баги при использовании иерархий классов