参考:
design-patterns-cpp/composite at master · JakubVojvoda/design-patterns-cpp · GitHub
一、什么是组合模式?
定义:将对象组合成树状的层次结构,使用户对
叶节点
(单个对象)和树节点
(组合对象)都有一致的访问性。
其中最关键的一点是,用户需要处理的对象能以层次结构表示。树本身就是一种具有递归性质的层次结构。如果树节点与叶节点都实现了相同的接口,那么无论树怎样扩展或裁减,都不会影响它被访问。
比如大部分国家的军队都采用层次结构管理。 每支部队包括几个师, 师由旅构成, 旅由团构成, 团可以继续划分为排。 最后, 每个排由一小队实实在在的士兵组成。 军事命令由最高层下达, 通过每个层级传递, 直到每位士兵都知道自己应该服从的命令。无论军队数量怎样变量,命令永远都下发。
二、实现
组合(Composite )模式包含以下主要角色:
- 抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。(总的抽象类或接口,定义一些通用的方法,比如新增、删除)
- 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件。
- 树枝构件(Composite)角色 / 中间构件:是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。
组合模式分为透明式的组合模式和安全式的组合模式。
1、透明方式
叶节点(Leaf)
与树节点(Composite)
都实现了抽象构件(Component)
接口。如果Component
中定义了Add()
、Remove()
、GetChild()
等方法,而叶节点都不需要,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。这样的好处就是客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。
/*
* C++ Design Patterns: Composite
* Author: Jakub Vojvoda [github.com/JakubVojvoda]
* 2016
*
* Source code is licensed under MIT License
* (for more details see LICENSE)
*
*/
#include <iostream>
#include <vector>
/*
* Component
* 为树叶构件和树枝构件声明公共接口
* 这里是透明式的组合模式,抽象构件还声明访问和管理子类的接口:Add\remove\getChild
*/
class Component
{
public:
virtual ~Component() {}
virtual Component *getChild( int )
{
return 0;
}
virtual void add( Component * ) { /* ... */ }
virtual void remove( int ) { /* ... */ }
virtual void operation() = 0;
};
/*
* Composite
* 是组合中的树节点对象,它有子节点,用于继承和实现抽象构件。
* 它的主要作用是存储和管理子部件
*/
class Composite : public Component
{
public:
~Composite()
{
for ( unsigned int i = 0; i < children.size(); i++ )
{
delete children[ i ];
}
}
Component *getChild( const unsigned int index )
{
return children[ index ];
}
void add( Component *component )
{
children.push_back( component );
}
void remove( const unsigned int index )
{
Component *child = children[ index ];
children.erase( children.begin() + index );
delete child;
}
void operation()
{
for ( unsigned int i = 0; i < children.size(); i++ )
{
children[ i ]->operation();
}
}
private:
std::vector<Component*> children;
};
/*
* Leaf
* 组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件
*/
class Leaf : public Component
{
public:
Leaf( const int i ) : id( i ) {}
~Leaf() {}
void operation()
{
std::cout << "Leaf "<< id <<" operation" << std::endl;
}
private:
int id;
};
int main()
{
Composite composite;
for ( unsigned int i = 0; i < 5; i++ )
{
composite.add( new Leaf( i ) );
}
composite.remove( 0 );
composite.operation();
return 0;
}
2、安全方式
仅在树节点中实现管理叶节点的方法(Add、Remove等)。
#include <iostream>
#include <vector>
class Component
{
public:
virtual ~Component() {}
virtual void operation() = 0;
};
class Composite : public Component
{
public:
~Composite()
{
for ( unsigned int i = 0; i < children.size(); i++ )
{
delete children[ i ];
}
}
Component *getChild( const unsigned int index )
{
return children[ index ];
}
void add( Component *component )
{
children.push_back( component );
}
void remove( const unsigned int index )
{
Component *child = children[ index ];
children.erase( children.begin() + index );
delete child;
}
void operation()
{
for ( unsigned int i = 0; i < children.size(); i++ )
{
children[ i ]->operation();
}
}
private:
std::vector<Component*> children;
};
class Leaf : public Component
{
public:
Leaf( const int i ) : id( i ) {}
~Leaf() {}
void operation()
{
std::cout << "Leaf "<< id <<" operation" << std::endl;
}
private:
int id;
};
int main()
{
Composite composite;
for ( unsigned int i = 0; i < 5; i++ )
{
composite.add( new Leaf( i ) );
}
composite.remove( 0 );
composite.operation();
return 0;
}
三、优缺点,适用场景
优点
- 组合模式使得客户端代码可以一致地处理单个对象和组合对象。
- 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足开闭原则。
缺点
- 设计较复杂,客户端需要花更多时间理清类之间的层次关系。
- 对于功能差异较大的类, 提供公共接口或许会有困难。