这是个人学习编程模式的系列学习笔记第十四篇。
采用Qt Creator进行编写,但尽量采用C++基础语法。
组合模式(Composite Pattern):将对象组合成树形结构以表示“部分 -整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
组合模式可以方便构建树型结构组织,比如总公司、分公司、办事处,每一级有可以有自己的具体部门,比如财务,人事。可以将各级公司作为树干或树枝,在树干或树枝上可以生长树叶(具体的部门,比如财务部,人事部,这些树叶不具备下一级),也可以生长下一级树枝(分公司、办事处)。
可以将全部部门按统一的抽象接口来实现。比如人事部没有下一级部门,依然提供新增下一级部门的接口,但不实现具体的功能。这种方式称为透明方式。这样的方式不需要调用者判断对象是否有对应的方法。
另外一种方式是安全方式。统一接口只定义大家都必须有的接口,这样的方式,调用者需要判断调用的对象是否具备这个功能。
场景描述
文件管理系统就是这样的树形结构,目录就是树枝,文件是树叶。目录下面可以有子目录,也可以有文件。
创建一个简单的文件管理系统,模拟对目录和文件的管理。
设计思路
将目录或文件抽象为一个统一的通用文件对象,可以有add、delete、show。add可以在下级增加一个目录或文件,delete可以删除下级的一个空目录或文件,show显示当前节点的全部下级内容。
UML
代码
#include <iostream>
#include <list>
using namespace std;
class Component
{
public:
Component(string name) : m_name(name) {}
virtual void add(Component *c) = 0;
virtual void del(Component *c) = 0;
virtual void show(int depth) = 0;
virtual string whoami() {return "Component";}
public:
string m_name;
};
class File : public Component
{
public:
File(string name) : Component(name) {}
void add(Component *c) {}
void del(Component *c) {}
void show(int depth)
{
for(int i = 0; i <= depth; i++) cout<<"--";
cout<<"file: "<<m_name<<"\n";
}
virtual string whoami() {return "File";}
};
class Directory : public Component
{
public:
Directory(string name) : Component(name) {}
void add(Component *c) { m_children.push_back(c);}
void del(Component *c)
{
string str = c->whoami();
if(str == "File")
{
m_children.remove(c);
cout<<"Del a file success!\n";
}
else if(str == "Directory")
{
if(static_cast<Directory *> (c)->m_children.empty()) m_children.remove(c);
else cout<<"Cann\'t del nonempty directory!\n";
}
}
void show(int depth)
{
for(int i = 0; i <= depth; i++) cout<<"--";
cout<<"directory: "<<m_name<<"\n";
list<Component *>::iterator iter;
for(iter = m_children.begin(); iter != m_children.end() ; iter++)
{
(*iter)->show(depth + 1);
}
}
virtual string whoami() {return "Directory";}
public:
list<Component *> m_children;
};
int main()
{
Directory dir("firstDIR");
Directory dir1("secondDIR");
Directory dir2("thirdDIR");
File file("file");
dir.add(&dir1);
dir1.add(&file);
dir1.add(&dir2);
dir.show(0);
dir.del(&dir1);
dir1.del(&file);
dir.show(0);
return 0;
}