如何遵守Liskov的替换原则(LSP)并仍然受益于多态性?

The*_*ght 11 c# polymorphism liskov-substitution-principle solid-principles

LSP说"派生类型不能改变基类型的行为",换言之,"派生类型必须完全可替换它们的基类型".

这意味着如果我们在基类中定义虚方法,我们就违反了这个原则.

此外,如果我们使用new关键字在驱动方法中隐藏方法,那么我们又违反了这个原则.

换句话说,如果我们使用多态,我们就违反了LSP!

在许多应用程序中,我在基类中使用了虚方法,现在我意识到它违反了LSP.此外,如果你使用模板方法模式,你违反了这个原则,我已经使用了很多.

那么,当你需要继承并且你也想从多态中受益时,如何设计符合这个原则的应用程序呢?我糊涂了!

请参阅此处的示例:http://www.oodesign.com/liskov-s-substitution-principle.html

Ily*_*nov 7

Barbara Liskov有一篇非常好的文章数据抽象和层次结构,她特别涉及多态行为和虚拟软件构造.阅读本文后,您可以看到,她深入地描述了软件组件如何通过简单的多态调用实现灵活性和模块化.

LSP说明了实现细节,而不是抽象.具体来说,如果您使用某种类型的接口或抽象T,您应该期望传递所有子类型T而不是观察意外行为或程序崩溃.

这里的关键字是意外的,因为它可以描述程序的任何属性(正确性,执行的任务,返回的语义,临时等).因此,制作方法virtual本身并不意味着违反LSP


Tom*_*zzo 4

LSP 表示,您必须能够以与使用派生类超类相同的方式使用派生类:“程序中的对象应该可以用其子类型的实例替换,而不改变该程序的正确性”。打破该规则的经典继承是从 Rectangle 类派生 Square 类,因为前者必须具有Height = Width,而后者可以具有Height != Width

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

public class Square : Rectangle
{
    public override Int32 Height
    {
        get { return base.Height; }
        set { SetDimensions(value); }
    }

    public override Int32 Width
    {
        get { return base.Width; }
        set { SetDimensions(value); }
    }

    private void SetDimensions(Int32 value)
    {
        base.Height = value;
        base.Width = value;
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,Width 和 Height 属性的行为发生了变化,这违反了该规则。让我们通过输出来看看行为发生变化的原因:

private static void Main()
{
    Rectangle rectangle = new Square();
    rectangle.Height = 2;
    rectangle.Width = 3;

    Console.WriteLine("{0} x {1}", rectangle.Width, rectangle.Height);
}

// Output: 3 x 2
Run Code Online (Sandbox Code Playgroud)