我只是想知道IS-A(UML术语和OOP)和Liskov替换原则(LSP)之间是否存在差异?
实际上,两者都在谈论继承。那么实践中的主要区别是什么?
答案 0 :(得分:8)
这两个术语最终都描述了相同的“概念”。
Liskov替换原则告诉您:当B类型的某个对象的每个和任何用法都可以时,类B(基础)和C(子)之间的继承关系是合理的。替换为C类型的对象。
这意味着:B定义了API和公共合同--C也必须维护这些属性!
而且IS-A相同:C 的某些对象也是 a B。
“差异”是:LSP为您提供了可以检查的完全规则。而IS-A更像是一种“观察”或意图表达。比如:你表示你希望那个C IS-A B。
换句话说:当您不知道如何正确使用继承时,IS-A无法帮助您编写正确的代码。 LSP清楚地告诉你类似的东西:
class Base { int foo(); }
class Child extends Base { @Override double foo(); }
无效。根据LSP,您只能加宽方法参数,限制返回值。
int iValue = someBase.foo();
不能替换为
int iValue = someChild.foo();
因为foo()
方法在其结果中加宽了。
最后的想法是:许多人认为C IS-A B 与写下Child extends Base
是相同的。是。但是这只告诉编译器C扩展B.这并不意味着你在C中使用的方法将遵循LSP,从而将C转换为B的真实有效后代。
C IS-A B 要求多于“C extends B”。要真正有效,LSP必须坚持!
答案 1 :(得分:0)
Is-A / Has-A与是否使用继承有关。 LaserCat是激光的一种,还是应该仅具有激光场?如果您以某种方式使用继承,则LSP是要注意的特定问题。
继承的好用途是拥有Animal a1;指向猫或狗,使用 a1.speed()(*)。 LSP说,猫和狗速度功能需要使用相同的单位。同样,用于Cats的a1.setWeight不允许负权重,但是Dogs会将其更改为0。当您可以调用任一函数时,LSP都与一致性有关。如果您已经知道Animal a1,这实际上很明显。把戏,这很难。
相反,假设您有独立的猫和狗。如果实际测量的速度不同,则最好让Cats使用公制,而Dogs使用英文。如果“猫和狗”是从Animal继承而来的,但是您绝不使用“ a1 =猫或狗”的把戏,那还是可以的。 c1.speed()可以肯定是公制,d1.speed()显然是每小时英里。但是,如果您具有animalRace(Animal a1)函数,则会遇到问题。
差异也是语气。 Is-a / has-a是刚起步的人的简单建议。 LSP来自30年来为博士学位撰写的论文。它使用的方程式适用于Com Sci专业研究生。它使用Pre和Post条件,这是当时常见的术语。 “替换”是一个很好的数学术语,但是今天我们只说“将指向任何子类的基类”。
(*)更详细:我们有动物超类,而猫和狗是子类。 Animal具有存根速度功能,Cat和Dog分别覆盖它。 a1.speed()查找正确的一个。一个真正的例子是一系列动物,它们确实拥有猫和狗。或带有动物输入功能的功能,需要一只猫或狗。
(same *)通常,基类是抽象的-我们永远不会创建Animal对象。但是,如果我们有一个Toaster超类和DeluxeToaster子类,那么窍门是相同的。采取多士炉的任何人都可能采用“是-是”多士炉的任何东西。
答案 2 :(得分:0)
继承是一种纯粹的句法关系,而里氏替换也是一种语义关系。
语法很简单:您学习如何将一个类声明为另一个类的子类,编译器会告诉您是否编写了有效的代码。如果代码编译通过,您就创建了一个继承关系 (IS-A)。
语义更难:它们决定了代码对客户意味着什么。语义通常包括文档之类的东西。在流行的 OOP 语言中,编译器无法告诉您代码是遵守还是违反了其预期含义。这就是 Liskov 介入的地方。