【转】设计模式学习笔记(8)-组合

组合(Composite)模式还是比较重要的,何谓组合?在Word中有这样一个命令


这里的组合和设计模式中的组合是一样的意思。我们都知道在word中将一些对象组合以后这些对象就可以视作一个对象,可以放大缩小移动等,和那些基本的线段、矩形、椭圆等图元没什么两样。这就是组合模式所要达到的目的。其意图:将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性
这样一来可以大大简化客户的操作,求同存异,使得程序的编写变得简单。
从类图上来看,组合模式十分类似于数据结构中的树形结构,学过数据结构的都知道,树型结构有两种节点:叶节点和其它节点。叶节点是没有子节点的,本例中的组合模式中的叶节点就是那些基本图元,而其它节点则是基本图元的组合,他们具有基本图元所拥有的所有性质,但也包括自己独有的性质主要是对子节点的操作。
类图:

这个例子中,Picture就是一个组合对象,而Graphic是所有对象的父类。Picture里面可以存储多个Graphic对象,可以是Line,Rectangle甚至是Picture自己。对这个例子进一步抽象:

这里就很清楚了,我们只有两种对象:叶子对象和组合对象。而它们都属于组件(Component)。
从这个例子我们写出一点框架代码:

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
class Component
{
public:
    virtual void draw() = 0;
    virtual void add(Component* comp) { }
    virtual Component* getComponent(int n){ return 0; }
};
class Leaf1 : public Component
{
public:
    void draw()
    {
       cout << "Draw a leaf1" << endl;
    }
};
class Leaf2 : public Component
{
public:
    void draw()
    {
       cout << "Draw a leaf2" << endl;
    }
};
class Composite : public Component
{
private:
    vector<Component*>* _compList;
public:
    Composite()
    {
        _compList = new vector<Component*>();
    }
    void draw()
    {
        for(vector<Component*>::iterator it = _compList->begin(); it != _compList->end(); it++)
        {
            (*it)->draw();
        }
    }
    void add(Component* comp)
    {
        _compList->push_back(comp);
    }
    Component* getComponent(int n)
    {
        return _compList->at(n);
    }
};

这里有两个叶子,Leaf1和Leaf2,注意叶子节点没有重载父类中的add和getComponent方法,这点会在后来介绍。还有一个组合类,Composite,里面维持着一个Component列表,调用draw方法
时就对该列表中的所有对象逐个调用draw方法。
main函数里这样写道:

1
2
3
4
5
6
7
8
9
10
Component* cpn = new Composite();
cpn->add(new Leaf1);
cpn->add(new Leaf2);
 
Component* c = new Composite();
c->add(new Leaf2);
c->add(new Leaf2);
 
cpn->add(c);
cpn->draw();

这意思很明了了,不用介绍。最终输出为:

1
2
3
4
Draw a leaf1
Draw a leaf2
Draw a leaf2
Draw a leaf2

结果正是我们想要的。
注意在main函数中我们想操作叶子对象一样操作组合对象,使得添加、修改等操作十分统一,体现出来组合模式的威力。

我们回到刚才所说的问题上:叶子节点不应该add等方法而组合对象有这些方法,应该如何对待?我们使用了比较胖的接口,把这些方法放到父类Component中,这其实违反了设计原则中的
一个类只能定义那些对它的子类有意义的操作
。这里不得已而未知,应为我们不能将叶子与组合分离出来,否则这个设计模式就毫无意义。为了达到统一的操作体验我们抛弃了安全性(叶
子中可能会调用add方法导致出错)而选择了透明性(打通叶子和组合对象的操作)。在面向对象程序设计中,我们经常要面临这样的抉择,关键原则是:选取最有效的方法,而不是墨守成规。

最后我们来看看组合模式的适用性:

  • 想表示对象的部分-整体层次
  • 希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象

组合模式往往和Iterator、Visitor、Flyweight、Decorator等模式一起使用,主要用在GUI设计等方面。

posted on 2013-03-05 14:41  TheKingOfKingFish  阅读(136)  评论(0)    收藏  举报

导航