Ответ
Принцип подстановки Барбары Лисков (LSP) — третий принцип SOLID. Он гласит: объекты в программе должны быть заменяемыми экземплярами их базовых типов без изменения корректности этой программы.
Простыми словами: Если у вас есть функция, работающая с классом Родитель, то вы должны иметь возможность передать ей любой класс-Ребенок, и программа продолжит работать правильно. Наследник не должен ужесточать предусловия или ослаблять постусловия родительского метода.
Классический пример нарушения LSP:
public class Bird
{
public virtual void Fly()
{
Console.WriteLine("I'm flying!");
}
}
public class Penguin : Bird // Пингвин — птица, но не летает!
{
public override void Fly()
{
throw new NotImplementedException("Penguins can't fly!");
}
}
// Клиентский код, ожидающий работу с Bird
public class BirdAviary
{
public void ReleaseBird(Bird bird)
{
bird.Fly(); // ВЫБРОСИТ ИСКЛЮЧЕНИЕ, если bird — Penguin!
}
}
Здесь Penguin нарушает контракт, заданный базовым классом Bird (метод Fly), и ломает клиентский код.
Исправление через пересмотр иерархии (соблюдение LSP):
// Базовый класс не навязывает необщее поведение
public abstract class Bird { }
// Интерфейс определяет способность, которой обладают не все птицы
public interface IFlyable
{
void Fly();
}
public class Sparrow : Bird, IFlyable // Воробей — птица и умеет летать
{
public void Fly() => Console.WriteLine("Sparrow is flying!");
}
public class Penguin : Bird // Пингвин — птица, но не реализует IFlyable
{
// Специфичные для пингвина методы, например, Swim()
public void Swim() => Console.WriteLine("Penguin is swimming!");
}
// Клиентский код теперь корректен
public class FlightSimulator
{
public void StartFlight(IFlyable flyableBird) // Принимает только то, что может летать
{
flyableBird.Fly();
}
}
Почему LSP важен:
- Обеспечивает надежность полиморфизма. Вы можете безопасно использовать коллекции базового типа.
- Упрощает тестирование. Можете использовать mock-объекты, наследующие от того же базового класса.
- Предотвращает появление неочевидных ошибок, когда подстановка подкласса приводит к неожиданному поведению.
Нарушение LSP часто сигнализирует о неправильно спроектированной иерархии наследования.