Liskov替代原则 – 没有压倒性的/虚拟的方法?

我对Liskovreplace原理的理解是,基类的某些属性是真实的,或者基类的某些实现行为,对于派生类也应该是真实的。

我猜这将意味着当一个方法在基类中定义时,它不应该在派生类中被覆盖 – 因为然后replace基类而不是派生类会得到不同的结果。 我想这也意味着,有(非纯)虚拟方法是一件坏事?

我想我可能对这个原理有一个错误的理解。 如果我不这样做,我不明白为什么这个原则是好的做法。 谁可以给我解释一下这个? 谢谢

基类中的子类重写方法完全是由Liskov替代原则允许的。

这可能会简化它太多,但我记得它是“一个子类应该不需要任何东西,承诺没有less”

如果一个客户端正在使用某个方法something(int i)的超类ABC ,那么客户端应该能够replaceABC任何子类而没有问题。 不要以variablestypes来考虑这个问题,也许应该根据前置条件和后置条件来考虑它。

如果我们上面的ABC基类中的something()方法有一个宽松的前提允许任何整数,那么ABC所有子类都必须允许任何整数。 不允许子类GreenABCsomething()方法添加一个附加前提条件,该方法要求参数为整数。 这会违反Liskov替代原则(即需要更多)。 因此,如果客户端使用BlueABC子类并将负整数传递给something() ,那么如果我们需要切换到GreenABC ,客户端将不会中断。

相反,如果基类ABC类的something()方法有一个后置条件 – 比如保证它不会返回零值 – 那么所有的子类都必须服从相同的后置条件,否则违反了Liskovreplace原则(即承诺较less) 。

我希望这有帮助。

不,它告诉你应该能够以和基础相同的方式使用派生类。 有很多方法可以覆盖一个方法而不会破坏这个方法。 一个简单的例子,C#中的GetHashCode()是所有类的基础,而且所有的类都可以作为“对象”来计算哈希码。 据我所知,打破规则的一个典型例子是从矩形派生出来的Square,因为Square不能同时具有Width和Height–因为设置一个会改变另一个,所以它不再符合矩形规则。 但是,您仍然可以使用.GetSize()来创build基本形状,因为所有形状都可以这样做 – 因此可以将任何派生形状replace为Shape。

有一个stream行的例子说明它是否像鸭子一样游泳,庸医喜欢鸭子,但需要电池,然后打破Liskov替代原则。

简而言之,你有一个正在被某人使用的鸭子类。 然后,通过引入PlasticDuck来添加层次结构,使其具有与Duck相同的重复行为(如游泳,嘎嘎声等),但需要电池来模拟这些行为。 这基本上意味着您正在为Sub Class的行为引入一个额外的前提条件,要求电池执行与之前的Base Duck类没有电池相同的行为。 这可能会让您的Duck类的用户感到意外,并可能会破坏围绕Base Duck类的预期行为而build立的function。

这是一个很好的链接 – http://lassala.net/2010/11/04/a-good-example-of-liskov-substitution-principle/

如果更改由基本方法定义的任何行为,则重写将打破Liskovreplace原则。 意思就是:

  1. 儿童方法的最弱前提条件不应该比基础方法更强。
  2. 子方法的后置条件意味着父方法的后置条件。 通过以下方式形成后置条件: a)由方法执行引起的所有副作用,以及b)返回的expression式的types和值。

从这两个要求你可以意味着,一个子方法中的任何新的function,不会影响超级方法所期望的,这并不违反这个原则。 这些条件允许您使用需要超类实例的子类实例。

如果这些规则不服从一个类违反LSP。 一个典型的例子是下面的层次结构:在ColoredPoint(x,y,color)中扩展Point(x,y)和重写方法equals(obj)Point(x,y) ,类ColoredPoint(x,y,color) 现在,如果有一个Set<Point>的实例,他可以假设在这个集合中具有相同坐标的两个点是相等的。 这与重写方法equals ,一般来说,没有办法扩展一个可实例化的类,并添加一个在equals方法中使用的方面而不会破坏LSP。

因此,每当你打破这个原则时,你都会隐含地引入一个潜在的错误,这个错误揭示了代码所期望的父类的不变性。 然而,在现实世界中往往没有明显的devise解决scheme不违反LSP,因此可以使用@ViolatesLSP类注解来警告客户端在多态集合中或者在任何时候使用类实例是不安全的其他types的依赖于Liskov替代原则的案例。

我认为你在描述原则的方式上是正确的,只有重写纯粹的虚拟或抽象的方法才能确保你不违反它。

但是,如果从客户的angular度来看原则,即引用基类的方法。 如果这个方法不能告诉(当然也不试图也不需要查明)传入的任何实例的类,那么你也没有违反原则。 所以重写一个基类方法可能并不重要(某种装饰器可能会这样做,在进程中调用基类方法)。

如果客户端似乎需要find传入的实例的类,那么您将面临维护的噩梦,因为您应该在维护工作中添加新的类,而不是修改现有的例程。 (另见OCP )

一个简单的解释(单击此链接 – >) Liskovreplace原则是,如果您有一个基类BASE和子类SUB1和SUB2,其余的代码应该始终引用BASE和NOT SUB1和SUB2。
就是这样,简单,只是不要指向子类