OOP組合和繼續的優缺點 —— 詳解繼承與組合的優缺點

—— 詳解繼承與組合的優缺點

 

組合與繼承都是提升代碼可重用性的手段。在設計對象模型時,能夠按照語義來識別類之間的組合關係和繼承關係。在有些狀況下,採用組合關係或者繼承關係能完成一樣的任務,組合和繼承存在着對應關係:組合中的總體類和繼承中的子類對應,組合中的局部類和繼承中的父類對應,以下圖:html

組合:繼承:ide

1、基礎知識工具

咱們先用代碼幫你們來理解一下組合和繼承:post

一、對於已經存在Parent類時須要擴展其方法時學習

結構圖:url

繼承代碼:spa

 

代碼
複製代碼
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()
        { 
        }
    }
複製代碼

 

二、若是發現兩個類具備不少代碼相同的類須要抽象時,以下圖A,B兩個類,這兩個類中method1,和method3兩個方法代碼相同3d

繼承:code

實現代碼:

 

代碼
複製代碼
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() { }
    }
複製代碼

 

2、繼承與組合的優缺點

 

合 關 系

繼 承 關 系

優勢:不破壞封裝,總體類與局部類之間鬆耦合,彼此相對獨立

缺點:破壞封裝,子類與父類之間緊密耦合,子類依賴於父類的實現,子類缺少獨立性

優勢:具備較好的可擴展性

缺點:支持擴展,可是每每以增長系統結構的複雜度爲代價

優勢:支持動態組合。在運行時,總體對象能夠選擇不一樣類型的局部對象

缺點:不支持動態繼承。在運行時,子類沒法選擇不一樣的父類

優勢:總體類能夠對局部類進行包裝,封裝局部類的接口,提供新的接口

缺點:子類不能改變父類的接口

缺點:總體類不能自動得到和局部類一樣的接口

優勢:子類能自動繼承父類的接口

缺點:建立總體類的對象時,須要建立全部局部類的對象

優勢:建立子類的對象時,無須建立父類的對象

 

一、爲何繼承破壞封裝性:

 

鴨子中不想要「飛」的方法,但由於繼承沒法封裝這個無用的「飛」方法 。

二、爲何繼承緊耦合:

 

看成爲父類的BaseTable中感受Insert這個名字不合適時,若是但願將其修改爲Create方法,那使用了子類對象Insert方法將會編譯出錯,可能你會以爲這改起來還算容易,由於有重構工具一會兒就行了而且編譯錯誤改起來很容易。但若是BaseTable和子類在不一樣的程序集中,維護的人員不一樣,BaseTable程序集升級,那原本能用的代碼突然不能用了,這仍是很難讓人接受的

三、爲何繼承擴展起來比較複雜

當圖書和數碼的算稅方式和數碼產品同樣時,而消費類產品的算稅方式是另外一樣時,若是採用繼承方案可能會演變成以下方式:

這樣若是產品繼續增長,算稅方式繼續增長,那繼承的層次會很是複雜,並且很難控制,而使用組合就能很好的解決這個問題,參見:面向對象思想的頭腦風暴(一)

四、繼承不能支持動態繼承

這個其實很好理解,由於繼承是編譯期就決定下來的,沒法在運行時改變,如3例中,若是用戶須要根據當地的狀況選擇計稅方式,使用繼承就解決不了,而使用組合結合反射就能很好的解決。

五、爲何繼承 子類不能改變父類接口

如2中的圖

子類中以爲Insert方法不合適,但願使用Create方法,由於繼承的緣由沒法改變

 

3、如何使用繼承

 

一、精心設計專門用於被繼承的類,繼承樹的抽象層應該比較穩定,通常不要多於三層。

二、對於不是專門用於被繼承的類,禁止其被繼承。

三、優先考慮用組合關係來提升代碼的可重用性。

四、子類是一種特殊的類型,而不僅是父類的一個角色

五、子類擴展,而不是覆蓋或者使父類的功能失效

 

 

4、組合的缺點:

一、總體類不能自動得到和局部類一樣的接口

若是父類的方法子類中幾乎都要暴露出去,這時可能會以爲使用組合很不方便,使用繼承彷佛更簡單方便。但從另外一個角度講,實際上也許子類中並不須要暴露這些方法,客戶端組合應用就能夠了。因此上邊推薦不要繼承那些不是爲了繼承而設計的類,通常爲了繼承而設計的類都是抽象類。

二、建立總體類的對象時,須要建立全部局部類的對象

這個可能沒什麼更好的辦法,但在實際應用中並無多出多少代碼。

5、相關原則

一、里氏代換原則(LSP)(如下轉自http://www.cnblogs.com/zhenyulu/articles/36061.html

Liskov Substitution Principle(里氏代換原則):子類型(subtype)必須可以替換它們的基類型。

 

白馬、黑馬
 

反過來的代換不成立
《墨子·小取》說:"娣,美人也,愛娣,非愛漂亮人也……"娣即是妹妹,哥哥喜好妹妹,是由於兩人是兄妹關係,而不是由於妹妹是個美人。所以,喜好妹妹不等同於喜好美人。用面嚮對象語言描述,美人是基類,妹妹是美人的子類。哥哥做爲一個有"喜好()"方法,接受妹妹做爲參數。那麼,這個"喜好()"方法通常不能接受美人的實例。

 

 

下邊那個長方形正方形的例子我就不轉了,你們能夠到上邊那個博客地址中瞭解。

二、合成/聚合複用原則(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的過程當中獲得了大量的強調,但並不意味着應該儘量地處處使用它。相反,使用它時要特別慎重。只有在清楚知道繼承在全部方法中最有效的前提下,纔可考慮它。 繼承最大的優勢就是擴展簡單,但大多數缺點都很致命,可是由於這個擴展簡單的優勢太明顯了,不少人並不深刻思考,因此形成了太多問題,但願這篇文章能引起你一些思考。

相關文章
相關標籤/搜索