C# 避免LSP违规的最佳方法 我们考虑下面的例子。我有这样的类的层次结构: 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(); } } }

C# 避免LSP违规的最佳方法 我们考虑下面的例子。我有这样的类的层次结构: 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(); } } },c#,oop,solid-principles,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 DoSometh

我的客户是这样的:

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();
        }
    }
}
类程序
{
静态void Main(字符串[]参数)
{
var foo=new foo();
var bar=新的bar();
var items=新列表{foo,bar};
手工项目(项目);
}
静态无效处理项(IEnumerable项)
{
foreach(项目中的var项目)
{
如果(项目为条形)
{
//代码气味!LSP违规。
var bar=项目为条形;
bar.ShouldDoSomethingElse=真;
}
项目.DoSomething();
}
}
}
注意,我们可以有几个客户端,其中一些可能需要ShouldDoSomethingElse='true'其他'false'

显然,在HandleItems()中以不同的方式处理项目是设计不好和违反Liskov替换原则的标志

您建议采用什么方法或模式来消除这种代码气味


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

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

然而,向下倾斜到
是一种代码味道,因为
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);
var foo=new foo();
var bar=新条{ShouldDoSomethingElse=true};
var items=新列表{foo,bar};
手工项目(项目);

这可以防止
HandleItems
必须了解派生类型的任何信息(允许单独部署派生类型),并防止
HandleItems
不断变化。

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

然而,向下倾斜到
是一种代码味道,因为
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);
var foo=new foo();
var bar=新条{ShouldDoSomethingElse=true};
var items=新列表{foo,bar};
手工项目(项目);

这可以防止
HandleItems
必须了解派生类型的任何信息(允许单独部署派生类型),并防止
HandleItems
不断变化。

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


我不确定我是否理解这个问题。您使用多态性,然后通过确定类型并基于该知识采取行动,使其变得毫无意义。DoSomething应该已经做了一些特定于类的事情,多态性的要点是让客户机不知道不同的实现,并且仍然可以获得适当的行为。

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

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