C#中的composite模式示例詳解
寫在前面
Composite組合模式屬于設(shè)計(jì)模式中比較熱門的一個,相信大家對它一定不像對訪問者模式那么陌生,畢竟誰又沒有遇到過樹形結(jié)構(gòu)呢。不過所謂溫故而知新,我們還是從一個例子出發(fā),起底一下這個模式吧。
一個簡單例子
設(shè)想我們要建立一個公司的人事架構(gòu),在一個公司里,我們可以簡單地分為兩種員工,一種是經(jīng)理(包括老板),另一種是基層員工,經(jīng)理可以有下屬,而普通員工不行,我們寫出這樣的代碼。
基層員工類
這種員工是最基層的員工,沒有下屬
class BasicLevelEmployee //基層員工
{
public string ID { get; set; }
public void ShowStatus(int indent)
{
string str = ID;
str = str.PadLeft(ID.Length + indent, '-');
Console.WriteLine(str);
}
}經(jīng)理類
經(jīng)理可以有下屬,下屬可能是基層員工,也可能是其他經(jīng)理(考慮老板這種情況,無疑其他經(jīng)理也是老板的下屬),因?yàn)楸然鶎訂T工多了下屬,所以也多了一些方法維護(hù)下屬屬性
class Manager //經(jīng)理
{
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>();
//下面是經(jīng)理所屬的方法
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); }
}
公司架構(gòu)類
公司架構(gòu)類非常簡單,只需要掌握最大的BOSS,整個公司人事架構(gòu)都可以順藤摸瓜的展示出來
class CompanyHierachy
{
public Manager BOSS { get; set; }
public void ShowStatus()
{
BOSS.ShowStatus(0);
}
}客戶端代碼
假設(shè)這個公司的結(jié)構(gòu)很單純,除了老板就是開發(fā)部門和財(cái)務(wù)部門,各個部門分設(shè)經(jīng)理是,所以我們寫出代碼如下
class Program
{
static void Main(string[] args)
{
//老板
Manager boss = new Manager() { ID = "BOSS" };
//開發(fā)部門經(jīng)理
Manager devManager = new Manager() { ID = "Dev Manager" };
//財(cái)務(wù)部門經(jīng)理
Manager financeManager = new Manager() { ID = "Finance Manager" };
//開發(fā)組長
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();
}
}代碼非常簡單,不需要更多解釋了,運(yùn)行后得到結(jié)果

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

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

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

安全型設(shè)計(jì)非常保守,只會提煉子類交集的方法到父類,這樣的好處是絕對安全,客戶端絕對不可能在BasicLevelEmployee對象上面調(diào)用AddSubordinate或者RemoveSubordinate。但有時候會面臨向下轉(zhuǎn)型的情況。
重構(gòu)后的代碼(透明型)
抽象出共同父類雇員類,使用透明型,所有的子類方法都提煉到這個類
abstract class Employee
{
public string ID { get; set; }
public abstract void ShowStatus(int indent);
//因?yàn)槭峭该餍?,所以基層員工用不上的方法也會被抽象到父類
public abstract void AddSubordinate(Employee e);
public abstract void RemoveSubordinate(Employee e);
}對于基層員工,如果客戶端無意間調(diào)用了不該使用的方法,這基本是一個明確的、表明客戶端代碼出現(xiàn)了邏輯問題的信號,這種情況直接拋出異常,能更快地暴露出問題
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();
}
}在經(jīng)理類中,得益于共有父類Employee,我們可以用一個列表裝下所有的下屬,不論下屬是基層員工,還是經(jīng)理,抑或是未來可能添加的實(shí)習(xí)生。畢竟他們都是雇員嘛
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>();
//下面是經(jīng)理所屬的方法
public override void AddSubordinate(Employee e) { Subordinate.Add(e); }
public override void RemoveSubordinate(Employee e) { Subordinate.Remove(e); }
}公司架構(gòu)類和客戶端代碼調(diào)用保持不變,運(yùn)行結(jié)果一致,重構(gòu)成功。
可以看到,在使用了組合模式之后,現(xiàn)在的代碼不但消除了冗余(不用再去維護(hù)多個下屬列表),也更具有抵御未來變化的能力,這樣的結(jié)構(gòu)比起原來,當(dāng)然是更加合理的。這就是結(jié)構(gòu)型設(shè)計(jì)模式的用武之地,讓對象的結(jié)構(gòu)更加的合理,更加的易于擴(kuò)展。
這就是關(guān)于Composite組合模式的介紹,鑒于筆者能力有限,如果大家對于這篇文章中所講有其他看法,歡迎留言討論。
到此這篇關(guān)于C#中的composite模式的文章就介紹到這了,更多相關(guān)C# composite模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#組件系列 你值得擁有的一款Excel處理神器Spire.XLS
又一款Excel處理神器Spire.XLS,這篇文章主要為大家詳細(xì)介紹了第三方組件Spire.XLS,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-09-09
c# 兩個數(shù)組比較,將重復(fù)部分去掉,返回不重復(fù)部分的實(shí)現(xiàn)
下面小編就為大家?guī)硪黄猚# 兩個數(shù)組比較,將重復(fù)部分去掉,返回不重復(fù)部分的實(shí)現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-12-12
使用C#開發(fā)OPC?Server服務(wù)器源碼解析
OPC?Server服務(wù)器服務(wù)器的開發(fā)比較繁瑣,本示例采用C#提供了一種簡單快速實(shí)現(xiàn)OPCServer的方法,已經(jīng)在工程項(xiàng)目中應(yīng)用,本文對C#開發(fā)OPC?Server服務(wù)器相關(guān)知識給大家介紹的非常詳細(xì),需要的朋友參考下吧2022-06-06
C#中Parallel類For、ForEach和Invoke使用介紹
這篇文章介紹了C#中Parallel類For、ForEach和Invoke的使用方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-04-04

