我对Liskov替换原则的理解是,对于派生类,基类的某些属性为true或某些已实现的基类行为也应该为true。
我想这意味着当一个方法在基类中定义时,它永远不应该在派生类中被覆盖 - 因为那样替换基类而不是派生类会产生不同的结果。我想这也意味着,拥有(非纯)虚拟方法是件坏事吗?
我想我可能对这个原则有错误的理解。如果我不这样做,我不明白为什么这个原则是好的做法。谁可以给我解释一下这个?感谢
答案 0 :(得分:51)
Liskov Substituion Principle完全允许基类中的子类重写方法。
这可能会过多地简化它,但我记得它是 “一个子类应该不再需要承诺”
如果客户端使用带有方法ABC
的超类something(int i)
,则客户端应该能够毫无问题地替换ABC
的任何子类。而不是根据变量类型来考虑这一点,或许可以根据前提条件和后置条件来考虑它。
如果我们something()
基类中的ABC
方法具有允许任何整数的宽松前提条件,那么ABC
的所有子类都必须还允许任何整数。子类GreenABC
不允许为something()
方法添加额外的前提条件,该方法要求参数为正整数。这将违反Liskov替代原则(即需要更多)。因此,如果客户端正在使用子类BlueABC
并将负整数传递给something()
,则如果我们需要切换到GreenABC
,客户端将不会中断。
相反,如果基类ABC
类something()
方法具有后置条件 - 例如保证它永远不会返回零值 - 那么所有子类也必须遵循相同的后置条件或者它们违反了Liskov替代原则(即承诺减少)。
我希望这会有所帮助。
答案 1 :(得分:10)
有一个流行的例子说,如果它像鸭子一样游泳,嘎嘎喜欢鸭子但需要电池,那么就打破了Liskov替代原则。
简单地说,你有一个正在被某人使用的基础Duck类。然后你通过介绍PlasticDuck添加层次结构与鸭子相同的重写行为(如游泳,嘎嘎等),但需要电池来模拟这些行为。这实际上意味着您要为Sub Class的行为引入一个额外的前提条件,要求电池执行与之前没有电池的Base Duck类所做的相同的行为。这可能会让您的Duck类的消费者感到意外,并且可能会破坏围绕Base Duck类的预期行为构建的功能。
这是一个很好的链接 - http://lassala.net/2010/11/04/a-good-example-of-liskov-substitution-principle/
答案 2 :(得分:7)
不,它告诉您应该能够以与其基础相同的方式使用派生类。有很多方法可以覆盖方法而不会破坏它。一个简单的例子,C#中的GetHashCode()是所有类的基础,并且它们中的所有类都可以用作“对象”来计算哈希码。据我所知,破坏规则的一个典型例子是从矩形中导出Square,因为Square不能同时具有宽度和高度 - 因为设置一个会改变另一个,因此它不再符合Rectangle规则。但是,您仍然可以使用.GetSize()来创建基本Shape,因为所有形状都可以执行此操作 - 因此任何派生的形状都可以替换并用作Shape。
答案 3 :(得分:3)
如果更改基本方法定义的任何行为,则覆盖中断Liskov替换原则。这意味着:
根据这两个要求,您可以暗示子方法中的任何新功能不会影响超级方法的预期,并不违反原则。这些条件允许您使用需要超类实例的子类实例。
如果不遵守这些规则,则类违反LSP。一个典型的例子是以下层次结构:类Point(x,y)
,类ColoredPoint(x,y,color)
,它扩展Point(x,y)
并覆盖equals(obj)
中反映颜色相等的ColoredPoint
方法Set<Point>
。现在,如果有一个equals
的实例,他可以假设在这个集合中具有相同坐标的两个点相等。覆盖方法equals
的情况并非如此,并且通常无法扩展可实例化的类并添加@ViolatesLSP
方法中使用的方面而不破坏LSP。
因此,每当您违反此原则时,您隐式引入了一个潜在的错误,该错误揭示了代码所期望的父类的不变性何时不满足。然而,在现实世界中,通常没有明显的设计解决方案不违反LSP,因此可以使用例如{{1}}类注释来警告客户端在多态集中使用类实例是不安全的。或任何其他依赖Liskov替代原则的案例。
答案 4 :(得分:1)
我认为你描述原则的方式确实是正确的,只有重写纯虚拟或抽象方法才能确保你不违反它。
但是,如果从客户端的角度来看这个原则,那就是一个引用基类的方法。如果这个方法无法告诉(当然不会尝试并且不需要查找)传入的任何实例的类,那么您也没有违反该原则。因此,覆盖基类方法可能并不重要(某些类型的装饰器可能会这样做,在过程中调用基类方法)。
如果客户端似乎需要找出传入的实例的类,那么您就是在进行维护噩梦,因为您应该在维护工作中添加新类,而不是修改现有的常规。 (另见OCP)