?
? 寫在前面??
Composite組合模式屬于設計模式中比較熱門的一個,相信大家對它一定不像對訪問者模式那么陌生,畢竟誰又沒有遇到過樹形結構呢。不過所謂溫故而知新,我們還是從一個例子出發,起底一下這個模式吧。
? 一個簡單例子??
設想我們要建立一個公司的人事架構,在一個公司里,我們可以簡單地分為兩種員工,一種是經理(包括老板),另一種是基層員工,經理可以有下屬,而普通員工不行,我們寫出這樣的代碼。
基層員工類
這種員工是最基層的員工,沒有下屬
class?BasicLevelEmployee?//基層員工
{public?string?ID?{?get;?set;?}public?void?ShowStatus(int?indent){string?str?=?ID;str?=?str.PadLeft(ID.Length?+?indent,?'-');Console.WriteLine(str);}
}
經理類
經理可以有下屬,下屬可能是基層員工,也可能是其他經理(考慮老板這種情況,無疑其他經理也是老板的下屬),因為比基層員工多了下屬,所以也多了一些方法維護下屬屬性
class?Manager?//經理
{public?string?ID?{?get;?set;?}public?void?ShowStatus(int?indent)?{string?str?=?ID;????????????str?=?str.PadLeft(ID.Length?+?indent,?'-');Console.WriteLine(str);indent?+=?4;Subordinate.ForEach(s?=>?s.ShowStatus(indent));SubordinateManagers.ForEach(m?=>?m.ShowStatus(indent));}public?List<BasicLevelEmployee>?Subordinate?=?new?List<BasicLevelEmployee>();public?List<Manager>?SubordinateManagers?=?new?List<Manager>();//下面是經理所屬的方法public?void?AddSubordinate(BasicLevelEmployee?e)?{?Subordinate.Add(e);?}public?void?AddSubordinate(Manager?e)?{?SubordinateManagers.Add(e);?}public?void?RemoveSubordinate(BasicLevelEmployee?e)?{?Subordinate.Remove(e);?}public?void?RemoveSubordinate(Manager?e)?{?SubordinateManagers.Remove(e);?}??????
}
公司架構類
公司架構類非常簡單,只需要掌握最大的BOSS,整個公司人事架構都可以順藤摸瓜的展示出來
class?CompanyHierachy
{public?Manager?BOSS?{?get;?set;?}public?void?ShowStatus(){BOSS.ShowStatus(0);}
}
客戶端代碼
假設這個公司的結構很單純,除了老板就是開發部門和財務部門,各個部門分設經理是,所以我們寫出代碼如下
class?Program
{static?void?Main(string[]?args){//老板Manager?boss?=?new?Manager()?{?ID?=?"BOSS"?};//開發部門經理Manager?devManager?=?new?Manager()?{?ID?=?"Dev?Manager"?};//財務部門經理Manager?financeManager?=?new?Manager()?{?ID?=?"Finance?Manager"?};//開發組長Manager?devLead?=?new?Manager()?{?ID?=?"Dev?Lead"?};//測試組長Manager?qcLead?=?new?Manager()?{?ID?=?"QC?Lead"?};boss.AddSubordinate(devManager);boss.AddSubordinate(financeManager);financeManager.AddSubordinate(new?BasicLevelEmployee()?{?ID?=?"Purchase"?});devManager.AddSubordinate(devLead);devManager.AddSubordinate(qcLead);devLead.AddSubordinate(new?BasicLevelEmployee()?{?ID?=?"Developer1"?});devLead.AddSubordinate(new?BasicLevelEmployee()?{?ID?=?"Developer2"?});qcLead.AddSubordinate(new?BasicLevelEmployee()?{?ID?=?"QuanityControl1"?});qcLead.AddSubordinate(new?BasicLevelEmployee()?{?ID?=?"QuanityControl2"?});CompanyHierachy?company?=?new?CompanyHierachy()?{?CEO?=?boss?};company.ShowStatus();}
}
代碼非常簡單,不需要更多解釋了,運行后得到結果

一切正常,代碼是工作的,公司架構建立成功了。
? 再想一下??
但是想想,這樣的代碼真的好嗎?感覺起碼有兩個地方我們可以改進。
1. 基層員工和經理其實有太多的共性(屬性和方法),可以利用抽象思維,讓他們繼承自同一種東西嗎?
2. 在經理類中我們維護了多個下屬列表,如果以后再加一個實習生,是不是我們又得創建更多的列表?如果我們使用了繼承,這個問題還會存在嗎?
基于此,利用抽象思維讓經理和員工繼承自同一個類(雇員)勢在必行。在抽象之后,經理類會繼承自雇員并且也內含雇員列表,可能第一次見到這種包含自身父類列表的設計方式會讓人感覺不習慣,但不用擔心,這其實是一種比較常見的設計方式。這種既有繼承也有合成的結構,就是組合模式的精髓。
? 使用組合模式進行重構??
組合模式屬于結構型設計模式,它利用類型層級和聚合層級構造更大的復合結構
說的更加直白一點,當對象的局部結構和對象自身相同的情況下,我們可以使用繼承加上聚合的方式來組合代碼,比如剛剛提到的例子中

觀察一下,對于Boss來說,它的局部結構,即DevManager和FinanceManager與它自己的結構有何區別?都是樹結構,無非就是根節點不一樣而已,所以于情于理這一塊可以用繼承加聚合來重構。
那么心細的朋友肯定發現了,有些操作是經理類獨有的,這些操作我們是應該抽象到和基層員工共同的父類雇員類嗎?對于這個問題,一般有兩種解決方案。
透明型

在此設計中,子類方法的并集被提煉到了共有父類,哪怕這些方法對于某些子類根本不需要,這樣的好處是客戶端在使用的時候根本不需要知道對象糾結是哪個子類,對客戶端透明,所以得名。當前設計多采用這種。
安全型

安全型設計非常保守,只會提煉子類交集的方法到父類,這樣的好處是絕對安全,客戶端絕對不可能在BasicLevelEmployee對象上面調用AddSubordinate或者RemoveSubordinate。但有時候會面臨向下轉型的情況。
重構后的代碼(透明型)
抽象出共同父類雇員類,使用透明型,所有的子類方法都提煉到這個類
abstract?class?Employee
{public?string?ID?{?get;?set;?}public?abstract?void?ShowStatus(int?indent);//因為是透明型,所以基層員工用不上的方法也會被抽象到父類public?abstract?void?AddSubordinate(Employee?e);public?abstract?void?RemoveSubordinate(Employee?e);
}
對于基層員工,如果客戶端無意間調用了不該使用的方法,這基本是一個明確的、表明客戶端代碼出現了邏輯問題的信號,這種情況直接拋出異常,能更快地暴露出問題
class?BasicLevelEmployee?:?Employee
{public?override?void?ShowStatus(int?indent){string?str?=?ID;str?=?str.PadLeft(ID.Length?+?indent,?'-');Console.WriteLine(str);}public?override?void?AddSubordinate(Employee?e){throw?new?NotImplementedException();}public?override?void?RemoveSubordinate(Employee?e){throw?new?NotImplementedException();}
}
在經理類中,得益于共有父類Employee,我們可以用一個列表裝下所有的下屬,不論下屬是基層員工,還是經理,抑或是未來可能添加的實習生。畢竟他們都是雇員嘛
class?Manager?:?Employee
{public?override?void?ShowStatus(int?indent){string?str?=?ID;str?=?str.PadLeft(ID.Length?+?indent,?'-');Console.WriteLine(str);indent?+=?4;Subordinate.ForEach(s?=>?s.ShowStatus(indent));}public?List<Employee>?Subordinate?=?new?List<Employee>();//下面是經理所屬的方法public?override?void?AddSubordinate(Employee?e)?{?Subordinate.Add(e);?}public?override?void?RemoveSubordinate(Employee?e)?{?Subordinate.Remove(e);?}
}
公司架構類和客戶端代碼調用保持不變,運行結果一致,重構成功。
可以看到,在使用了組合模式之后,現在的代碼不但消除了冗余(不用再去維護多個下屬列表),也更具有抵御未來變化的能力,這樣的結構比起原來,當然是更加合理的。這就是結構型設計模式的用武之地,讓對象的結構更加的合理,更加的易于擴展。
這就是關于Composite組合模式的介紹,鑒于筆者能力有限,如果大家對于這篇文章中所講有其他看法,歡迎留言討論。
> 作者:老胡寫代碼
> 原文:https://www.cnblogs.com/deatharthas/p/16390116.html