意图:
将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。
组合模式有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
生活中的例子
组合模式将对象组合成树形结构以表示"部分-整体"的层次结构。让用户一致地使用单个对象和组合对象。虽然例子抽象一些,但是算术表达式确实是组合的例子。算术表达式包括操作数、操作符和另一个操作数。操作数可以是数字,也可以是另一个表达式。这样,2+3和(2+3)+(4*6)都是合法的表达式。
在上面的图中,Component是组合和个体的基类,单个对象成了Leaf,而组合对象就是Composite,他们同时具有父类的操作。因此在客户看来他们是一致的。这样的话我们可以在Composite下再可以添加Composite对象或者Leaf对象,如此递归成了树形结构。
例子:
#include <iostream>
#include <string>
#include <list>
using namespace std;
class Component
{
protected:
string name;
public:
Component(string name)
{
this->name = name;
}
public:
virtual void Add(Component *c){}
virtual void Remove(Component *c){}
virtual void Display(int depth){}
};
class Leaf : public Component
{
public:
Leaf(string name) : Component(name){}
virtual void Add(Component *c)
{
cout<<"Cannot add to a leaf"<<endl;
}
virtual void Remove(Component *c)
{
cout<<"Cannot add to a leaf"<<endl;
}
virtual void Display(int depth)
{
string s(depth, '-');
cout<<s<<name<<endl;
}
};
class Composite : public Component
{
private:
list<Component*> children;
public:
Composite(string name) : Component(name){}
virtual void Add(Component *c)
{
children.push_back(c);
}
virtual void Remove(Component *c)
{
children.remove(c);
}
virtual void Display(int depth)
{
string s(depth, '-');
cout<<s<<name<<endl;
for (list<Component*>::iterator iter=children.begin();
iter!=children.end(); ++iter)
{
(*iter)->Display(depth+2);
}
}
};
int main(int argc, char **argv)
{
Composite *root = new Composite("root");
root->Add(new Leaf("Leaf A"));
root->Add(new Leaf("Leaf B"));
Composite *comp = new Composite("Composite X");
comp->Add(new Leaf("Leaf XA"));
comp->Add(new Leaf("Leaf XB"));
root->Add(comp);
Composite *comp2 = new Composite("Composite XY");
comp2->Add(new Leaf("Leaf XYA"));
comp2->Add(new Leaf("Leaf XYB"));
comp->Add(comp2);
root->Add(new Leaf("Leaf C"));
Leaf *leaf = new Leaf("Leaf D");
root->Add(leaf);
root->Remove(leaf);
root->Display(1);
system("pause");
return 0;
}
大话设计模式的例子:
有一家公司,总公司在北京,在上海有其在华东地区的分公司,然后在南京和杭州分别有各自的办事处。这里每个地方无论是总部还是分部都会有相同的部门设置,所以可以用Composite模式解决。
#include <iostream>
#include <string>
#include <list>
using namespace std;
class Company
{
protected:
string name;
public:
Company(string name) : name(name){}
virtual void Add(Company *c){}
virtual void Remove(Company *c){}
virtual void Display(int depth){}
virtual void LineofDuty(){}
};
class ConcreteCompany : public Company
{
private:
list<Company*> children;
public:
ConcreteCompany(string name) : Company(name){}
virtual void Add(Company *c)
{
children.push_back(c);
}
virtual void Remove(Company *c)
{
children.remove(c);
}
virtual void Display(int depth)
{
string s(depth, '-');
cout<<s<<name<<endl;
for (list<Company*>::const_iterator iter=children.begin();
iter!=children.end(); ++iter) {
(*iter)->Display(depth+2);
}
}
virtual void LineofDuty()
{
for (list<Company*>::const_iterator iter=children.begin();
iter!=children.end(); ++iter) {
(*iter)->LineofDuty();
}
}
};
class HRDepartment : public Company
{
public:
HRDepartment(string name) : Company(name){}
virtual void Display(int depth)
{
string s(depth, '-');
cout<<s<<name<<endl;
}
virtual void LineofDuty()
{
cout<<name<<" "<<"员工招聘培训管理"<<endl;
}
};
class FinanceDepartment : public Company
{
public:
FinanceDepartment(string name) : Company(name){}
virtual void Display(int depth)
{
string s(depth, '-');
cout<<s<<name<<endl;
}
virtual void LineofDuty()
{
cout<<name<<" "<<"公司财务收支管理"<<endl;
}
};
int main(int argc, char **argv)
{
ConcreteCompany *root = new ConcreteCompany("北京公司总部");
ConcreteCompany *compNan = new ConcreteCompany("南京办事处");
compNan->Add(new HRDepartment("南京办事处人力资源部"));
compNan->Add(new FinanceDepartment("南京办事处财务部"));
ConcreteCompany *compHang = new ConcreteCompany("杭州办事处");
compHang->Add(new HRDepartment("杭州办事处人力资源部"));
compHang->Add(new FinanceDepartment("杭州办事处财务部"));
ConcreteCompany *compShang = new ConcreteCompany("上海华东分公司");
compShang->Add(compNan);
compShang->Add(new HRDepartment("上海华东分公司人力资源部"));
compShang->Add(new FinanceDepartment("上海华东分公司财务部"));
compShang->Add(compHang);
root->Add(new HRDepartment("总公司人力资源部"));
root->Add(compShang);
root->Add(new FinanceDepartment("总公司财务部"));
cout<<"结构图"<<endl;
root->Display(1);
cout<<"职责"<<endl;
root->LineofDuty();
system("pause");
return 0;
}
这就是一个树形结构,子节点都可以跟父节点一样拥有相同的职能,但是他们所处的层次却是清晰地分开了,这就是Composite模式的好处。
注:组合模式一种继承关系,派生类中含有包含基类指针的容器。
组合模式和其他相关模式
1)装饰模式(Decorator模式)经常与Composite模式一起使用。当装饰和组合一起使用时,它们
通常有一个公共的父类。因此装饰必须支持具有 Add、Remove和GetChild操作的Component接口。
2)Flyweight模式让你共享组件,但不再能引用他们的父部件。
3)(迭代器模式)Itertor可用来遍历Composite。
4)(观察者模式)Visitor将本来应该分布在Composite和L e a f类中的操作和行为局部化。