← Назад к вопросам

Что значит буква L в SOLID?

2.0 Middle🔥 211 комментариев
#ООП и паттерны проектирования

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что значит буква L в SOLID?

Буква L в SOLID соответствует принципу Liskov Substitution Principle (LSP)Принципу подстановки Лисков. Это один из пяти фундаментальных принципов объектно-ориентированного дизайна, сформулированный Барбарой Лисков в 1987 году и являющийся ключевым элементом для создания устойчивых, расширяемых и поддерживаемых систем.

Формулировка принципа

Функции, которые используют базовый тип, должны иметь возможность использовать подтипы базового типа, не узнавая об этом.

В более простой форме: если класс S является подклассом класса T, то объекты класса T могут быть заменены объектами класса S без нарушения корректности программы. Это означает, что наследование должно сохранять семантику и контракты базового класса, а не просто механически расширять его структуру.

Суть и цели принципа

Принцип подстановки Лисков направлен на обеспечение корректности и надежности системы при использовании полиморфизма и наследования. Его нарушение приводит к:

  • Неожиданным ошибкам в работе программы.
  • Сложности в тестировании и расширении кода.
  • Нарушению контрактов между компонентами системы.

Пример нарушения и соблюдения LSP

Рассмотрим классический пример с геометрическими фигурами, который часто иллюстрирует нарушение LSP.

Пример нарушения LSP:

public class Rectangle
{
    public virtual int Width { get; set; }
    public virtual int Height { get; set; }

    public int Area()
    {
        return Width * Height;
    }
}

public class Square : Rectangle
{
    public override int Width
    {
        get => base.Width;
        set
        {
            base.Width = value;
            base.Height = value; // Нарушение: изменение Height при установке Width
        }
    }

    public override int Height
    {
        get => base.Height;
        set
        {
            base.Height = value;
            base.Width = value; // Нарушение: изменение Width при установке Height
        }
    }
}

В этом примере класс Square нарушает LSP, потому что его поведение отличается от поведения Rectangle. Для Rectangle ширина и высота независимы, а для Square они связаны. Если клиентский код рассчитывает площадь, предполагая независимость свойств, подстановка Square вместо Rectangle приведет к неожиданным результатам.

Пример соблюдения LSP:

public abstract class Shape
{
    public abstract int Area();
}

public class Rectangle : Shape
{
    public int Width { get; set; }
    public int Height { get; set; }

    public override int Area()
    {
        return Width * Height;
    }
}

public class Square : Shape
{
    public int Side { get; set; }

    public override int Area()
    {
        return Side * Side;
    }
}

Здесь Square и Rectangle являются подтипами Shape, но не нарушают контракты друг друга. Клиентский код, работающий с Shape, может безопасно использовать любой подтип для вычисления площади.

Ключевые признаки соблюдения LSP

  1. Сохранение инвариантов базового класса: Подкласс не должен изменять фундаментальные свойства или условия, которые истинны для базового класса.
  2. Сохранение контрактов методов:
    • Предусловия (requirements before execution) не должны быть усилены.
    • Постусловия (guarantees after execution) не должны быть ослаблены.
    • История изменений объекта должна оставаться совместимой.
  3. Сохранение типов: Методы подкласса должны возвращать типы и принимать параметры, совместимые с базовым классом.

Практическое применение в C#

В C# соблюдение LSP часто связано с правильным использованием интерфейсов и абстрактных классов, а также с избеганием антипаттернов, таких как:

  • Нарушение контрактов исключений: Подкласс выбрасывает исключения, не ожидаемые для базового класса.
  • Изменение семантики возвращаемых значений: Например, возврат null вместо ожидаемого объекта.
  • Скрытые зависимости: Подкласс добавляет дополнительные требования к окружению.
// Пример хорошего дизайна с интерфейсом
public interface ILogger
{
    void Log(string message);
}

public class FileLogger : ILogger
{
    public void Log(string message)
    {
        // Запись в файл
    }
}

public class ConsoleLogger : ILogger
{
    public void Log(string message)
    {
        // Вывод в консоль
    }
}

// Клиентский код может использовать любой ILogger, соблюдая LSP
public class Service
{
    private readonly ILogger _logger;

    public Service(ILogger logger)
    {
        _logger = logger;
    }

    public void Execute()
    {
        _logger.Log("Выполнение операции");
    }
}

Итог

Принцип подстановки Лисков (LSP) — это не просто техническое правило, а философия дизайна, которая гарантирует, что наследование используется для создания логически совместимых и взаимозаменяемых компонентов. Его соблюдение:

  • Упрощает тестирование через использование базовых типов.
  • Укрепляет связность и отсутствие зависимостей между модулями.
  • Позволяет безопасно применять паттерны проектирования, такие как Стратегия или Декоратор.

Таким образом, L в SOLID подчеркивает, что полиморфизм должен быть семантически корректным, а наследование — не просто синтаксическим инструментом, но способом создания истинно расширяемых систем.

Что значит буква L в SOLID? | PrepBro