Понимание отношений между Лисковым и OCP

Я укрепляю свое понимание взаимосвязи между замещающим принципалом Лисков и принципом открытого закрытия. Если бы кто-нибудь мог подтвердить мои выводы и ответить на мои вопросы ниже, это было бы здорово.

У меня есть следующие классы. Как видите, B является производным от A и переопределяет функцию DisplayMessage, чтобы изменить поведение.

public class A
{
    private readonly string _message;

    public A(string message)
    {
        _message = message;
    }

    public virtual void DisplayMessage()
    {
        Console.WriteLine(_message);
    }
}

public class B : A
{
    public B(string message) : base(message){}

    public override void DisplayMessage()
    {
        Console.WriteLine("I'm overwriting the expected behavior of A::DisplayMessage() and violating LSP >:-D");
    }
}

Теперь в моей программе начальной загрузки ShowClassTypeis ожидается объект типа A, который должен услужливо написать, какой это тип класса. Однако B нарушает LSP, поэтому, когда вызывается его функция DisplayMessage, она выводит совершенно неожиданное сообщение и по существу мешает намеченной цели ShowClassType.

class Program
{
    static void Main(string[] args)
    {
        A a = new A("I am A");
        B b = new B("I am B");

        DoStuff(b);

        Console.ReadLine();
    }

    private static void ShowClassType(A model)
    {
        Console.WriteLine("What Class are you??");
        model.DisplayMessage();
    }
}

Итак, мой вопрос заключается в том, прав ли я, заключая, что ShowClassType теперь нарушает принцип открытия и закрытия, потому что теперь, когда тип B может войти и изменить ожидаемую функцию этого метода, он больше не закрыт для модификации (т.е. чтобы гарантировать, что он поддерживает это ожидаемое поведение, вам придется изменить его, чтобы он сначала проверял, чтобы убедиться, что мы работаем только с исходным объектом A)?

Или, наоборот, это просто хороший пример, показывающий, что ShowClassType закрыт для модификации и что, передав производный тип (хотя и нарушающий LSP), мы расширили то, для чего он предназначен?

Наконец, является ли плохой практикой создание виртуальных функций в базовых классах, если базовый класс не является абстрактным? Поступая таким образом, не провоцируем ли мы производные классы нарушать принцип замещения Лискова?

Ваше здоровье


person Chris    schedule 08.10.2011    source источник


Ответы (2)


Я бы сказал, что не ShowClassType нарушает принцип открытости/закрытости. Только класс B нарушает принцип подстановки Лисков. A открыт для расширения, но закрыт для модификации. Из Википедии,

сущность может позволить изменить свое поведение без изменения исходного кода.

Очевидно, что исходный код A не изменен. Частные члены A также не используются (что также было бы нарушением принципа Open/Closed в моей книге). B строго использует общедоступный интерфейс A, поэтому, хотя принцип Open/Closed соблюдается, принцип подстановки Лисков нарушается.

Последний вопрос заслуживает обсуждения сам по себе. Связанный с этим вопрос по SO здесь.

person S.L. Barth    schedule 08.10.2011

Я думаю, что это не нарушение не LSP и не OCP в ЭТОМ контексте использования.

На мой взгляд, ShowClassType не является нарушением OCP: 1. Функция не может нарушать OCP, это может сделать только классовая архитектура. 2. Вы можете добавить новые поведения в классы, производные от A, чтобы они не нарушали OCP.

А как же ЛСП? Ваша причина - пользователь не ожидал получить это сообщение? Но он получил какое-то сообщение! Если переопределение функции возвращает какое-то сообщение, я думаю, что это нормально в ЭТОМ контексте вашего кода. Если функция, складывающая два числа, перекрывается, а 1+1 возвращает 678, то для меня это неожиданно и плохо. НО, если для ученого-физика с планеты Марс, это может быть хорошим ответом.

НЕ АНАЛИЗИРУЙТЕ ПРОБЛЕМУ БЕЗ ВСЕХ КОНТЕКСТОВ!!! Вы должны получить полную картину проблемы. И, конечно же

person zzfima    schedule 10.05.2012