前面我們說(shuō)過(guò),構(gòu)建新類(lèi)的兩個(gè)基本方法是組合和繼承,如果你的主要目的是代碼重用,那么最好使用組合的方法構(gòu)造新類(lèi),使用繼承的方法構(gòu)造新類(lèi)造成的可能問(wèn)題是,無(wú)意的修改基類(lèi)可能會(huì)破壞子類(lèi)的實(shí)現(xiàn)。
關(guān)于繼承關(guān)系你可以問(wèn)自己一個(gè)問(wèn)題,是否它建模了一個(gè) is-a 關(guān)系。例如,說(shuō) ArrayElement 是 Element 是合理的。你能問(wèn)的另一個(gè)問(wèn)題是,是否客戶想要把子類(lèi)類(lèi)型當(dāng)作基類(lèi)類(lèi)型來(lái)用。
前一個(gè)版本中,LineElement 與 ArrayElement 有一個(gè)繼承關(guān)系,從那里繼承了 contents?,F(xiàn)在它在 ArrayElement 的例子里,我們的確期待客戶會(huì)想要把 ArrayElement 當(dāng)作 Element 使用。
請(qǐng)看下面的類(lèi)層次關(guān)系圖:
http://wiki.jikexueyuan.com/project/scala-development-guide/images/14.png" alt="" />
看著這張圖,問(wèn)問(wèn)上面的問(wèn)題,是否感覺(jué)其中的任何關(guān)系有可疑嗎?尤其是,對(duì)你來(lái)說(shuō) LineElement 是 ArrayElement 是否顯而易見(jiàn)呢?你是否認(rèn)為客戶會(huì)需要把 LineElement 當(dāng)作 ArrayElement 使用?實(shí)際上,我們把 LineElement 定義為 ArrayElement 主要是想重用 ArrayElement 的 contents 定義。因此或許把 LineElement 定義為 Element 的直接子類(lèi)會(huì)更好一些,就像這樣:
class LineElement(s: String) extends Element {
val contents = Array(s)
override def width = s.length
override def height = 1
}
前一個(gè)版本中,LineElemen t與 ArrayElement 有一個(gè)繼承關(guān)系,從那里繼承了 contents?,F(xiàn)在它與 Array 有一個(gè)組合關(guān)系:在它自己的 contents 字段中持有一個(gè)字串?dāng)?shù)組的引用。有了 LineElement 的這個(gè)實(shí)現(xiàn),Element 的繼承層級(jí)現(xiàn)在如下圖所示:
http://wiki.jikexueyuan.com/project/scala-development-guide/images/15.png" alt="" />
因此在選用組合還是通過(guò)繼承來(lái)構(gòu)造新類(lèi)時(shí),需要根據(jù)需要選擇合適的方法。