组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ ——Erich Gamma et. al.
伊始
组合(Composite)模式,又叫作部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。
结构模型
■ Component:抽象的组件对象,为组合中的对象声明接口,让客户端可以通过这个接口来访问和管理整个对象结构,可以在里面为定义的功能提供缺省的实现。
■ Leaf:叶子节点对象,定义和实现叶子对象的行为,不再包含其他的子节点对象。
■ Composite:组合对象,通常会存储子组件,定义包含子组件的那些组件的行为,并实现在组件接口中定义的与子组件有关的操作。
■ Client:客户端,通过组件接口来操作组合结构里面的组件对象。
实现域
Component
定义
struct Component {
virtual ~Component(){}
ABSTRACT(void process());
virtual void add(Component*) {
return;
}
virtual void remove(Component*) {
return;
}
};
注意,上面ABSTRACT
是一个C++封装宏,表示定义的方法是纯虚函数,亦即接口,原型如下:
#define ABSTRACT(...) virtual __VA_ARGS__ = 0
由于叶子对象没有add
和remove
方法的,这里在抽象类提供了一份默认实现。
- 叶子对象
Leaf
定义
struct Leaf : Component {
OVERRIDE(void process()) {
/* do process self */
}
};
其中,上面OVERRIDE
也是一个C++封装宏,表示实现或者覆写父类的接口(或方法),原型如下:
#define OVERRIDE(...) virtual __VA_ARGS__ override
- 组合对象
Composite
定义
using Components = std::list<Component*>;
struct Composite : Component {
explicit Composite(const std::string& name) : name_{name} {}
OVERRIDE(void process()) {
/* do process self */
/* process child */
for (auto& c : components_) {
c->process();
}
}
OVERRIDE(void add(Component* component)) {
components_.push_back(component)
}
OVERRIDE(void remove(Component* component)) {
components_.remove(component)
}
private:
std::string name_;
Components components_;
};
- 用户使用
这里假设用户需要设计一个管理商品类别树的场景,如下
- 商品
- 服装
- 男装
- 女装
- 书籍
- 中文书籍
- 外文书籍
使用组合模式,如下
void process(Component& component) {
component.process();
}
int main() {
Composite root("商品");
Composite dress("服装");
Composite book("书籍");
Leaf man_dress("男装");
Leaf women_dress("女装");
Leaf book_zh("中文书籍");
Leaf book_foreign("外文书籍");
root.add(&dress);
dress.add(&man_dress);
dress.add(&women_dress);
root.add(&book);
book.add(&book_zh);
book.add(&book_foreign);
process(root);
}
优缺点
- 优点
1. 定义了包含基本对象和组合对象的类层次结构,基本对象可以被组合成复杂的组合对象,而组合对象又可以组合成更复杂的组合对象,可以不断地递归组合下去,从而构成一个统一的组合对象的类层次结构。
2. 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
3. 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;
- 缺点
1. 不容易限制容器中的构件;
2. 不容易用继承的方法来增加构件的新功能;
End
学习是输入,分享是输出,坚持持续分享。。。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。