关于oop:IS-A和Liskov替代原则之间的区别?

Difference between the IS-A and Liskov Substitution Principle?

我只是想知道IS-A(UML术语和OOP)和Liskov替换原则(LSP)之间是否存在差异?

实际上,他们都在谈论遗产。那么,实践中的主要区别是什么呢?


两个术语最后都描述了相同的"概念"。

Liskov替换原理告诉你:当B类(base)和C类(child)的某个对象的每一个用法都是正确的时,B类(base)和C类(child)之间的继承关系是正确的。可以用C类型的对象替换。

这意味着:B定义了一个API和一个公共契约——C也必须维护这些属性!

is-a等于同样的东西:c的某个物体也是b。

区别在于:LSP提供了您可以检查的精确规则。而IS-A更像是一种"观察"或意向表达。比如:你表示你希望C班是B班。

换句话说:当您不知道如何正确地使用继承时,IS-A并不能帮助您编写正确的代码。而LSP清楚地告诉你:

1
2
class Base { int foo(); }
class Child extends Base { @Override double foo(); }

无效。根据lsp,只能扩大方法参数,并限制返回值。

1
int iValue = someBase.foo();

不能替换为

1
int iValue = someChild.foo();

因为foo()方法的结果被扩大了。

最后一个想法是:很多人认为C是-A是B,这和写下Child extends Base是一样的。对。但这只告诉编译器C扩展了B,并不意味着在C中使用的方法将遵循LSP,从而将C变成B的真正有效的后代。

C IS-A B要求"C延伸B"以上。要真正有效,必须坚持LSP!


IS-A/HAS-A是关于是否使用继承。激光探测仪是一种激光器,还是应该有一个激光场?如果您以某种方式使用继承,那么lsp是一个需要注意的特定问题。

继承的好用法是动物a1;指向猫或狗,使用A1.速度()(*)。LSP说猫和狗的速度功能需要使用相同的单位。同样,A1.setweight对于猫不能允许负权重,但是狗将其更改为0。当您可以调用任意一个函数时,lsp是关于一致性的。如果你已经知道动物a1的话,事实上这很明显;诡计,这很难。

相反,假设你有独立的猫和狗。如果真的速度是不同的,猫用公制,狗用英语。如果猫和狗是从动物身上继承下来的,但是你从来没有用过"a1=猫或狗"的伎俩,那它还是可以的。c1.speed()是公制的,d1.speed()显然是每小时英里数。但如果你有功能动画(动物A1),你就有问题。

区别也在于音调。IS-A/HAS-A对于刚起步的人来说是一个简单的建议。LSP是一篇30年前为博士写的论文,它所使用的方程式是针对研究生的COM科学专业的。它使用了前后条件,这是当时常见的、众所周知的术语。""替换"是一个很好的数学术语,但今天我们只说"将指向任何子类的基类"。

(*)更详细地说:我们有超类动物,以及猫和狗的子类。动物有一个存根速度功能,猫和狗都会覆盖它。a1.speed()查找正确的。一个真正的例子是一系列动物,它们真的养着猫和狗。或具有动物输入的函数,期望有猫或狗。

(同样的*)通常基类是抽象的-我们永远不会创建动物对象。但如果我们有一个烤面包机超类和一个妄想烤面包机子类,技巧是相同的。任何带着烤面包机的东西都可能带着任何"是的"烤面包机。