简介
将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
掌握组合模式的重点是要理解清楚 “部分/整体” 还有 ”单个对象“ 与 “组合对象” 的含义。
又称部分—整体模式,使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
组合模式可以让客户端像修改配置文件一样简单的完成本来需要流程控制语句来完成的功能。
经典案例:系统目录结构,网站导航结构等。
- 关于分级数据结构的一个普遍性的例子是你每次使用电脑时所遇到的:文件系统。文件系统由目录和文件组成。每个目录都可以装内容。目录的内容可以是文件,也可以是目录。
- 目录——容器
- 文件——叶节点
角色
Component 是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件。
Leaf 在组合中表示叶子结点对象,叶子结点没有子结点。
Composite 定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关操作,如增加(add)和删除(remove)等。
两种形式
组合模式的实现根据所实现接口的区别分为两种形式:
- 透明组合模式
- 安全组合模式
图解
适用性
你想表示对象的部分-整体层次结构。
你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
优点
可以直观地定义分层次的复杂对象,表示“整体—部分”,让客户忽略了层次的差异,方便对整个层次结构进行控制。
简化客户端代码:客户可以一致地使用一个组合结构或者单个对象,无需考虑是单个对象还是整个组合结构。
增加新容器和叶子都十分方便,符合“开闭原则”。
为树形结构的面向对象实现提供了一种灵活的解决方案。
缺点
- 增加新构件时,很难对容器中的构件类型进行限制。
- 设计更加抽象。对象的业务规则如果很复杂,则实现组合模式具有很大的挑战性。
透明组合模式
图解
关键
- 在Component里面声明所有的用来管理子类对象的方法,包括add、remove等方法。
好处
- 所有的构件类都有相同的接口。客户端可以等同的对待所有的对象。
缺点
- 不够安全。因为在在Component里面声明所有的用来管理子类对象的方法,所以在叶子中存在同样的方法(虚函数)。但叶子不可能有下一层对象,因此add、remove等方法没有意义。在编译期间不会出错,只会在运行期间才出错。
偏向于使用透明组合模式:客户可以一致地使用一个组合结构或者单个对象,无需考虑是单个对象还是整个组合结构。
安全组合模式
图解
关键
- 在Composite里面声明所有的用来管理子类对象的方法,包括add、remove等方法。
好处
- Component里面没有声明所有的用来管理子类对象的方法,叶子对象同样没有管理子类对象的方法,因而如果客户端对叶子使用add、remove等方法时,编译期间就会出错。
缺点
- 不够透明。组合构件和叶子具有不同的接口。
实例
- 文件夹和文件是我们管理文件的方式,一个文件夹下可以有多个文件夹和多个文件……
安全模式
- component.h
#ifndef _COMPONENT_H_
#define _COMPONENT_H_
#include <iostream>
#include <string>
using namespace std;
class Component
{
public:
Component(string name) : name(name) {}
virtual ~Component() {}
virtual void Display(int indent) = 0; // 展示
protected:
string name;
};
#endif // _COMPONENT_H_
- leaf.h
#ifndef _LEAF_H_
#define _LEAF_H_
#include "component.h"
class Leaf : public Component
{
public:
Leaf(string name) : Component(name) {}
virtual ~Leaf() {}
void Display(int indent) {
string newStr(indent, '-');
cout << newStr << " " << name << endl;
}
};
#endif // _LEAF_H_
- composite.h
#ifndef _COMPOSITE_H_
#define _COMPOSITE_H_
#include <list>
#include "component.h"
class Composite : public Component
{
public:
Composite(string name) : Component(name) {}
virtual ~Composite() {
while (!elements.empty()) {
list<Component*>::iterator it = elements.begin();
if (*it != nullptr) {
delete *it;
*it = nullptr;
}
elements.erase(it);
}
}
void Add(Component *cmpt) {
elements.push_back(cmpt);
}
void Remove(Component *cmpt) {
list<Component*>::iterator it = elements.begin();
while (it != elements.end()) {
if (*it == cmpt) {
delete *it;
*it = nullptr;
elements.erase(it);
break;
}
++it;
}
}
Component* GetChild(int index) {
if (index >= elements.size())
return nullptr;
list<Component*>::iterator it = elements.begin();
while (--index > 0) {
++it;
}
return *it;
}
// 显示
void Display(int indent) {
string newStr(indent, '-');
cout << newStr << "+ " << name << endl;
// 递归
list<Component*>::iterator it = elements.begin();
while (it != elements.end()) {
(*it)->Display(indent + 2);
++it;
}
}
private:
list<Component* > elements;
};
#endif // _COMPOSITE_H_
- main.cpp
#include<iostream>
#include"component.h"
#include"composite.h"
#include"leaf.h"
using namespace std;
int main(int argc, char* argv[]) {
Composite* pRoot = new Composite("C++学习资料");
Composite* composite1 = new Composite("第一单元");
composite1->Add(new Leaf("1.1"));
composite1->Add(new Leaf("1.2"));
pRoot->Add(composite1);
Composite *composite2 = new Composite("第二单元");
composite2->Add(new Leaf("2.1"));
composite2->Add(new Leaf("2.2"));
pRoot->Add(composite2);
pRoot->Add(new Leaf("第三单元"));
pRoot->Add(new Leaf("第四单元"));
Component *leaf1 = new Leaf("2.3");
composite2->Add(leaf1);
composite2->Remove(leaf1);
pRoot->Display(1);
// 防止内存泄漏
if (pRoot != nullptr) {
delete pRoot;
pRoot = nullptr;
}
getchar();
return 0;
}
透明模式
- component.h
#ifndef _COMPONENT_H_
#define _COMPONENT_H_
#include <iostream>
#include <string>
using namespace std;
class Component
{
public:
Component(string name) : name(name) {}
virtual ~Component() {}
virtual void Add(Component*) = 0;
virtual void Remove(Component*) = 0;
virtual Component* GetChild(int index) = 0;
virtual void Display(int indent) = 0; // 展示
protected:
string name;
};
#endif // _COMPONENT_H_
- leaf.h
#ifndef _LEAF_H_
#define _LEAF_H_
#include "component.h"
class Leaf : public Component
{
public:
Leaf(string name) : Component(name) {}
virtual ~Leaf() {}
void Add(Component* com) {
cout << "can't" << endl;
}
void Remove(Component* com) {
cout << "can't" << endl;
}
Component* GetChild(int index) {
cout << "can't" << endl;
return nullptr;
}
void Display(int indent) {
string newStr(indent, '-');
cout << newStr << " " << name << endl;
}
};
#endif // _LEAF_H_
- composite.h
#ifndef _COMPOSITE_H_
#define _COMPOSITE_H_
#include <list>
#include "component.h"
class Composite : public Component
{
public:
Composite(string name) : Component(name) {}
virtual ~Composite() {
while (!elements.empty()) {
list<Component*>::iterator it = elements.begin();
if (*it != nullptr) {
delete *it;
*it = nullptr;
}
elements.erase(it);
}
}
void Add(Component *cmpt) {
elements.push_back(cmpt);
}
void Remove(Component *cmpt) {
list<Component*>::iterator it = elements.begin();
while (it != elements.end()) {
if (*it == cmpt) {
delete *it;
*it = nullptr;
elements.erase(it);
break;
}
++it;
}
}
Component* GetChild(int index) {
if (index >= elements.size())
return nullptr;
list<Component*>::iterator it = elements.begin();
while (--index > 0) {
++it;
}
return *it;
}
void Display(int indent) {
string newStr(indent, '-');
cout << newStr << "+ " << name << endl;
// 递归
list<Component*>::iterator it = elements.begin();
while (it != elements.end()) {
(*it)->Display(indent + 2);
++it;
}
}
private:
list<Component* > elements;
};
#endif // _COMPOSITE_H_
- main.cpp
#include<iostream>
#include"component.h"
#include"composite.h"
#include"leaf.h"
using namespace std;
int main(int argc, char* argv[]) {
Component* pRoot = new Composite("C++学习资料");
Component* composite1 = new Composite("第一单元");
composite1->Add(new Leaf("1.1"));
composite1->Add(new Leaf("1.2"));
pRoot->Add(composite1);
Component *composite2 = new Composite("第二单元");
composite2->Add(new Leaf("2.1"));
composite2->Add(new Leaf("2.2"));
pRoot->Add(composite2);
pRoot->Add(new Leaf("第三单元"));
pRoot->Add(new Leaf("第四单元"));
Component *leaf1 = new Leaf("2.3");
composite2->Add(leaf1);
composite2->Remove(leaf1);
pRoot->Display(1);
// 防止内存泄漏
if (pRoot != nullptr) {
delete pRoot;
pRoot = nullptr;
}
getchar();
return 0;
}