意图:
将对象组合成树形结构以表示“部分-整体”的层次结构。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类中的操作和行为局部化。