当前位置: 首页 > 知识库问答 >
问题:

Liskov替换原理(LSP)及其代码示例

段宏毅
2023-03-14

Liskov替代原则要求

  1. 子类型中的前提条件不能加强

任何人都可以发布一个违反上述每一点的例子和另一个解决这些问题的例子吗?

共有2个答案

冉永宁
2023-03-14

本文对问题中的所有四项都进行了彻底的审查。

先决条件不能在子类型中得到加强。

这个答案给出了“真鸭”和“电鸭”的例子,我建议你去看看。为了简洁起见,我将在这个项目中使用它。

这意味着子类型不能妨碍原始方法在基类中的行为。在上面提到的答案的代码中,两只鸭子都可以游泳,但是只有打开了电子鸭子才会游泳。因此,任何要求鸭子(从接口IDuck)游泳的代码单元现在都不起作用,除非明确指定鸭子是Electric icDuck(然后打开),这需要在任何地方实现。

后条件不能在子类型中被削弱。

对于这一个,我们可以从鸭子类比中退一步。让我们以这个答案为基础。假设我们有一个只接受正整数的基类。如果在子类型中,在扩展该方法时,我们删除了数字必须为正的条件,那么所有过去认为数字为正的代码单元现在都有被破坏的风险,因为现在无法保证数字为正。下面是这个想法的一个代表:

public class IndexBaseClass
{
    protected int index;
    public virtual int Index
    {
        get
        {
            //Will return positive integers only
            return index < 0 ? 0 : index;
        }
        set
        {
            index = value;
        }
    }
}

public class IndexSubClass : IndexBaseClass
{
    public override int Index
    {
        get
        {
            //Will pay no mind whether the number is positive or negative
            return index;
        }
    }
}

public class Testing
{
    public static int GetIndexOfList(IndexBaseClass indexObject)
    {
        var list = new List<int>
        {
            1, 2, 3, 4
        };

        return list[indexObject.Index];
    }
}

如果调用GetIndexOfList传递IndexSubClass对象,则无法保证该数字为正数,因此可能会中断应用程序。假设您已经在代码中调用了这个方法。您将不得不浪费时间检查所有实现中的正值。

子类型中必须保留超类型的不变量。

父类可能有一些不变量,也就是说,只要对象存在,某些条件就必须保持为真。任何子类都不应该继承这个类并消除这个不变量,因为到目前为止所有的实现都有崩溃的风险。在下面的例子中,父类抛出一个异常,如果它是负的,然后设置它,但是子类只是简单地忽略它,它只是设置不变量。

以下代码取自此处:

public class ShippingStrategy
{
    public ShippingStrategy(decimal flatRate)
    {
        if (flatRate <= decimal.Zero)
            throw new ArgumentOutOfRangeException("flatRate", "Flat rate must be positive 
  and non-zero");

        this.flatRate = flatRate;
    }

    protected decimal flatRate;
}

public class WorldWideShippingStrategy : ShippingStrategy
{
    public WorldWideShippingStrategy(decimal flatRate)
        : base(flatRate)
    {
        //The subclass inherits the parent's constructor, but neglects the invariant (the value must be positive)
    }

    public decimal FlatRate
    {
        get
        {
            return flatRate;
        }
        set
        {
            flatRate = value;
        }
    }
}

历史约束(历史规则)。

这一条与上一条规则相同。它指出子类型不应引入改变父类中不可变属性的方法,例如在子类中将新的Set方法添加到以前只能通过构造函数设置的属性中。

例如:

public class Parent
{
    protected int a;

    public Parent(int a)
    {
        this.a = a;
    }
}

public class Child : Parent
{
    public Child(int a) : base(a)
    {
        this.a = a;
    }

    public void SetA(int a)
    {
        this.a = a;
    }
}

现在,由于子类的存在,父类中以前不可变的属性现在是可变的。这也违反了LSP。

窦涵忍
2023-03-14

你知道ICollection接口吗?假设您正在编写一个获取ICollection的方法,并通过使用其Add方法或更好的Clear方法对其进行操作。如果有人传递ReadOnlyCollection(实现ICollection),您将获得使用Add的异常。现在,由于接口定义了ok,因此ReadOnlyCollection违反了LSP,因此您永远也不会想到这一点。

 类似资料:
  • 我试图通过反复阅读维基百科条目来确定我对上述原则的理解。 撇开仍然让我悲伤的协变和逆变的概念不谈,wikipedia还提到超类型的不变量必须保留在子类型和历史约束或历史规则中。基于最后两个概念,我提出了一个小例子: 所以我的问题是:基于上述两个概念,我用这个例子是否违反了原则?若否,原因为何? 事先非常感谢。

  • 问题内容: 我的情况与Code Complete中Steve McConnell 提到的情况非常相似。我唯一的问题是基于车辆,而三轮车恰好是根据法律,属于汽车。到目前为止,汽车只有四个轮子。无论如何,我的域都不必要地复杂,因此很容易遵循下面的cats示例。 对重写例程并且在派生例程中不执行任何操作的类要保持怀疑。这通常表明基类的设计存在错误。例如,假设您有一个Cat类和一个例程Scratch(),

  • 来自维基百科, Liskov的行为子类型概念定义了对象的可替代性概念;也就是说,如果S是T的子类型,则程序中T类型的对象可以替换为S类型的对象,而不改变该程序的任何期望属性(例如正确性)。 假设以下类层次结构: 基本抽象类-。它有一个只读属性,在后继程序中被重写。 基类的继承者-,它重写并返回灰色。 Cat的继任者-,它覆盖并返回带条纹的。 然后我们声明一个方法,参数类型为(不是)。 向该方法发送

  • 在创建我的班级结构时,我努力坚持利斯科夫替代原则。我想在Day类中存储一组日历项。需要有几种不同类型的日历项,例如: 任命项目 备注项目 轮换项目 它们都共享一些抽象基类CalendarItem中的常见功能: 但例如RotaItem有一些额外的功能: 其他类也添加了自己的逻辑等。 我有一组CalendarBaseItem用于我的日课: 但在回顾这一点时,我可以看到我正在打破LSP原则,因为我必须检

  • 存在无法写入或查找的流派生类这一事实是否违反了Liskov替换原则? 例如,无法查找NetworkStream,如果调用方法,它将抛出。 还是因为存在标志就可以了? 考虑到众所周知的继承自的例子...将标志和添加到是否可以解决问题? 这难道不是打开了通过添加旗帜来解决问题的大门吗?

  • 假设我有一个抽象类鸟,它的一个函数是飞(int高度)。 我有许多不同的鸟类,每个类都有自己不同的飞的实现,这个函数在整个应用程序中被广泛使用。 有一天,我的老板来了,要求我添加一只鸭子,它做其他鸟类所做的一切,只是它不飞,而是在应用程序的池塘里游泳。 将duck添加为bird的子类型违反了Liskov替换规则,因为在调用duck时。我们要么抛出异常,要么什么也不做,要么违反正确性原则。 在牢记坚实