一、为何需要组合模式(Composite)?
在代码设计中,有种情况是对象之间存在层次关系,即对象之间会存在父结点和子结点的关系。
比如在文件管理系统中,所有文件和文件夹形成树状结构,文件夹目录里存在子文件夹和文件。文件夹属于枝结点,文件属于叶结点。枝结点有下一个层级,所以可以对其子结点的对象进行移除或增加新对象的操作。而叶结点并没有移除和增加的操作,是因为它没有子结点。
对于这种对象之间存在的层级关系,我们可以使用组合模式来设计。
特点:
将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。(一致性:如文件夹和文件都有被打开的操作。所有文件夹对其子结点都有移除和增加新子结点的操作。)
结构:
组合对象的抽象接口(Component):定义组合对象的公共接口。(组合对象指的是所有结点。如文件系统的打开,移除和增加的功能接口)
叶结点具体类(Leaf):叶结点实现组合对象的抽象方法,但没有子结点。(如文件)
枝结点具体类(Composite):枝节点实现组合对象的抽象方法,并且有一个容器用来存储子结点。(如文件夹)
适合应用场景特点:
- 对象之间存在层次结构,具有使用某些功能的一致性。
二、例子
需求:
一家公司里的部门有人事部、开发部、测试部、设计部。现在该公司又把测试部再分为软件测试组和硬件测试组。每个测试组都有一个组长。每当完成某个阶段任务时,公司要求每个部门以及测试部组长进行汇报工作进度的情况。
设计分析:
- 枝结点:公司、人事部、开发部、设计部、测试部、软件测试组、硬件测试组。叶结点:软件测试组长和硬件测试组长。
- 所有结点的公共动作:汇报工作。
- 所有树枝结点都有增加和移除结点的操作。
1、定义组合对象的抽象接口:
c
//Component:(组合对象的抽象接口,叶结点抽象接口)
public abstract class Company
{
public string Name { get; protected set; }
public abstract void reportWork();
}
//Component:(组合对象的抽象接口,枝结点抽象接口)
public abstract class AbsComposite : Company
{
public abstract void Add(Company company);
public abstract void Remove(Company company);
}
2、定义叶结点具体类和枝结点具体类:
c
//Leaf:叶结点具体类(组长、同事成员)
public class GroupLeader : Company
{
public GroupLeader(string name)
{
Name = name;
}
public override void reportWork()
{
Console.WriteLine(Name);
}
}
//Composite:枝结点具体类(公司、部门)
public class Composite : AbsComposite
{
private List<Company> _CompanyList = new List<Company>();//容器
public List<Company> CompanyList { get { return _CompanyList; } }
public Composite(string name)
{
this.Name = name;
}
public override void Add(Company company)
{
_CompanyList.Add(company);
}
public override void Remove(Company company)
{
_CompanyList.Remove(company);
}
public override void reportWork()
{
Console.WriteLine("The following members need to report their work to our company:");
getCompanyList(CompanyList);
}
//不是组合对象的公共接口,是 Department 类私有的成员方法。
//该方法是为了测试例子而用的,并不是学习组合模式的要点。可忽略。
private void getCompanyList(List<Company> list)
{
foreach (var item in list)
{
var itemDepart = item as Composite;//枝结点
if (itemDepart != null)
{
if (itemDepart.CompanyList?.Count > 0)
{
Console.WriteLine();
Console.Write("【" + item.Name +"】" + "-");枝结点,有子结点
getCompanyList(itemDepart.CompanyList);
continue;
}
Console.WriteLine("【" + item.Name + "】");//枝结点,子结点为空
}
else
Console.Write("(" + item.Name + ")");//叶结点
}
}
}
3、主程序:
c
//主程序
class Program
{
static void Main(string[] args)
{
AbsComposite root = new Composite("阳光组合公司");
root.Add(new Composite("人事部"));
root.Add(new Composite("开发部"));
root.Add(new Composite("设计部"));
var testDepartment = new Composite("测试部");
var software = new Composite("软件测试组");
var hardware = new Composite("硬件测试组");
GroupLeader softwareLeader = new GroupLeader("软件测试组长");
GroupLeader hardwareLeader = new GroupLeader("硬件测试组长");
root.Add(testDepartment);
testDepartment.Add(software);
testDepartment.Add(hardware);
software.Add(softwareLeader);
hardware.Add(hardwareLeader);
root.reportWork();//各部门和测试组长汇报工作
Console.ReadLine();
}
}