組合與繼承是利用其它現存類定義新類的兩個方法。如果你接下來的工作主要是代碼重用,通常你應 採用組合而不是繼承。只有繼承受脆基類問題之苦,這種情況你可能會無意中通過改變超類而破壞了子 類。
關於繼承關係你可以問自己一個問題,是否它建模了一個is-a關係。Meyers,《Effective C++》 【 Mey91】例如,說ArrayElement是Element是合理的。你能問的另一個問題是,是否客戶想要把子類類型 當作超類類型來用。Eckel,《Thinking in Java》【Eck98】在ArrayElement的例子裡,我們的確期待 客戶會想要把ArrayElement當作Element使用。
如果你對展示在圖釋10.3的繼承關係問了這些的問題,那麼是否感覺其中的任何關係有可疑嗎?尤其 是,對你來說LineElement是ArrayElement是否顯而易見呢?你是否認為客戶會需要把LineElement當作 ArrayElement使用?實際上,我們把LineElement定義為ArrayElement主要是想重用ArrayElement的 contents定義。因此或許把LineElement定義為Element的直接子類會更好一些,就像這樣:
class LineElement(s: String) extends Element { val contents = Array(s) override def width = s.length override def height = 1}
前一個版本中,LineElement與ArrayElement有一個繼承關係,從那裡繼承了contents。現在它與 Array有一個組合關係:在它自己的contents欄位中持有一個字串數組的引用。類ArrayElement也與 Array有組合關係,因為它的參數化contents欄位持有字串數組的引用。ArrayElement的代碼展示在第xx 頁的代碼10.5中。其組合關係用一個菱形表現在類圖中,正如展示在第xx頁的圖釋10.1中那樣。有了 LineElement的這個實現,Element的繼承層級現在看上去如展示在圖釋10.4中那樣。
圖釋 10.4 修改了LineElement後的類層級