1、组合模式(Composite):将对象组合成树形结构已表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
UML图如下:
参与者:
Component
----为组合中的对象声明接口
----在适当的情况下,实现所有类共有接口的缺省行为。
----在声明一个接口用于访问和管理Component的子组件
Leaf
----在组合中表示节点对象,叶节点没有子节点
----在组合中定义图元对象的行为
Composite
----定义有子部件的哪些部件的行为
----存储子部件
Client
----通过Component 接口操纵组合部件的队形。
协作:
用户使用Component类接口与组合结构中的对象进行交互。如果接收者是一个叶节点,则直接处理请求。如果接收者是Composite,它通常将请求发送给他们的子部件,在转发请求之前与之后可能执行一些辅助操作。
2、组合模式的透明方式与安全方式
1)透明方法:
也就是说在Component中声明所有用来管理子对象的方法,其中包括Add,Remove等。这样实现Component接口所有的子类都具备了Add和Remove。
这样做的好处就是叶节点和枝节点对于外界没有区别,它们具备完全一致的行为接口,但问题也很明显。因为Leaf类本身不具备Add(),Remove方法的功能,所以实现它是没有意义的。
2)安全方法:
也就是在Component接口中不去声明Add和Remove方法,那么子类的Leaf也就不需要去实现它,而是在Composite声明所有用来管理子类对象的方法,这样的做就不会出现刚才提出的问题,由于不够透明,所以树叶和树枝类将不具有相同的接口,客户端的调用需要做相应的判断,带来了不便。
3、如何使用组合模式?
答:当你发现需求中是体系部分与整体层次结构时,以及你希望用户可以忽略组合对象与单个对象的的不同,统一使用组合结构中的所有对象时,就应该考虑用组合模式了。
4、组合模式的好处?
答:组合模式定义了包含基本对象,组合对象的类层次结构,基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断地递归下去,客户代码中,任何用到基本对象的地方都可以使用组合对象。用户不用关心到底是处理一个叶节点还是处理一个组合组件。也就用不着为定义组合而写一些选择判断语句了。组合模式让客户可以一致地使用组合结构和单个对象。
代码实现如下;
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
#include <iostream>
#include <string> #include <vector> #include <algorithm> using namespace std; /* Component为组合中的对象声明接口,在适当情况下, 实现所有类共有接口的默认行为。声明一个接口用于 访问和管理Component的子部件 */ class Component { protected: string name; public: Component(string); /* 通常都用Add和Remove的方法 来提供增加和移除树叶或树枝的功能*/ virtual void Add(Component *) = 0; virtual void Remove(Component *) = 0; virtual void Display( int) = 0; }; Component::Component(string name) { Component::name = name; } /* Leaf在组合中表示叶节点对象,叶节点没有子节点。 */ class Leaf : public Component { public: Leaf(string name): Component(name) {} void Add(Component *); void Remove(Component *); void Display( int); }; /* 由于叶子没有增加分枝和树叶,所以Add和Remove方法实现它没有意义, 但这样做可以消除叶节点和枝节点对象在抽象层次的区别,它们具有 完全一致的接口。 */ void Leaf::Add(Component *p_c) { cout << "Cannot add to a leaf" << endl; } void Leaf::Remove(Component *p_c) { cout << "Cannot remove from a leaf" << endl; } /* 叶子节点的具体方法,此处是显示器名称和级别 */ void Leaf::Display( int depth) { for ( int i = 0; i < depth; ++i) { cout << "-"; } cout << name << endl; } /* Component定义有枝节点行为,用来存储子部件 在Component接口中实现与子部件有关的操作, 比如增加Add和删除Remove。 */ class Composite : public Component { private: vector<Component *> v; public: Composite(string name) : Component(name) {} void Add(Component *); void Remove(Component *); void Display( int); }; void Composite::Add(Component *p_c) { v.push_back(p_c); } void Composite::Remove(Component *p_c) { vector<Component *>::iterator it; it = find(v.begin(), v.end(), p_c); v.erase(it); } /* 显示节点名称并对其下级进行遍历 */ void Composite::Display( int depth) { for ( int i = 0; i < depth; ++i) { cout << "-"; } cout << name << endl; int cnt = v.size(); for ( int i = 0; i < cnt; ++i) { v[i]->Display(depth + 2); } } int main() { Composite *root = new Composite( "root"); Leaf *leafA = new Leaf( "Leaf A"); root->Add(leafA); 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); root->Display( 1); root->Remove(leafA); root->Display( 1); return 0; } |
DP书上给出的定义:将对象组合成树形结构以表示“部分-整体”的层次结构。组合使得用户对单个对象和组合对象的使用具有一致性。注意两个字“树形”。这种树形结构在现实生活中随处可见,比如一个集团公司,它有一个母公司,下设很多家子公司。不管是母公司还是子公司,都有各自直属的财务部、人力资源部、销售部等。对于母公司来说,不论是子公司,还是直属的财务部、人力资源部,都是它的部门。整个公司的部门拓扑图就是一个树形结构。
下面给出组合模式的UML图。从图中可以看到,FinanceDepartment、HRDepartment两个类作为叶结点,因此没有定义添加函数。而ConcreteCompany类可以作为中间结点,所以可以有添加函数。那么怎么添加呢?这个类中定义了一个链表,用来放添加的元素。
相应的代码实现为:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
class Company
{ public: Company(string name) { m_name = name; } virtual ~Company() {} virtual void Add(Company *pCom) {} virtual void Show( int depth) {} protected: string m_name; }; //具体公司 class ConcreteCompany : public Company { public: ConcreteCompany(string name): Company(name) {} virtual ~ConcreteCompany() {} void Add(Company *pCom) { m_listCompany.push_back(pCom); //位于树的中间,可以增加子树 } void Show( int depth) { for( int i = 0; i < depth; i++) cout << "-"; cout << m_name << endl; list<Company *>::iterator iter = m_listCompany.begin(); for(; iter != m_listCompany.end(); iter++) //显示下层结点 (*iter)->Show(depth + 2); } private: list<Company *> m_listCompany; }; //具体的部门,财务部 class FinanceDepartment : public Company { public: FinanceDepartment(string name): Company(name) {} virtual ~FinanceDepartment() {} virtual void Show( int depth) //只需显示,无限添加函数,因为已是叶结点 { for( int i = 0; i < depth; i++) cout << "-"; cout << m_name << endl; } }; //具体的部门,人力资源部 class HRDepartment : public Company { public: HRDepartment(string name): Company(name) {} virtual ~HRDepartment() {} virtual void Show( int depth) //只需显示,无限添加函数,因为已是叶结点 { for( int i = 0; i < depth; i++) cout << "-"; cout << m_name << endl; } }; int main() { Company *root = new ConcreteCompany( "总公司"); Company *leaf1 = new FinanceDepartment( "财务部"); Company *leaf2 = new HRDepartment( "人力资源部"); root->Add(leaf1); root->Add(leaf2); //分公司A Company *mid1 = new ConcreteCompany( "分公司A"); Company *leaf3 = new FinanceDepartment( "财务部"); Company *leaf4 = new HRDepartment( "人力资源部"); mid1->Add(leaf3); mid1->Add(leaf4); root->Add(mid1); //分公司B Company *mid2 = new ConcreteCompany( "分公司B"); FinanceDepartment *leaf5 = new FinanceDepartment( "财务部"); HRDepartment *leaf6 = new HRDepartment( "人力资源部"); mid2->Add(leaf5); mid2->Add(leaf6); root->Add(mid2); root->Show( 0); delete leaf1; delete leaf2; delete leaf3; delete leaf4; delete leaf5; delete leaf6; delete mid1; delete mid2; delete root; return 0; } |