組合or繼承

面向對象設計有一個原則「優先使用對象組合,而不是繼承」。學習

下面是二者優缺點的比較:spa

組 合 關 系設計

繼 承 關 系code

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

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

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

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

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

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

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

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

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

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

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

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

  

咱們能夠發現繼承的缺點遠遠多於優勢,儘管繼承在學習OOP的過程當中獲得了大量的強調,但並不意味着應該儘量地處處使用它。

相反,使用它時要特別慎重。

只有在清楚知道繼承在全部方法中最有效的前提下,纔可考慮它。

 繼承最大的優勢就是擴展簡單,但大多數缺點都很致命,可是由於這個擴展簡單的優勢太明顯了,不少人並不深刻思考,因此形成了太多問題。

雖然筆者的平常工做已經遭遇了太多的Legacy Code由於繼承而致使的問題,可是在寫新代碼時,仍然容易陷入繼承而沒法自拔。

最近在寫得一分新代碼:

 1     public interface IEntity{}
 5     
 6     public interface IEntityContainer
 7     {
 8         string Name { get;}
 9         int EntityId { get;}
10         IList<IEntity> EntityContainer{ get;}
11     }
12     
13     public class XEntityContainer : IEntityContainer
14     {
15         public XEntityContainer()
16         {
17             //Name = ...;
18             //EntityId = ...;
19             //EntityContainer = ...;
20         }
21         
22         public string Name{get; private set;}
23         public int EntityId{get; private set;}
24         public IList<IEntity> EntityContainer{ get; private set;}
25     }
26     
27     public class YEntityContainer : IEntityContainer
28     {
29         public YEntityContainer()
30         {
31             //Name = ...;
32             //EntityId = ...;
33             //EntityContainer = ...;
34         }
35         
36         public string Name { get;private set;}
37         public int EntityId { get;private set;}
38         public IList<IEntity> EntityContainer{ get; private set;}
39     }

 

Code Review時以爲若是全部的子類都會包含這三行的話,實際上是有些duplication的:

1 public string Name { get;private set;}
2 public int EntityId { get;private set;}
3 public IList<IEntity> EntityContainer{ get; private set;}

因此就建議使用組合的方式引入一個新的類型來提供這幾項信息。

但,跟着直覺就把Code寫成了這樣子:

 1     public interface IEntity{}
 5     
 6     public interface IEntityContainer
 7     {
 8         string Name { get;}
 9         int EntityId { get;}
10         IList<IEntity> EntityContainer{ get;}
11     }
12     
13     public class EntityContainerBase : IEntityContainer
14     {
15         public string Name{get; protected set;}
16         public int EntityId{get; protected set;}
17         public IList<IEntity> EntityContainer{ get; protected set;}
18     }
19     
20     public class XEntityContainer : EntityContainerBase
21     {
22         public XEntityContainer()
23         {
24             //Name = ...;
25             //EntityId = ...;
26             //EntityContainer = ...;
27         }
28     }
29     
30     public class YEntityContainer : EntityContainerBase
31     {
32         public YEntityContainer()
33         {
34             //Name = ...;
35             //EntityId = ...;
36             //EntityContainer = ...;
37         }
38     }

就這樣一個好好的二層繼承,好好的interface繼承,被掰成了三層結構。

比較「壞」的是這種潛意識裏依然把繼承依然當成了第一選擇。

根據同一項目裏已有的一段新Code,能夠知道未來有些utility方法確定會不斷地往EntityContainerBase里加,直到有一天把它變成Legacy...

我更加傾向於咱們應該這樣改:

 1     public interface IEntity{}
 5     
 6     public struct Container
 7     {
 8         public string Name{get;set;};
 9         public int EntityId{get;set;};
10         public IList<IEntity> EntityList{get;set;};
11     }
12     
13     public interface IEntityContainer
14     {
15         string Name { get;}
16         int EntityId { get;}
17         IList<IEntity> EntityContainer{ get;}
18     }
19     
20     public class XEntityContainer : IEntityContainer
21     {
22         public XEntityContainer()
23         {
24             //m_Container.Name = ...;
25             //m_Container.EntityId = ...;
26             //m_Container.EntityList = ...;
27         }
28         
29         public string Name { get{return m_Container.Name;}}
30         public int EntityId { get{return m_Container.EntityId;};}
31         public IList<IEntity> EntityContainer{ get{return m_Container.EntityList;};}
32         
33         private Container m_Container;
34     }
35     
36     public class YEntityContainer : IEntityContainer
37     {
38         public YEntityContainer()
39         {
40             //m_Container.Name = ...;
41             //m_Container.EntityId = ...;
42             //m_Container.EntityList = ...;
43         }
44         
45         public string Name { get{return m_Container.Name;}}
46         public int EntityId { get{return m_Container.EntityId;};}
47         public IList<IEntity> EntityContainer{ get{return m_Container.EntityList;};}
48         
49         private Container m_Container;
50     }

多麼漂亮的二層結構,沒有任何Base類,未來的公共方法,Utility方法不會「無腦」的往Base裏塞。

相關文章
相關標籤/搜索