设计模式之组合模式

来源:互联网 发布:电脑软件转移工具 编辑:程序博客网 时间:2024/06/05 00:39

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、组合模式的好处?

答:组合模式定义了包含基本对象,组合对象的类层次结构,基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断地递归下去,客户代码中,任何用到基本对象的地方都可以使用组合对象。用户不用关心到底是处理一个叶节点还是处理一个组合组件。也就用不着为定义组合而写一些选择判断语句了。组合模式让客户可以一致地使用组合结构和单个对象。

代码实现如下;


 C++ Code 
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类可以作为中间结点,所以可以有添加函数。那么怎么添加呢?这个类中定义了一个链表,用来放添加的元素。



    相应的代码实现为:

 C++ Code 
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;
}
 上面的实现方式有缺点,就是内存的释放不好,需要客户自己动手,非常不方便。有待改进,比较好的做法是让ConcreteCompany类来释放。因为所有的指针都是存在ConcreteCompany类的链表中。C++的麻烦,没有垃圾回收机制。


0 0
原创粉丝点击