程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 無廢話C#設計模式之十一:Composite

無廢話C#設計模式之十一:Composite

編輯:關於.NET

本系列文章將向大家介紹一下C#的設計模式,此為第十一篇文章,相信對大家會有所幫助的。廢話不多說,繼續來看。

  意圖

  將對象組合成樹形結構以表示“部分-整體”的層次結構。Composite模式使得用戶對單個對象和組合對象的使用具有一致性。

  場景

  我們知道,一個網絡游戲通常會有多個游戲大區。每一個游戲大區會有很多游戲服務器(一個游戲大區就是一組游戲服務器)。每一個游戲服務器上會有不同的服務(可以是多個服務)。這是一個明顯的部分-整體關系,假設我們現在需要制作一個服務器管理工具,用於顯示所有大區、服務器以及服務的信息,並且能開啟這些服務(可以是單獨開啟一個服務,也可以是開啟整個服務器上的所有服務,也可以是開啟整個大區的所有服務)。

  可以看到,游戲服務器和游戲大區都是一個組合對象,而游戲服務是最底層的節點。客戶端在開啟一個游戲大區服務的時候,必須和游戲服務器以及游戲服務進行依賴,而在開啟游戲服務器上所有服務的時候,必須和游戲服務進行依賴。試想一下,如果一個公司的總裁在管理上不但需要和各總監以及經理進行溝通,還有和底層的員工溝通,那麼總裁是不是會太忙碌了一點?由此,我們引入組合模式,使組合對象和單個對象具有一樣的表現形式。

  示例代碼

  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("電信區"); 
  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 
  { 
  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 serviceList = new List(); 
  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 serverList = new List(); 
  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============="); 
  } 
  } 
  }

  代碼執行結果如下圖:

  無廢話C#設計模式之十一:Composite_網頁教學網webjx.com轉載

代碼說明

  Element類型就是抽象構件的角色,它給組合對象以及單個對象提供了一個一致的接口,使得它們都能有一致的行為。

  這裡就出現一個問題,組合對象需要通過Add和Remove方法來為其添加子節點,而最底層的樹葉構件下並沒有任何子節點,在接口中定義這些操作子節點方法的方式叫做透明方式的合成模式,缺點就是不夠安全,容易在運行時出現異常。如果把這些操作子節點的方法定義從抽象構件中刪除,由各樹枝構件來實現的話,就是安全方式的合成模式,缺點也就是不夠透明。具體怎麼做還要根據自己的需求。

  GameService當然就是樹葉構件,如果調用它的Add以及Remove方法會拋出一個異常。當然,你也可以以其它方式來記錄這種邏輯錯誤。

  GameServer是一個樹枝構件。一個游戲服務器上會有多個游戲服務。這裡注意到一點,在開啟服務器上所有服務的時候,我們對服務進行了排序,排序是按照服務的類型進行的。然後,我們按照服務類型從大到小的次序開啟了服務。一般一個網絡游戲的服務會有很多種,而這些服務的開啟是有先後次序的,先開記錄日志的服務、排名服務、再開數據交換的服務、最後才是大廳服務等。我們不可能讓用戶進入大廳的時候沒有地方寫數據和日志吧。開啟服務是按照次序的,那麼關閉服務也就是按照相反的次序了。

  從上面這點,我們就可以看到組合模式的好處了,樹枝構件在管理樹葉構件的時候通常還會有一些邏輯,不會是簡單的增加和刪除操作。如果這些工作交給客戶端去做的話,就太不合理了。

  最上層的樹枝構件就是GameArea類型。它並沒有什麼特殊的地方。

  何時采用

  從代碼角度來說,如果類型之間組成了層次結構,你希望使用統一的接口來管理每一個層次的類型的時候。

  從應用角度來說,如果你希望把一對多的關系轉化為一對一的關系的時候。

  實現要點

  使用透明模式還是安全模式根據自己的需要定。

  在某些情況下,樹葉構件可以訪問樹枝構件獲取一些信息。

  如果樹葉構件數量比較多,樹枝構件頻繁遍歷子節點的話可以考慮進行緩存。

  既然所有對象有了統一的接口,客戶端應該針對抽象構件進行編程。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved