1. 定义
组合模式(Composite),将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
简单来说,这个模式通常运用于一些部分-整体这些概念中,当我们不需要区分其具体区别时候,可以用这个模式
比如说,卖电脑的商家,可以卖单独配件也可以卖组装整机,又如复制文件,可以一个一个文件复制粘贴还可以整个文件夹进行复制,再比如文本编辑,可以给单个字加粗、变色、改字体,当然也可以给整段文字做同样的操作,其本质都是整体和部分不需要区分使用,都是同一个效果。
2. 使用背景
当发现需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象(就是由子对象组合而来的对象)与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式了。
比方说需要一个类似树状图的结构,而里面某些分支又有分支,可以重复用,就可以采用组合模式
3. 模式使用结构图
解释:
Component
为组合中的对象声明接口,在适当情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component
的子部件。
Leaf
在组合中表示叶节点对象,叶节点没有子节点。
Composite
定义有枝节点行为,用来存储子部件,在Component
接口中实现与子部件有关的操作,比如增加Add和删除Remove。
4. 组合模式使用例子
题目:
我们需要实现下面这个树状图,其中财务部和人力资源部可以重复使用,即不管是总公司的财务、人力还是分公司的财务、人力,都是同一个职责
结构图:
代码:
1.定义组件,方便后面的类继承其功能,方便复用,里面必须有其所有公共部分的方法或者是代码
//定义一个组件,方便后续重复使用,这里的组件是某公司
abstract class Company
{
protected readonly string name;
public Company(string name)
{
this.name = name;
}
public abstract void Add(Company conponent);//增加
public abstract void Remove(Company conponent);//移除
public abstract void Display(int depth);//显示
public abstract void LineOfDuty();//履行职责
}
2.创建一个继承于前面组件是、具体的公司类
class ConcreteCompany : Company
{
//组件集合
private List<Company> companies = new List<Company>();
public ConcreteCompany(string name) : base(name)
{
}
//添加子组件
public override void Add(Company conponent)
{
companies.Add(conponent);
}
//移除子组件
public override void Remove(Company conponent)
{
companies.Remove(conponent);
}
//展示旗下所有内容
public override void Display(int depth)
{
Console.WriteLine(new String('-',depth)+name);
foreach (var t in companies)
{
t.Display(depth + 2);
}
}
//实现旗下所有职责
public override void LineOfDuty()
{
foreach (var item in companies)
{
item.LineOfDuty();
}
}
}
3.实现财务部、人力资源部的类,也是继承于组件类
//财务部 树叶节点
class FinanceDepartment : Company
{
public FinanceDepartment(string name) : base(name)
{
}
public override void Add(Company conponent)
{
}
public override void Display(int depth)
{
Console.WriteLine(new String('-',depth)+name);
}
public override void LineOfDuty()
{
Console.WriteLine("{0}公司财务收支管理",name);
}
public override void Remove(Company conponent)
{
}
}
//人力资源部 树叶节点
class HRDepartment : Company
{
public HRDepartment(string name) : base(name)
{
}
public override void Add(Company conponent)
{
}
public override void Display(int depth)
{
Console.WriteLine(new String('-', depth) + name);
}
public override void LineOfDuty()
{
Console.WriteLine("{0}员工招聘培训管理", name);
}
public override void Remove(Company conponent)
{
}
}
4.主程序
class Program
{
static void Main(string[] args)
{
ConcreteCompany root = new ConcreteCompany("北京总公司");
root.Add(new HRDepartment("北京总公司人力资源部"));
root.Add(new FinanceDepartment("北京总公司财务部"));
ConcreteCompany c1 = new ConcreteCompany("上海华东分公司");
c1.Add(new HRDepartment("上海华东分公司人力资源部"));
c1.Add(new FinanceDepartment("上海华东分公司财务部"));
root.Add(c1);
ConcreteCompany c11 = new ConcreteCompany("南京办事处");
c11.Add(new HRDepartment("南京办事处人力资源部"));
c11.Add(new FinanceDepartment("南京办事处财务部"));
c1.Add(c11);
ConcreteCompany c22 = new ConcreteCompany("杭州办事处");
c22.Add(new HRDepartment("杭州办事处人力资源部"));
c22.Add(new FinanceDepartment("杭州办事处财务部"));
c1.Add(c22);
Console.WriteLine("\n结构图:");
root.Display(1);
Console.WriteLine("\n职责:");
root.LineOfDuty();
Console.Read();
}
}
结果显示:
5. 全部程序
using System;
using System.Collections.Generic;
namespace 组合模式
{
class Program
{
static void Main(string[] args)
{
ConcreteCompany root = new ConcreteCompany("北京总公司");
root.Add(new HRDepartment("北京总公司人力资源部"));
root.Add(new FinanceDepartment("北京总公司财务部"));
ConcreteCompany c1 = new ConcreteCompany("上海华东分公司");
c1.Add(new HRDepartment("上海华东分公司人力资源部"));
c1.Add(new FinanceDepartment("上海华东分公司财务部"));
root.Add(c1);
ConcreteCompany c11 = new ConcreteCompany("南京办事处");
c11.Add(new HRDepartment("南京办事处人力资源部"));
c11.Add(new FinanceDepartment("南京办事处财务部"));
c1.Add(c11);
ConcreteCompany c22 = new ConcreteCompany("杭州办事处");
c22.Add(new HRDepartment("杭州办事处人力资源部"));
c22.Add(new FinanceDepartment("杭州办事处财务部"));
c1.Add(c22);
Console.WriteLine("\n结构图:");
root.Display(1);
Console.WriteLine("\n职责:");
root.LineOfDuty();
Console.Read();
}
}
//定义一个组件,方便后续重复使用,这里的组件是某公司
abstract class Company
{
protected readonly string name;
public Company(string name)
{
this.name = name;
}
public abstract void Add(Company conponent);//增加
public abstract void Remove(Company conponent);//移除
public abstract void Display(int depth);//显示
public abstract void LineOfDuty();//履行职责
}
class ConcreteCompany : Company
{
//组件集合
private List<Company> companies = new List<Company>();
public ConcreteCompany(string name) : base(name)
{
}
//添加子组件
public override void Add(Company conponent)
{
companies.Add(conponent);
}
//移除子组件
public override void Remove(Company conponent)
{
companies.Remove(conponent);
}
//展示旗下所有内容
public override void Display(int depth)
{
Console.WriteLine(new String('-',depth)+name);
foreach (var t in companies)
{
t.Display(depth + 2);
}
}
//实现旗下所有职责
public override void LineOfDuty()
{
foreach (var item in companies)
{
item.LineOfDuty();
}
}
}
//财务部 树叶节点
class FinanceDepartment : Company
{
public FinanceDepartment(string name) : base(name)
{
}
public override void Add(Company conponent)
{
}
public override void Display(int depth)
{
Console.WriteLine(new String('-',depth)+name);
}
public override void LineOfDuty()
{
Console.WriteLine("{0}公司财务收支管理",name);
}
public override void Remove(Company conponent)
{
}
}
class HRDepartment : Company
{
public HRDepartment(string name) : base(name)
{
}
public override void Add(Company conponent)
{
}
public override void Display(int depth)
{
Console.WriteLine(new String('-', depth) + name);
}
public override void LineOfDuty()
{
Console.WriteLine("{0}员工招聘培训管理", name);
}
public override void Remove(Company conponent)
{
}
}
}
6. 总结
对于前面那个例子,组合模式这样就定义了包含人力资源部和财务部这些基本对象和分公司、办事处等组合对象的层次结构。基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断地传递下去,客户代码中,任何用到基本对象的地方都可以使用组合对象了。
另外用户是不用关心到底是处理一个叶节点还是处理一个组合组件,也就用不着为定义组合写一些选择判断语句了。
简单点说,就是组合模式让客户可以一致地使用组合结构和单个对象。
这也就是说,那家公司开多少个以及多少级办事处都没问题了。哪怕到地级市、县级市、镇、乡、村、户……