(原创)无废话C#设计模式之十一:Composite
無廢話C#設(shè)計(jì)模式之十一:Composite
?
意圖
?
?????? 將對(duì)象組合成樹形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)。Composite模式使得用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性。
?
場(chǎng)景
?
我們知道,一個(gè)網(wǎng)絡(luò)游戲通常會(huì)有多個(gè)游戲大區(qū)。每一個(gè)游戲大區(qū)會(huì)有很多游戲服務(wù)器(一個(gè)游戲大區(qū)就是一組游戲服務(wù)器)。每一個(gè)游戲服務(wù)器上會(huì)有不同的服務(wù)(可以是多個(gè)服務(wù))。這是一個(gè)明顯的部分-整體關(guān)系,假設(shè)我們現(xiàn)在需要制作一個(gè)服務(wù)器管理工具,用于顯示所有大區(qū)、服務(wù)器以及服務(wù)的信息,并且能開啟這些服務(wù)(可以是單獨(dú)開啟一個(gè)服務(wù),也可以是開啟整個(gè)服務(wù)器上的所有服務(wù),也可以是開啟整個(gè)大區(qū)的所有服務(wù))。
可以看到,游戲服務(wù)器和游戲大區(qū)都是一個(gè)組合對(duì)象,而游戲服務(wù)是最底層的節(jié)點(diǎn)。客戶端在開啟一個(gè)游戲大區(qū)服務(wù)的時(shí)候,必須和游戲服務(wù)器以及游戲服務(wù)進(jìn)行依賴,而在開啟游戲服務(wù)器上所有服務(wù)的時(shí)候,必須和游戲服務(wù)進(jìn)行依賴。試想一下,如果一個(gè)公司的總裁在管理上不但需要和各總監(jiān)以及經(jīng)理進(jìn)行溝通,還有和底層的員工溝通,那么總裁是不是會(huì)太忙碌了一點(diǎn)?由此,我們引入組合模式,使組合對(duì)象和單個(gè)對(duì)象具有一樣的表現(xiàn)形式。
?
示例代碼
?
| using System; using System.Collections.Generic; using System.Text; ? namespace CompositeExample { ??? class Program ??? { ??????? static void Main(string[] args) ??????? { ??????????? Element server1 = new GameServer("GS1", "192.168.0.1"); ??????????? server1.Add(new GameService("Lobby1", 1, "S5Lobby1")); ??????????? server1.Add(new GameService("Gate1", 2, "S5Gate1")); ??????????? server1.Add(new GameService("DataExchange1", 3, "S5DataExchange1")); ??????????? server1.Add(new GameService("Rank1", 4, "S5Rank1")); ??????????? server1.Add(new GameService("Log1", 5, "S5Log1")); ??????????? Element server2 = new GameServer("GS2", "192.168.0.2"); ??????????? server2.Add(new GameService("Lobby2", 1, "S5Lobby2")); ??????????? server2.Add(new GameService("Gate2", 2, "S5Gate2")); ???? ???????server2.Add(new GameService("DataExchange2", 3, "S5DataExchange1")); ??????????? server2.Add(new GameService("Rank2", 4, "S5Rank2")); ??????????? server2.Add(new GameService("Log2", 5, "S5Log2")); ??????????? Element area = new GameArea("電信區(qū)"); ??????????? area.Add(server1); ??????????? area.Add(server2); ??????????? area.Display(); ??????????? area.Start(); ??????????? area.Stop(); ??????? } ??? } ? ??? abstract class Element ??? { ??????? protected string name; ? ??????? public Element(string name) ??????? { ??????????? this.name = name; ??????? } ? ??????? public abstract void Add(Element element); ??????? public abstract void Remove(Element element); ??????? public abstract void Display(); ??????? public abstract void Start(); ??????? public abstract void Stop(); ??? } ? ??? class GameService : Element, IComparable<GameService> ??? { ??????? private int serviceType; ??????? private string serviceName; ? ??????? public GameService(string name, int serviceType, string serviceName) ??????????? : base (name) ??????? { ??????????? this.serviceName = serviceName; ??????????? this.serviceType = serviceType; ??????? } ? ??????? public override void Add(Element element) ??????? { ??????????? throw new ApplicationException("xxx"); ??????? } ? ??????? public override void Remove(Element element) ??????? { ??????????? throw new ApplicationException("xxx"); ??????? } ? ??????? public override void Display() ??????? { ??????????? Console.WriteLine(string.Format("name:{0},serviceType:{1},serviceName:{2}", name, serviceType, serviceName)); ??????? } ? ??????? public override void Start() ??????? { ??????????? Console.WriteLine(string.Format("{0} started", name)); ??????? } ? ??????? public override void Stop() ??????? { ??????????? Console.WriteLine(string.Format("{0} stopped", name)); ??????? } ? ??????? public int CompareTo(GameService other) ??????? { ??????????? return other.serviceType.CompareTo(serviceType); ??????? } ??? } ? ??? class GameServer : Element ??? { ??????? private string serverIP; ??????? private List<GameService> serviceList = new List<GameService>(); ? ??????? public GameServer(string name, string serverIP) ??????????? : base(name) ??????? { ??????????? ??????????? this.serverIP = serverIP; ??????? } ??????? ??????? public override void Add(Element element) ??????? { ??????????? serviceList.Add((GameService)element); ??????? } ? ??????? public override void Remove(Element element) ??????? { ??????????? serviceList.Remove((GameService)element); ??????? } ? ??????? public override void Display() ???? ???{ ??????????? Console.WriteLine(string.Format("{0}{1}({2}){3}", new string('+', 10), name, serverIP, new string('+', 10))); ??????????? foreach (Element element in serviceList) ??????????? { ??????????????? element.Display(); ??????????? } ??????? } ? ? ??????public override void Start() ??????? { ??????????? serviceList.Sort(); ??????????? Console.WriteLine("=============Starting the whole " + name + "============="); ??????????? for (int i = 0; i < serviceList.Count; i++ ) ??????????? { ??????????????? serviceList[i].Start(); ??????????? } ??????????? Console.WriteLine("=============The whole " + name + " started============="); ??????? } ? ??????? public override void Stop() ??????? { ??????????? Console.WriteLine("=============Stopping the whole " + name + "============="); ??????????? for (int i = serviceList.Count -1; i >= 0; i--) ??????????? { ??????????????? serviceList[i].Stop(); ??????????? } ??????????? Console.WriteLine("=============The whole " + name + " stopped============="); ??????? } ??? } ? ??? class GameArea : Element ??? { ??????? private List<GameServer> serverList = new List<GameServer>(); ? ??????? public GameArea(string name) ??????????? : base(name) { } ? ??????? public override void Add(Element element) ??????? { ??????????? serverList.Add((GameServer)element); ??????? } ? ??????? public override void Remove(Element element) ??????? { ??????????? serverList.Remove((GameServer)element); ??????? } ? ??????? public override void Display() ??????? { ??????????? Console.WriteLine(new string('=',20)); ??????????? Console.WriteLine("?????? " + name); ??????????? Console.WriteLine(new string('=', 20)); ??????????? foreach (Element element in serverList) ??????????? { ??????????????? element.Display(); ??????????? } ??????? } ? ??????? public override void Start() ??????? { ??????????? Console.WriteLine("=============Starting the whole " + name + "============="); ??????????? foreach (Element element in serverList) ??????????? { ??????????????? element.Start(); ??????????? } ??????????? Console.WriteLine("=============The whole " + name + " started============="); ??????? } ? ??????? public override void Stop() ??????? { ??????????? Console.WriteLine("=============Stopping the whole " + name + "============="); ??????????? foreach (Element element in serverList) ??????????? { ??????????????? element.Stop(); ??????????? } ??????????? Console.WriteLine("=============The whole " + name + " stopped============="); ??????? } ??? } } |
代碼執(zhí)行結(jié)果如下圖:
?
?
代碼說明
?
l???????? Element類型就是抽象構(gòu)件的角色,它給組合對(duì)象以及單個(gè)對(duì)象提供了一個(gè)一致的接口,使得它們都能有一致的行為。
l???????? 這里就出現(xiàn)一個(gè)問題,組合對(duì)象需要通過Add和Remove方法來為其添加子節(jié)點(diǎn),而最底層的樹葉構(gòu)件下并沒有任何子節(jié)點(diǎn),在接口中定義這些操作子節(jié)點(diǎn)方法的方式叫做透明方式的合成模式,缺點(diǎn)就是不夠安全,容易在運(yùn)行時(shí)出現(xiàn)異常。如果把這些操作子節(jié)點(diǎn)的方法定義從抽象構(gòu)件中刪除,由各樹枝構(gòu)件來實(shí)現(xiàn)的話,就是安全方式的合成模式,缺點(diǎn)也就是不夠透明。具體怎么做還要根據(jù)自己的需求。
l???????? GameService當(dāng)然就是樹葉構(gòu)件,如果調(diào)用它的Add以及Remove方法會(huì)拋出一個(gè)異常。當(dāng)然,你也可以以其它方式來記錄這種邏輯錯(cuò)誤。
l???????? GameServer是一個(gè)樹枝構(gòu)件。一個(gè)游戲服務(wù)器上會(huì)有多個(gè)游戲服務(wù)。這里注意到一點(diǎn),在開啟服務(wù)器上所有服務(wù)的時(shí)候,我們對(duì)服務(wù)進(jìn)行了排序,排序是按照服務(wù)的類型進(jìn)行的。然后,我們按照服務(wù)類型從大到小的次序開啟了服務(wù)。一般一個(gè)網(wǎng)絡(luò)游戲的服務(wù)會(huì)有很多種,而這些服務(wù)的開啟是有先后次序的,先開記錄日志的服務(wù)、排名服務(wù)、再開數(shù)據(jù)交換的服務(wù)、最后才是大廳服務(wù)等。我們不可能讓用戶進(jìn)入大廳的時(shí)候沒有地方寫數(shù)據(jù)和日志吧。開啟服務(wù)是按照次序的,那么關(guān)閉服務(wù)也就是按照相反的次序了。
l???????? 從上面這點(diǎn),我們就可以看到組合模式的好處了,樹枝構(gòu)件在管理樹葉構(gòu)件的時(shí)候通常還會(huì)有一些邏輯,不會(huì)是簡(jiǎn)單的增加和刪除操作。如果這些工作交給客戶端去做的話,就太不合理了。
l???????? 最上層的樹枝構(gòu)件就是GameArea類型。它并沒有什么特殊的地方。
?
何時(shí)采用
?
l???????? 從代碼角度來說,如果類型之間組成了層次結(jié)構(gòu),你希望使用統(tǒng)一的接口來管理每一個(gè)層次的類型的時(shí)候。
l???????? 從應(yīng)用角度來說,如果你希望把一對(duì)多的關(guān)系轉(zhuǎn)化為一對(duì)一的關(guān)系的時(shí)候。
?
實(shí)現(xiàn)要點(diǎn)
?
l???????? 使用透明模式還是安全模式根據(jù)自己的需要定。
l???????? 在某些情況下,樹葉構(gòu)件可以訪問樹枝構(gòu)件獲取一些信息。
l???????? 如果樹葉構(gòu)件數(shù)量比較多,樹枝構(gòu)件頻繁遍歷子節(jié)點(diǎn)的話可以考慮進(jìn)行緩存。
l???????? 既然所有對(duì)象有了統(tǒng)一的接口,客戶端應(yīng)該針對(duì)抽象構(gòu)件進(jìn)行編程。
?
注意事項(xiàng)
?
l???????? 無
總結(jié)
以上是生活随笔為你收集整理的(原创)无废话C#设计模式之十一:Composite的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 栈对象、堆对象、静态对象的比较
- 下一篇: c# char unsigned_dll