Логотип YeaHub

База вопросов

Собеседования

Тренажёр

База ресурсов

Обучение

Навыки

Задачи

Войти

Выбери, каким будет IT завтра — вместе c нами!

YeaHub — это полностью открытый проект, призванный объединить и улучшить IT-сферу. Наш исходный код доступен для просмотра на GitHub. Дизайн проекта также открыт для ознакомления в Figma.

© 2026 YeaHub

AI info

Карта сайта

Документы

Медиа

Назад
Вопрос про C#: solid, liskov substitution

В чём заключается принцип подстановки Барбары Лисков (Liskov Substitution)?

Вопрос требует объяснения сути принципа LSP, который гарантирует, что объекты производных классов могут быть использованы вместо объектов базовых классов без изменения корректности программы.

Короткий ответ

Принцип подстановки Лисков гласит: функции, которые используют ссылку на базовый класс, должны иметь возможность использовать объекты производных классов, не зная об этом. Наследник не должен усиливать предусловия, ослаблять постусловия и не должен изменять поведение, ожидаемое от базового класса. Классический пример нарушения — квадрат, наследующий от прямоугольника и ломающий его логику.

Длинный ответ

LSP является расширением полиморфизма и обеспечивает логическую согласованность в иерархии наследования. Нарушение этого принципа приводит к неожиданным ошибкам, которые сложно обнаружить.

Основные правила, вытекающие из LSP:

  1. Синтаксическая совместимость:

    • Наследник должен иметь те же публичные методы и свойства, что и родитель. Это обеспечивается компилятором.

  2. Семантическая совместимость (поведенческая):

    • Это самая важная и сложная часть. Поведение наследника не должно противоречить поведению, обещанному базовым классом.

    • Ослабление предусловий (Preconditions): Наследник не может требовать большего, чем базовый класс. Например, если метод базового класса принимает любой целый число, метод наследника не может требовать только положительные числа.

    • Усиление постусловий (Postconditions): Наследник не может гарантировать меньше, чем базовый класс. Например, если метод базового класса гарантирует, что база данных будет подключена после вызова, наследник не может нарушать это обещание.

    • Сохранение инвариантов (Invariants): Наследник должен сохранять все логические условия, которые всегда истинны для базового класса.

Классический пример нарушения LSP: "Квадрат/Прямоугольник"

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

    public int Area => Width * Height;
}

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

class Program {
    static void UseRectangle(Rectangle rect) { // Функция ожидает Rectangle
        rect.Width = 5;
        rect.Height = 4;
        Console.WriteLine($"Expected area: 20, Got: {rect.Area}"); // Ожидается 20
    }

    static void Main() {
        Rectangle rect = new Rectangle();
        UseRectangle(rect); // Вывод: Expected area: 20, Got: 20 - OK.

        Rectangle square = new Square(); // Подстановка наследника
        UseRectangle(square); // Вывод: Expected area: 20, Got: 16 - ОШИБКА!
    }
}

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

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

Уровень

  • Рейтинг:

    2

  • Сложность:

    7

Навыки

  • C#

    C#

Ключевые слова

#solid

#liskov substitution

Подпишись на C# Developer в телеграм