譯文
作為一個Java開發者,我從沒有聽過LSP模式。我隻在讀一些C++相關的東西的時候聽說過這種模式。這非常奇怪,因為這種模式有時候被當作面向對象程式設計的五大原則之一。
這個原則是 Barbara Liskov于1987年第一次提出,并在1994年闡述為:
"假設S是T的子類型,t是T的對象,s是S的對象。如果q(t)能跑通,那麼q(s)也應該能跑通。"
“Let q(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原則的繼承的例子。
public class MyOrderedCollection {
protected List<Integer> list = new ArrayList<>();
public void addElement(Integer i) {
list.add(i);
}
public Integer getElement(Integer index) {
return list.get(index);
}
}
public class MyOrderedAndSortedCollection extends MyOrderedCollection {
// 重寫 addElement将集合排序
public void addElement(Integer i) {
super.addElement(i);
Collections.sort(super.list);
}
}
public class ExampleLSP1 {
public static void main(String args[]) {
MyOrderedCollection collection1 = new MyOrderedCollection();
MyOrderedCollection collection2 = new MyOrderedAndSortedCollection();
int a = 8, b = 4;
collection1.addElement(a);
collection1.addElement(b);
collection2.addElement(a);
collection2.addElement(b);
getAndPrintSecondElement(collection1);
getAndPrintSecondElement(collection2);
}
public static void getAndPrintSecondElement(MyOrderedCollection collection) {
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原則的一種可行方案:
public interface MyCollection {
abstract public void addElement(Integer i);
abstract public Integer getElement(Integer index);
}
public class MyOrderedCollection implements MyCollection {
...
}
public class MyOrderedAndSortedCollection implements MyCollection {
...
}
通過這種配置方式,
MyOrderedCollection
與
MyOrderedAndSortedCollection
就不一樣了(即使他們實作了同一個接口):
- 一定要使用插入時順序的代碼就用
,它不會被MyOrderedCollection
修改掉MyOrderedAndSortedCollection
- 一段使用了
的代碼一定是不會受到插入順序的影響的。是以它既可以是MyCollection
也可以是MyOrderedCollection
MyOrderedAndSortedCollection
這種模式可以被視作一種強行為子類型
原文連結
連結:Design Pattern: Liskov’s Substitution Principle (LSP) | Coding Geek (coding-geek.com)
譯者是個菜鳥,初學Java,翻譯僅為了提高提高自身閱讀英文技術貼的能力。如有錯誤還望指正。
侵删。