簡介
這次要跟大家介紹的是Composite Pattern,這是GoF(Gang of Four)經典的23種設計模式之一,GoF把設計模式分為三種:
Composite Pattern被歸類在Structural Patterns,這類設計模式著重在類別及物件的組合,形成更龐雜的結構,從而實現新的功能。
Composite pattern 的官方定義:
將物件(objects)組合(compose)成樹狀結構以表示part-whole hierarchies(可參見文末連結,此處不贅述),這使客戶(clients)能夠一致地(uniformly)對待單個物件和物件的組合。
大家熟悉的檔案系統就是一種Composite pattern的實現,我們進入檔案系統基本上只會看到資料夾(Directory)和檔案(file)這兩種物件,一個資料夾可以包含任意數量的檔案或是資料夾,這麼一來就形成了樹狀的結構,而我們都可以對其進行刪除或是重新命名的動作,這就是定義所說的一致性對待。只要遇到階層或是樹狀的結構都可以評估看看是否需要套用Composite pattern。
實作
此範例是採用C#實作簡易版的公司職員管理,下圖為UML類別圖,僅有2個類別實作1個介面。
Program.cs
using System; using System.Collections.Generic; namespace CompositePattern { interface IEmployee { //To set an employee name string Name { get; set; } //To set an employee department string Dept { get; set; } //To set an employee designation string Designation { get; set; } //To display an employee details void DisplayDetails(); } //Leaf Node class Employee : IEmployee { public string Name { get; set; } public string Dept { get; set; } public string Designation { get; set; } //Details of a leaf node public void DisplayDetails() { Console.WriteLine($"\t{Name} 隸屬於 { Dept} 頭銜:{Designation}"); } } //Non-leaf node class CompositeEmployee : IEmployee { public string Name { get; set; } public string Dept { get; set; } public string Designation { get; set; } // 下屬容器 private List<IEmployee> subordinateList = new List<IEmployee>(); // 新增員工 public void AddEmployee(IEmployee e) { subordinateList.Add(e); } // 移除員工 public void RemoveEmployee(IEmployee e) { subordinateList.Remove(e); } //Details of a composite node public void DisplayDetails() { Console.WriteLine($"\n{Name} 隸屬於 {Dept} 頭銜:{Designation}"); foreach (IEmployee e in subordinateList) { e.DisplayDetails(); } } } class Program { static void Main(string[] args) { Console.WriteLine("***Composite Pattern Demo. ***"); #region 審計委員會 // 2名審計委員會員工 Employee e1 = new Employee { Name = "John", Dept = "審計委員會", Designation = "工讀生" }; Employee e2 = new Employee { Name = "Daniel", Dept = "審計委員會", Designation = "組長" }; // 審計委員會主管 CompositeEmployee manager1 = new CompositeEmployee { Name = "Peter", Dept = "審計委員會", Designation = "主管" }; // 主管Peter旗下新增2名員工 manager1.AddEmployee(e1); manager1.AddEmployee(e2); #endregion #region 薪酬委員會 // 3名薪酬委員會員工 Employee e3 = new Employee { Name = "Maggie", Dept = "薪酬委員會", Designation = "工讀生" }; Employee e4 = new Employee { Name = "Amy", Dept = "薪酬委員會", Designation = "工讀生" }; Employee e5 = new Employee { Name = "Allen", Dept = "薪酬委員會", Designation = "組長" }; CompositeEmployee manager2 = new CompositeEmployee { Name = "Alex", Dept = "薪酬委員會", Designation = "主管" }; // 主管Alex旗下新增3名員工 manager2.AddEmployee(e3); manager2.AddEmployee(e4); manager2.AddEmployee(e5); #endregion #region 管理層頂端 CompositeEmployee CEO = new CompositeEmployee { Name = "Tom", Dept = "董事會", Designation = "執行長" }; // 執行長Tom旗下新增2名員工 CEO.AddEmployee(manager1); CEO.AddEmployee(manager2); #endregion Console.WriteLine("\n列出完整組織員工明細:"); CEO.DisplayDetails(); // 移除CEO旗下的員工Peter CEO.RemoveEmployee(manager1); Console.WriteLine("\n-----------------------------------------\n"); Console.WriteLine("\n移除Peter後的員工明細:"); CEO.DisplayDetails(); Console.WriteLine("\n-----------------------------------------\n"); Console.WriteLine("\n只列出主管Alex旗下員工明細:"); manager2.DisplayDetails(); Console.WriteLine("\n-----------------------------------------\n"); Console.WriteLine("\n只列出員工John的員工明細:"); e1.DisplayDetails(); //Wait for user Console.ReadKey(); } } }
程式說明
這個範例只有一個C#檔,直接複製程式碼另存為Program.cs檔並加入至Visual Studio創建的Console應用程式專案中即可運行。
main函數是C#程式運行的起始點,可以視為用戶端(client),範例中我們在其中進行了各式操作:
整個樹狀結構中CompositeEmployee作為node,Employee作為leaf,這兩個類別共同實作了IEmployee介面,達到了一致性的對待(都可以顯示員工明細)。
part-whole hierarchies補充說明連結 https://www.open.edu/openlearn/ocw/mod/oucontent/view.php?id=82521§ion=1.3