介绍:组合模式(Composite):将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
组合模式结构图
组合模式的基本代码
声明一个接口用于访问和管理Component的子部件
Component为组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行文。声明一个接口用于访问和管理Component的子部件abstract class Component
{
protected string name;
public Component(string name)
{
this.name = name;
}
public abstract void Add(Component c); //通常都用Add和Remove方法来提供增加或移除树枝的功能
public abstract void Remove(Component c);
public abstract void Display(int depth);
}
Leaf在组合中表示叶节点对象,叶节点没有子节点
class Leaf : Component
{
public Leaf(string name)
: base(name)
{ }
public override void Add(Component c) //由于叶子没有再增加分枝和树叶,所以Add和Remove方法实现它没有意义,但这样做可以消除
//叶节点和枝节点对象在抽象层次的区别,它们具备完全一致的接口
{
Console.WriteLine("Cannot add to a leaf");
}
public override void Remove(Component c)
{
Console.WriteLine("Cannot remove from a leaf");
}
public override void Display(int depth)
{
Console.WriteLine(new String ('-', depth)+name); //叶节点的具体方法。此处是显示其名称和级别
}
}
Composite 定义有枝节节点的行为,用来存储子部件,在Composite接口中实现与子部件有关的操作,比如增加Add和删除Remove
class Composite : Component
{
private List<Component> children = new List<Component>(); //一个子对象集合用来存储其下属的枝节点和叶节点
public Composite(string name)
: base(name)
{ }
public override void Add(Component c)
{
children.Add(c);
}
public override void Remove(Component c)
{
children.Remove(c);
}
public override void Display(int depth) //显示其枝节点名称,并对其下级进行遍历
{
Console.WriteLine(new String ('-',depth )+name);
foreach (Component component in children )
{
component.Display(depth + 2);
}
}
}
客户端代码
static void Main(string[] args)
{
Composite root = new Composite("root"); //生成树根root,根上长出两叶LeafA和LeafB
root.Add(new Leaf("Leaf A"));
root.Add(new Leaf("Leaf B"));
Composite comp = new Composite("Composite X"); //根上长出分支Composite X,分支上也有两叶LeafXA和LeafXB
comp.Add(new Leaf("Leaf XA"));
comp.Add(new Leaf("Leaf XB"));
root.Add(comp);
Composite comp2 = new Composite("Composite XY"); //在Composite X上再长出分支CompositeXY,分支上也有两叶LeafXYA和LeafXYB
comp.Add(new Leaf("Leaf XYA"));
comp.Add(new Leaf("Leaf XYB"));
comp.Add(comp2);
root.Add(new Leaf("Leaf C")); //根部上右长出两叶LeafC和LeafD,可惜LeafD没长牢,被风吹走了
Leaf leaf = new Leaf("Leaf D");
root.Add(leaf);
root.Remove(leaf);
root.Display(1); //显示大树的样子
Console.Read();
}
效果图
例子
小菜的公司接了一个项目,是为一家在全国许多城市都有分销机构的大公司做的办公管理系统,总部有人力资源部、财务、运营等部门。因为北京的总公司试用了小菜公司开发的软件,感觉不错,所以他们希望可以在他们的全部分分公司推广,一起使用。他们在北京有总部,在全国几大城市设有分公司,比如上海设有华东区分布,然后在一些省会城市还设有办事处,比如南京办事处、杭州办事处。现在有个问题是,总公司的人力资源部,财务部等办公管理功能在所有的分公司或办事处都需要。
总公司的要求:总部、分部和办事处是树状结构的,也就是有组织结构的,不可以简单的平行管理。
需求分析:
从总公司的需求来看,这是一个整体与部分的关系。总公司的组织结构,比如人力资源部、财务部的管理功能可以复用于分公司。这其实就是整体与部分的可以被一致对待的问题。(例如,在Word文档里的文字,对单个字的处理和对多个字、甚至整个文档的处理,其实是一样的)用户希望被一致对待,程序开发者也希望一致处理。
如果把北京总公司当做一颗大树的根部的话,它的下属分公司就是这棵树的分枝,甚至更小的分枝,而他们的相关职能部门由于没有分枝了,所以可以理解为树叶。
所以最好的办法我们在处理总公司的财务管理功能和处理子公司的财务管理功能的方法是一样的。
代码结构图
客户端代码
static void Main(string[] args)
{
ConcreteCompany root = new ConcreteCompany("北京总公司");
root.Add(new HRDepartment("总公司人力资源部"));
root.Add(new FinanceDepartment("总公司财务部"));
ConcreteCompany comp = new ConcreteCompany("上海华东分公司");
comp.Add(new HRDepartment("华东分公司人力资源部"));
comp.Add(new FinanceDepartment("华东分公司财务部"));
root.Add(comp);
ConcreteCompany comp1 = new ConcreteCompany("南京办事处");
comp1.Add(new HRDepartment("南京办事处人力资源部"));
comp1.Add(new FinanceDepartment("南京办事处财务部"));
comp.Add(comp1);
ConcreteCompany comp2 = new ConcreteCompany("杭州办事处");
comp2.Add(new HRDepartment("杭州办事处人力资源部"));
comp2.Add(new FinanceDepartment("杭州办事处财务部"));
comp.Add(comp2);
Console.WriteLine("\n结构图:");
root.Display(1);
Console.WriteLine("\n职责:");
root.LineOfDuty();
Console.Read();
}
公司类 抽象类或接口
abstract class Company
{
protected string name;
public Company(string name)
{
this.name = name;
}
public abstract void Add(Company c);//增加
public abstract void Remove(Company c);//移除
public abstract void Display(int depth);//显示
public abstract void LineOfDuty();//履行职责
}
具体公司类 实现接口 树枝节点
class ConcreteCompany : Company
{
private List<Company> children = new List<Company>();
public ConcreteCompany(string name)
: base(name)
{ }
public override void Add(Company c)
{
children.Add(c);
}
public override void Remove(Company c)
{
children.Remove(c);
}
public override void Display(int depth)
{
Console.WriteLine(new String('-', depth) + name);
foreach (Company component in children)
{
component.Display(depth + 2);
}
}
//履行职责
public override void LineOfDuty()
{
foreach (Company component in children)
{
component.LineOfDuty();
}
}
}
人力资源部
class HRDepartment : Company
{
public HRDepartment(string name)
: base(name)
{ }
public override void Add(Company c)
{
}
public override void Remove(Company c)
{
}
public override void Display(int depth)
{
Console.WriteLine(new String('-', depth) + name);
}
public override void LineOfDuty()
{
Console.WriteLine("{0} 员工招聘培训管理", name);
}
}
财务部
class FinanceDepartment : Company
{
public FinanceDepartment(string name)
: base(name)
{ }
public override void Add(Company c)
{
}
public override void Remove(Company c)
{
}
public override void Display(int depth)
{
Console.WriteLine(new String('-', depth) + name);
}
public override void LineOfDuty()
{
Console.WriteLine("{0} 公司财务收支管理", name);
}
}
效果显示
组合的适用场合:
1. 你想表示对象的部分-整体层次结构。
2. 你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
优点:
1. 组合模式可以很容易的增加新的构件。
2. 使用组合模式可以使客户端变的很容易设计,因为客户端可以对组合和叶节点一视同仁。
缺点:
1. 使用组合模式后,控制树枝构件的类型不太容易。
2. 用继承的方法来增加新的行为很困难。
本文介绍了组合模式在构建企业组织结构上的应用,通过使用组合模式,实现了总部、分部和办事处的树状结构管理,使得财务管理等功能在不同层级上能够一致处理,简化了代码结构并提高了代码复用性。
1133





