【问题标题】:The best way to avoid LSP violation避免违反 LSP 的最佳方法
【发布时间】:2016-03-28 11:40:05
【问题描述】:

让我们考虑以下示例。我有这样的类的层次结构:

abstract class Base
{
    public abstract void DoSomething();
}

class Foo : Base
{
    public override void DoSomething()
    {
        Console.WriteLine("Foo. DoSomething...");
    }
}

class Bar : Base
{
    public override void DoSomething()
    {
        Console.WriteLine("Bar. DoSomething...");

        if (ShouldDoSomethingElse)
        {
            DoSomethingElse();
        }
    }

    public void DoSomethingElse()
    {
        Console.WriteLine("Bar. DoSomething else...");
    }

    public bool ShouldDoSomethingElse { get; set; }
}

我的客户是这样的:

class Program
{
    static void Main(string[] args)
    {
        var foo = new Foo();
        var bar = new Bar();
        var items = new List<Base> {foo, bar};

        HandleItems(items);
    }

    static void HandleItems(IEnumerable<Base> items)
    {
        foreach (var item in items)
        {
            if (item is Bar)
            {
                //Code smell! LSP violation.
                var bar = item as Bar;
                bar.ShouldDoSomethingElse = true;
            }
            item.DoSomething();
        }
    }
}

请注意,我们可以有多个客户端,其中一些可能需要 ShouldDoSomethingElse = 'true' 其他 'false'。

毫无疑问,在 HandleItems() 中以不同方式处理项目是设计不良和违反 Liskov 替换原则的标志。

您会建议什么方法或模式来消除这种代码异味?

如果有人问过类似的问题,我很抱歉。

【问题讨论】:

    标签: c# oop solid-principles


    【解决方案1】:

    您的代码不违反 Liskov 替换原则。该原则仅说明所有子类型必须以兼容的方式运行,而不会在注入不同的实现时破坏消费者。在你的例子中。当您提供不同的类型时,代码不会中断。

    不过,向下转换为Bar 是一种代码味道,因为HandleItems 违反了依赖倒置原则,因为HandleItems 现在依赖于具体类型而不是抽象。此外,此代码稍后可能会导致违反打开/关闭原则,因为您可能需要在每次添加新的 Base 子类型时更改 HandleItems 方法。一旦您需要更改HandleItems,这意味着它不会关闭以进行修改。

    但是您的示例很抽象,因此很难给出一些准确的反馈,但总的来说,我会说您应该将设置 ShouldDoSomethingElse 的责任转移给调用者,例如:

    var foo = new Foo();
    var bar = new Bar { ShouldDoSomethingElse = true };
    var items = new List<Base> { foo, bar };
    
    HandleItems(items);
    

    这可以防止HandleItems 知道任何有关派生类型的信息(这允许单独部署派生类型)并防止HandleItems 不断变化。

    【讨论】:

      【解决方案2】:

      如果你想要根据客户端不同的行为,让客户端传递一个参数给 DoSomething,告诉方法要做什么。或者创建两种不同的方法,然后由客户端调用哪个方法。

      我不确定我是否理解问题所在。您使用多态性,然后通过确定类型并根据该知识采取行动来使其毫无意义。 DoSomething 应该已经做了一些特定于类的事情,多态性的目的是让客户端不知道不同的实现并仍然获得适当的行为。

      【讨论】:

        猜你喜欢
        • 2017-02-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-15
        • 2018-02-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多