[Design Pattern]組合模式(Composite pattern)
我自己學習 Design Pattern 了解2件很重要的規則,
1.多組合少繼承
但也不是說繼承不好,而是要謹慎使用繼承。
2.多擴充少修改
因新需求擴充新功能,而少修改已經通過測試的程式碼。
而這些最終目的就是要讓程式達到高內聚低耦合,anyway~~~
回到真實世界來看一下,大家一定都有寫過依照所屬個別部門單位或部門群組(處),
然後處理相關商業需求邏輯,好比報表顯示、讀取資料...等,
大多Client端可能要先依輸入資料取得相對應部門物件,
這裡面可能也有部門群組的判斷處理(如處別又是另一種商業邏輯),
而且部門群組的異動也將會造成Client端程式碼頻繁修改,
導致Client端的耦合性很高、不容易維護...等,
為了將Client程式碼解耦,我們透過組合模式,將物件組合成為簡單層次結構,
讓Client端可以使用一致的方法來處理組合物件就像處理個別物件一樣(將一對多改為一對一),
並且自己可以處理自己複雜的商業邏輯,
下面我們實際來看一下組合模式可以幫Client程式碼帶來什麼樣的改善。
UML
需求:依照部門處理相關商業邏輯
大多數人可能會這樣寫Client端程式碼
string[] Departments = new string[4] { "DepA", "DepB", "DepC","DepAll" };
Factory depfactory = new Factory();
foreach (string depart in Departments)
{
IDepProcess mydep = depfactory.GetDep(depart);
if (mydep is DepartmentA)//單部門
{
//讀資料
mydep.Process();
}
else if (mydep is DepartmentB)
{
//更新資料
mydep.Process();
}
else if (mydep is DepartmentC)
{
//刪除資料
mydep.Process();
}
else if (mydep is DepartmentGroup)//部門群組
{
//處別的處理
//先確認各部門人數..等
mydep.Process();
List<IDepProcess> mydeps = (mydep as DepartmentGroup).GetDepartments();
foreach (IDepProcess dep in mydeps)
{
//if(dep is DepartmentA)....
//Client繼續處理相關判斷
}
}
}
Console.ReadLine();
執行結果
public class Factory
{
public Factory() { }
public IDepProcess GetDep(string Department)
{
IDepProcess _IDepProcess = null;
switch (Department)
{
case "DepA":
_IDepProcess = new DepartmentA();
break;
case "DepB":
_IDepProcess = new DepartmentB();
break;
case "DepC":
_IDepProcess = new DepartmentC();
break;
case "DepAll":
_IDepProcess = new DepartmentGroup();
break;
default:
_IDepProcess = new DepartmentA();
break;
}
return _IDepProcess;
}
}
public interface IDepProcess
{
void Process();
}
public class DepartmentA : IDepProcess
{
#region IDepProcess 成員
public void Process()
{
Console.WriteLine("部門A處理");
}
#endregion
}
public class DepartmentB : IDepProcess
{
#region IDepProcess 成員
public void Process()
{
Console.WriteLine("部門B處理");
}
#endregion
}
public class DepartmentC : IDepProcess
{
#region IDepProcess 成員
public void Process()
{
Console.WriteLine("部門C處理");
}
#endregion
}
public class DepartmentGroup : IDepProcess
{
protected List<IDepProcess> departments=new List<IDepProcess>();
public List<IDepProcess> GetDepartments()
{
return departments;
}
#region IDepProcess 成員
public void Process()
{
Console.WriteLine("組合部門處理");
}
#endregion
}
接下來使用組合模式來重構Client程式碼
IDepProcessmydep=newDepartmentGroup();
mydep.Add(newDepartmentA());
mydep.Add(newDepartmentB());
mydep.Add(newDepartmentC());
mydep.Process();
執行結果
修改共同介面
/// <summary>
/// 共同介面
/// </summary>
public interface IDepProcess
{
void Process();
void Add(IDepProcess DepProcess);
void Remove(IDepProcess DepProcess);
}
為了要處理子物件邏輯,所以增加2各方法(Add、Remove)
其他類別修改如下
public class DepartmentA : IDepProcess
{
#region IDepProcess 成員
public void Process()
{
Console.WriteLine("部門A處理");
}
public void Add(IDepProcess DepProcess)
{
throw new Exception("The method or operation is not implemented.");
}
public void Remove(IDepProcess DepProcess)
{
throw new Exception("The method or operation is not implemented.");
}
#endregion
}
Add 和 Remove 對於沒有子部門(最底層)類別來說沒有任何意義,所以拋出自定義訊息。
群組類別修改如下
public class DepartmentGroup : IDepProcess
{
protected List<IDepProcess> departments = new List<IDepProcess>();
#region IDepProcess 成員
public void Add(IDepProcess DepProcess)
{
departments.Add(DepProcess);
}
public void Remove(IDepProcess DepProcess)
{
departments.Remove(DepProcess);
}
public void Process()
{
Console.WriteLine("組合部門處理");
if (departments.Count > 0)
{
foreach (IDepProcess dep in departments)
{
dep.Process();//可能是子部門或其他部門
}
}
}
#endregion
}
從應用角度來看,如果你想把一對多的關係,轉換為一對一的關係(一致性的行為),
那麼你可以看到組合模式所帶來的好處。
參考