避免LSP违规的最佳方法

时间:2015-12-22 08:58:45

标签: c# oop solid-principles

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

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 Substitution Principle违规的标志。

您建议采用什么方法或模式来摆脱这种代码味道?

如果已经提出类似的问题,我很抱歉。

2 个答案:

答案 0 :(得分:5)

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

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

然而,您的示例是抽象的,这使得很难给出一些准确的反馈,但总的来说,我会说您应该将ShouldDoSomethingElse设置为调用者的责任,例如:

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

HandleItems(items);

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

答案 1 :(得分:1)

如果您希望根据客户端使用不同的行为,请让客户端向DoSomething传递一个参数,告诉该方法该做什么。或者创建两种不同的方法,并将其留给客户端调用。

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