译文
作为一个Java开发者,我从没有听过LSP模式。我只在读一些C++相关的东西的时候听说过这种模式。这非常奇怪,因为这种模式有时候被当作面向对象编程的五大原则之一。
这个原则是 Barbara Liskov于1987年第一次提出,并在1994年阐述为:
"假设S是T的子类型,t是T的对象,s是S的对象。如果q(t)能跑通,那么q(s)也应该能跑通。"
“Letq(x) be a property provable about objects x of type T. Then q(y) should be provable for objects y of type S,where S is a subtype of T.”
换句话说:
- 如果B是A的子类,
- A有一个方法f(),
- 如果b是B的实例,a是a的实例,
- 那么在不修改代码行为的前提下,所有能调用"a.f()"的地方,也应该能够调用“b.f()”。
让我们看一个不遵守LSP原则的继承的例子。
publicclassMyOrderedCollection { protectedList<Integer>list=newArrayList<>(); publicvoidaddElement(Integeri) { list.add(i); } publicIntegergetElement(Integerindex) { returnlist.get(index); } } publicclassMyOrderedAndSortedCollectionextendsMyOrderedCollection { // 重写 addElement将集合排序publicvoidaddElement(Integeri) { super.addElement(i); Collections.sort(super.list); } } publicclassExampleLSP1 { publicstaticvoidmain(Stringargs[]) { MyOrderedCollectioncollection1=newMyOrderedCollection(); MyOrderedCollectioncollection2=newMyOrderedAndSortedCollection(); inta=8, b=4; collection1.addElement(a); collection1.addElement(b); collection2.addElement(a); collection2.addElement(b); getAndPrintSecondElement(collection1); getAndPrintSecondElement(collection2); } publicstaticvoidgetAndPrintSecondElement(MyOrderedCollectioncollection) { System.out.println("The second element is :"+collection.getElement(1)); } }
代码的运行结果是:
The second element is :4 The second element is :8
这结果为什么是一个隐患?
假设开发者A写了MyOrderedCollection
。两年后的某一天,开发者B由于需要使用到排序后的集合,便令MyOrderedAndSortedCollection
继承了MyOrderedCollection
。由于是继承关系,所以能够将MyOrderedCollection
作为参数的函数,MyOrderedAndSortedCollection
也能作为参数使用。当其中一些函数就是要用到MyOrderedCollection
中的插入时顺序怎么办?
为了避免这种情况,开发者B需要检查所有使用了MyOrderedCollection
的遗留代码,并且确认它们是否会引用到MyOrderedAndSortedCollection
上。根据遗留代码的数量与复杂程度,这可能会花费好几周的时间。而且去动现存(在运行)的代码,并不是一件好事。
下列方式就是遵守LSP原则的一种可行方案:
publicinterfaceMyCollection { abstractpublicvoidaddElement(Integeri); abstractpublicIntegergetElement(Integerindex); } publicclassMyOrderedCollectionimplementsMyCollection { ... } publicclassMyOrderedAndSortedCollectionimplementsMyCollection { ... }
通过这种配置方式,MyOrderedCollection
与MyOrderedAndSortedCollection
就不一样了(即使他们实现了同一个接口):
- 一定要使用插入时顺序的代码就用
MyOrderedCollection
,它不会被MyOrderedAndSortedCollection
修改掉 - 一段使用了
MyCollection
的代码一定是不会受到插入顺序的影响的。所以它既可以是MyOrderedCollection
也可以是MyOrderedAndSortedCollection
这种模式可以被视作一种强行为子类型
原文链接
链接:Design Pattern: Liskov’s Substitution Principle (LSP) | Coding Geek (coding-geek.com)
译者是个菜鸟,初学Java,翻译仅为了提高提高自身阅读英文技术贴的能力。如有错误还望指正。
侵删。