整體和部分
還記得這條諺語嗎,“整體大於部分之和”?如果把一個個獨立的事件組合成一個相互作用的整體,產生的結果會比單個個體的作用之和要大得多。
程式也是一樣的道理。隨著一個個新方法被添加到程式中,整個程式可能的控制流程程迅速增加。對於大型程式而言,很快局面就會無法控制了。就象是一個荒謬而又不可思議的戲法,有時您得到的最終結果並不是您所期望的方向 ― 這同您在重載方法或者覆蓋方法時遇到的情況有些類似。
Broken Dispatch 錯誤模式
物件導向語言的最強大的特性之一就是繼承多態性。這一特性允許我們根據參數類型重載和覆蓋方法。但是,象其它功能強大的工具一樣,這個特性也會引入新的危險。
雖然 Java 程式員們很快就能學會管理一次調用中將調用哪個方法的規則,但在大型程式中卻很容易出現這種情況:在一個類中重載了一個方法,結果卻是以前在另一個類中可以啟動並執行代碼被中斷了。這樣的錯誤正符合我所說的 Broken Dispatch 模式。
該模式可以描述如下:
傳遞給某個重載方法,比如 foo 的參數,卻被傳給了另一個方法,比如 goo ,它支援更廣泛的參數類型。
goo 然後通過這些參數調用 foo 。
但是由於 goo 內的這些參數的靜態類型更為廣泛,因此,可能會調用方法 foo 的錯誤版本。
象這樣的錯誤很難診斷,因為可能只是添加了新的方法(而不是修改現有的方法)就引入了錯誤。而且,在發現問題之前,程式可能會繼續執行相當長的一段時間。
癥狀
為了說明這種模式的本質,讓我們來看看下面這段範例程式碼,它是為實現我前面的文章“ 空標誌錯誤模式”中的不可變列表而編寫的。
清單 1. 實現不可變列表
interface List {
public Object getFirst();
public List getRest();
}
class Empty implements List {
public Object getFirst() { throw new NoSuchElementException(); }
public List getRest() { throw new NoSuchElementException(); }
public boolean equals(Object that) {
return this.getClass() == that.getClass();
}
}
class Cons implements List {
Object first;
List rest;
Cons(Object _first) {
this.first = _first;
this.rest = new Empty();
}
Cons(Object _first, List _rest) {
this.first = _first;
this.rest = _rest;
}
public Object getFirst() { return this.first; }
public List getRest() { return this.rest; }
...
}