OO中,複用代碼可以有組合和繼承兩種方式,正如廣大人民羣衆所論述的,儘可能使用組合。
這裏我再不厭其煩的說明一下理由:
1. 組合比繼承在框架結構上要簡單,不會造成過深的繼承層次。
2. 組合是黑盒重用,抽象層次更高。
其實上面這兩條我覺得也沒啥,重點還是下面三條:
3.組合可以在運行時動態選擇複用的對象,而繼承不行。直接上圖,相信您看一眼就明白了
當Stack複用Sequence對象來實現自己的行爲時,只要是序列型容器就可以了,具體可以是Vector,List。
最重要的是,具體選擇Vector or List對象,可以在運行時由成員變量sequence的值決定,而變量的值是可以動態改變的
4. 使用繼承方式,子類必須被迫接收所有父類公有方法,但是實際上,有些方法子類是不需要的,甚至這些方法會對子類有害。
顯然,使用繼承的方式實現Stack,雖然push 和 pop有了正確的行爲,但是Vector的行爲在Stack中仍然是可以合法調用的。你可能覺得這些多餘的方法無關緊要,不去管他就可以了。但是別有用心的黑客呢,閒着蛋疼的某個屌絲呢?系統說不定就因此崩了
5. 當對象有多個行爲或屬性時,組合更容易複用。
(原諒我這裏私自把鴨改成雞,因爲我覺得這樣更能激發想象空間)
使用繼承的方式實現, 叫的行爲並不能被很好複用,“會叫不會飛的雞”與“會叫又會飛的雞” 在 叫的行爲上是相同的,但是這裏無法複用,只能在這兩個子類中分別實現“會叫”這個行爲。 如果客戶又要求你增加一個會叫又會舞的雞,你又得重複實現一遍“會叫”的行爲。
使用組合的方式就完全是"名副其實"了, 這裏複用,只是簡單的搭積木。這裏實際體現的是單一職責原則。
當然,組合的使用是有條件的,因爲是黑箱複用,所以當有些行爲無法通過對象的共有方法實現時,就只能依賴繼承(可以有保護方法,典型的如模板方法)。
另外,對於理由5,如果飛的行爲與雞本身耦合很大,其實現必須依賴於雞的私有或者保護成員,那麼也是不可能應用組合的。
無論如何,只要有條件,我們應儘可能使用組合。