組合與繼承都是提高代碼可重用性的手段。在設計對象模型時,可以按照語義來識别類之間的組合關系和繼承關系。在有些情況下,采用組合關系或者繼承關系能完成同樣的任務,組合和繼承存在着對應關系:組合中的整體類和繼承中的子類對應,組合中的局部類和繼承中的父類對應,如下圖:
組合:繼承:
一、基礎知識
我們先用代碼幫大家來了解一下組合和繼承:
1、對于已經存在Parent類時需要擴充其方法時
結構圖:
繼承代碼:
代碼
組合代碼:
2、如果發現兩個類具有很多代碼相同的類需要抽象時,如下圖A,B兩個類,這兩個類中method1,和method3兩個方法代碼相同
繼承:
實作代碼:
組合:
二、繼承與組合的優缺點
合 關 系
繼 承 關 系
優點:不破壞封裝,整體類與局部類之間松耦合,彼此相對獨立
缺點:破壞封裝,子類與父類之間緊密耦合,子類依賴于父類的實作,子類缺乏獨立性
優點:具有較好的可擴充性
缺點:支援擴充,但是往往以增加系統結構的複雜度為代價
優點:支援動态組合。在運作時,整體對象可以選擇不同類型的局部對象
缺點:不支援動态繼承。在運作時,子類無法選擇不同的父類
優點:整體類可以對局部類進行包裝,封裝局部類的接口,提供新的接口
缺點:子類不能改變父類的接口
缺點:整體類不能自動獲得和局部類同樣的接口
優點:子類能自動繼承父類的接口
缺點:建立整體類的對象時,需要建立所有局部類的對象
優點:建立子類的對象時,無須建立父類的對象
1、為什麼繼承破壞封裝性:
鴨子中不想要“飛”的方法,但因為繼承無法封裝這個無用的“飛”方法 。
2、為什麼繼承緊耦合:
當作為父類的BaseTable中感覺Insert這個名字不合适時,如果希望将其修改成Create方法,那使用了子類對象Insert方法将會編譯出錯,可能你會覺得這改起來還算容易,因為有重構工具一下子就好了并且編譯錯誤改起來很容易。但如果BaseTable和子類在不同的程式集中,維護的人員不同,BaseTable程式集更新,那本來能用的代碼忽然不能用了,這還是很難讓人接受的
3、為什麼繼承擴充起來比較複雜
當圖書和數位的算稅方式和數位産品一樣時,而消費類産品的算稅方式是另一樣時,如果采用繼承方案可能會演變成如下方式:
4、繼承不能支援動态繼承
這個其實很好了解,因為繼承是編譯期就決定下來的,無法在運作時改變,如3例中,如果使用者需要根據當地的情況選擇計稅方式,使用繼承就解決不了,而使用組合結合反射就能很好的解決。
5、為什麼繼承 子類不能改變父類接口
如2中的圖
子類中覺得Insert方法不合适,希望使用Create方法,因為繼承的原因無法改變
三、如何使用繼承
1、精心設計專門用于被繼承的類,繼承樹的抽象層應該比較穩定,一般不要多于三層。
2、對于不是專門用于被繼承的類,禁止其被繼承。
3、優先考慮用組合關系來提高代碼的可重用性。
4、子類是一種特殊的類型,而不隻是父類的一個角色
5、子類擴充,而不是覆寫或者使父類的功能失效
四、組合的缺點:
1、整體類不能自動獲得和局部類同樣的接口
如果父類的方法子類中幾乎都要暴露出去,這時可能會覺得使用組合很不友善,使用繼承似乎更簡單友善。但從另一個角度講,實際上也許子類中并不需要暴露這些方法,用戶端組合應用就可以了。是以上邊推薦不要繼承那些不是為了繼承而設計的類,一般為了繼承而設計的類都是抽象類。
2、建立整體類的對象時,需要建立所有局部類的對象
這個可能沒什麼更好的辦法,但在實際應用中并沒有多出多少代碼。
五、相關原則
Liskov Substitution Principle(裡氏代換原則):子類型(subtype)必須能夠替換它們的基類型。
白馬、黑馬
反過來的代換不成立
《墨子·小取》說:"娣,美人也,愛娣,非愛美人也……"娣便是妹妹,哥哥喜愛妹妹,是因為兩人是兄妹關系,而不是因為妹妹是個美人。是以,喜愛妹妹不等同于喜愛美人。用面向對象語言描述,美人是基類,妹妹是美人的子類。哥哥作為一個有"喜愛()"方法,接受妹妹作為參數。那麼,這個"喜愛()"方法一般不能接受美人的執行個體。
下邊那個長方形正方形的例子我就不轉了,大家可以到上邊那個部落格位址中了解。
合成/聚合複用原則(Composite/Aggregate Reuse Principle或CARP)經常又叫做合成複用原則(Composite Reuse Principle或CRP),就是在一個新的對象裡面使用一些已有的對象,使之成為新對象的一部分;新對象通過向這些對象的委派達到複用已有功能的目的。
簡而言之,要盡量使用合成/聚合,盡量不要使用繼承。
o Design to interfaces.
o Favor composition over inheritance.
o Find what varies and encapsulate it.
(摘自:Design Patterns Explained)
區分"Has-A"與"Is-A"
"Is-A"是嚴格的分類學意義上定義,意思是一個類是另一個類的"一種"。而"Has-A"則不同,它表示某一個角色具有某一項責任。
導緻錯誤的使用繼承而不是合成/聚合的一個常見的原因是錯誤的把"Has-A"當作"Is-A"。
例如:
實際上,雇員、經理、學生描述的是一種角色,比如一個人是"經理"必然是"雇員",另外一個人可能是"學生雇員",在上面的設計中,一個人無法同時擁有多個角色,是"雇員"就不能再是"學生"了,這顯然是不合理的。
錯誤源于把"角色"的等級結構與"人"的等級結構混淆起來,誤把"Has-A"當作"Is-A"。解決辦法:
總結:
根據我們前面講的内容我們可以發現繼承的缺點遠遠多于優點,盡管繼承在學習OOP的過程中得到了大量的強調,但并不意味着應該盡可能地到處使用它。相反,使用它時要特别慎重。隻有在清楚知道繼承在所有方法中最有效的前提下,才可考慮它。 繼承最大的優點就是擴充簡單,但大多數缺點都很緻命,但是因為這個擴充簡單的優點太明顯了,很多人并不深入思考,是以造成了太多問題,希望這篇文章能引發你一些思考。
參考:
<a href="http://blog.csdn.net/Cpp_Java_Man/archive/2006/05/02/705279.aspx" target="_blank">http://blog.csdn.net/Cpp_Java_Man/archive/2006/05/02/705279.aspx</a>
<a href="http://blog.csdn.net/zjliu1984/archive/2009/06/26/4299657.aspx" target="_blank">http://blog.csdn.net/zjliu1984/archive/2009/06/26/4299657.aspx</a>
<a href="http://www.cnblogs.com/zhenyulu/category/6930.html" target="_blank">http://www.cnblogs.com/zhenyulu/category/6930.html</a>
<a href="http://www.cnblogs.com/bluedy1229/archive/2008/11/19/1286692.html" target="_blank">http://www.cnblogs.com/bluedy1229/archive/2008/11/19/1286692.html</a>
本文轉自 你聽海是不是在笑 部落格園部落格,原文連結:http://www.cnblogs.com/nuaalfm/archive/2010/04/23/1718453.html ,如需轉載請自行聯系原作者