OOP组合和继续的优缺点
—— 詳解繼承與組合的優缺點
組合與繼承都是提高代碼可重用性的手段。在設計對象模型時,可以按照語義來識別類之間的組合關系和繼承關系。在有些情況下,采用組合關系或者繼承關系能完成同樣的任務,組合和繼承存在著對應關系:組合中的整體類和繼承中的子類對應,組合中的局部類和繼承中的父類對應,如下圖:
組合:繼承:
一、基礎知識
我們先用代碼幫大家來理解一下組合和繼承:
1、對于已經存在Parent類時需要擴展其方法時
結構圖:
繼承代碼:
?
代碼 class?Parent????{
????????public?void?Method1()?
????????{?
????????}
????????public?void?Method2()?{?}
????????public?void?Method3()?{?}
????}
?class?Child1:Parent
????{
????????public?void?MethodA()?{?}
????}
?class?Child2:Parent
????{
????????public?void?MethodA()?{?}
????}
?
組合代碼:
?
代碼 class?ComponentA????{
????????Parent?p?=?new?Parent();
????????public?void?Method2()
????????{
????????????p.Method2();
????????}
????????public?void?MethodA()
????????{
????????}
????}
?class?ComponentB
????{
????????Parent?p?=?new?Parent();
????????public?void?Method2()?{
????????????p.Method2();
????????}
????????public?void?MethodB()
????????{?
????????}
????}
?
2、如果發現兩個類具有很多代碼相同的類需要抽象時,如下圖A,B兩個類,這兩個類中method1,和method3兩個方法代碼相同
繼承:
實現代碼:
?
代碼 class?A:C????{
????????public?void?MethodB()?{?}
????????public?override?void?Method2()
????????{
????????????
????????}
????}
????class?B:C
????{
????????public?override?void?Method2()
????????{
????????????
????????}
????????public?void?MethodB()?{?}
????}
????class?C
????{
????????public?void?Method1()?{?}
????????public?virtual?void?Method2()?{?}
????????public?void?Method3()?{?}
????}
?
?
組合:
實現代碼:
?
代碼 class?A????{
????????public?void?MethodA()?{?}
????????C?c?=?new?C();
????????public?void?Method1()
????????{
????????????c.Method1();
????????}
????????public?void?Method2()?{?}
????}
????class?B
????{
????????public?void?MethodB()?{?}
????????public?void?Method2()?{?}
????}
????class?C
????{
????????public?void?Method1()?{?}
????????public?void?Method2()?{?}
????????public?void?Method3()?{?}
????}
?
二、繼承與組合的優缺點
?
| 合 關 系 | 繼 承 關 系 |
| 優點:不破壞封裝,整體類與局部類之間松耦合,彼此相對獨立 | 缺點:破壞封裝,子類與父類之間緊密耦合,子類依賴于父類的實現,子類缺乏獨立性 |
| 優點:具有較好的可擴展性 | 缺點:支持擴展,但是往往以增加系統結構的復雜度為代價 |
| 優點:支持動態組合。在運行時,整體對象可以選擇不同類型的局部對象 | 缺點:不支持動態繼承。在運行時,子類無法選擇不同的父類 |
| 優點:整體類可以對局部類進行包裝,封裝局部類的接口,提供新的接口 | 缺點:子類不能改變父類的接口 |
| 缺點:整體類不能自動獲得和局部類同樣的接口 | 優點:子類能自動繼承父類的接口 |
| 缺點:創建整體類的對象時,需要創建所有局部類的對象 | 優點:創建子類的對象時,無須創建父類的對象 |
?
1、為什么繼承破壞封裝性:
?
鴨子中不想要“飛”的方法,但因為繼承無法封裝這個無用的“飛”方法 。
2、為什么繼承緊耦合:
?
當作為父類的BaseTable中感覺Insert這個名字不合適時,如果希望將其修改成Create方法,那使用了子類對象Insert方法將會編譯出錯,可能你會覺得這改起來還算容易,因為有重構工具一下子就好了并且編譯錯誤改起來很容易。但如果BaseTable和子類在不同的程序集中,維護的人員不同,BaseTable程序集升級,那本來能用的代碼忽然不能用了,這還是很難讓人接受的
3、為什么繼承擴展起來比較復雜
當圖書和數碼的算稅方式和數碼產品一樣時,而消費類產品的算稅方式是另一樣時,如果采用繼承方案可能會演變成如下方式:
這樣如果產品繼續增加,算稅方式繼續增加,那繼承的層次會非常復雜,而且很難控制,而使用組合就能很好的解決這個問題,參見:面向對象思想的頭腦風暴(一)
4、繼承不能支持動態繼承
這個其實很好理解,因為繼承是編譯期就決定下來的,無法在運行時改變,如3例中,如果用戶需要根據當地的情況選擇計稅方式,使用繼承就解決不了,而使用組合結合反射就能很好的解決。
5、為什么繼承?子類不能改變父類接口
如2中的圖
子類中覺得Insert方法不合適,希望使用Create方法,因為繼承的原因無法改變
?
三、如何使用繼承
?
1、精心設計專門用于被繼承的類,繼承樹的抽象層應該比較穩定,一般不要多于三層。
2、對于不是專門用于被繼承的類,禁止其被繼承。
3、優先考慮用組合關系來提高代碼的可重用性。
4、子類是一種特殊的類型,而不只是父類的一個角色
5、子類擴展,而不是覆蓋或者使父類的功能失效
?
?
四、組合的缺點:
1、整體類不能自動獲得和局部類同樣的接口
如果父類的方法子類中幾乎都要暴露出去,這時可能會覺得使用組合很不方便,使用繼承似乎更簡單方便。但從另一個角度講,實際上也許子類中并不需要暴露這些方法,客戶端組合應用就可以了。所以上邊推薦不要繼承那些不是為了繼承而設計的類,一般為了繼承而設計的類都是抽象類。
2、創建整體類的對象時,需要創建所有局部類的對象
這個可能沒什么更好的辦法,但在實際應用中并沒有多出多少代碼。
五、相關原則
1、里氏代換原則(LSP)(以下轉自http://www.cnblogs.com/zhenyulu/articles/36061.html)
Liskov Substitution Principle(里氏代換原則):子類型(subtype)必須能夠替換它們的基類型。
?
白馬、黑馬
?
反過來的代換不成立
《墨子·小取》說:"娣,美人也,愛娣,非愛美人也……"娣便是妹妹,哥哥喜愛妹妹,是因為兩人是兄妹關系,而不是因為妹妹是個美人。因此,喜愛妹妹不等同于喜愛美人。用面向對象語言描述,美人是基類,妹妹是美人的子類。哥哥作為一個有"喜愛()"方法,接受妹妹作為參數。那么,這個"喜愛()"方法一般不能接受美人的實例。
?
?
下邊那個長方形正方形的例子我就不轉了,大家可以到上邊那個博客地址中了解。
2、合成/聚合復用原則(CARP)(以下轉自http://www.cnblogs.com/zhenyulu/articles/36068.html)
合成/聚合復用原則(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的過程中得到了大量的強調,但并不意味著應該盡可能地到處使用它。相反,使用它時要特別慎重。只有在清楚知道繼承在所有方法中最有效的前提下,才可考慮它。?繼承最大的優點就是擴展簡單,但大多數缺點都很致命,但是因為這個擴展簡單的優點太明顯了,很多人并不深入思考,所以造成了太多問題,希望這篇文章能引發你一些思考。
轉載于:https://www.cnblogs.com/keyyang/p/3941548.html
總結
以上是生活随笔為你收集整理的OOP组合和继续的优缺点的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Choose unique values
- 下一篇: silverlight导出excel