C++实现设计模式——组合(Composite)模式
- 组合模式定义
把一组相似的对象当作一个单一的对象,它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
- 组合模式应用场景
- 希望客户端可以忽略组合对象与单个对象的差异时;
- 对象层次具备整体和部分,呈树形结构(比如树形菜单,公司组织架构等)
- 有一个或多个共同特点。它们有一个主线。
- 组合模式举例
组合模式在日常中其实经常遇到,只是有时候我们不知道这种就是组合模式罢了。比如:文件系统,大家平时在windows里面无论是对于什么后缀的文件还是目录,我们都可以用删除,添加,重命名等统一的操作,不需要区别对待。手机菜单,话机菜单等等一切目录菜单也是一样的,我们可以用统一的接口操作。
-
为什么使用组合模式
接下来我们就用文件系统来说明一下我们为什么要用组合模式来设计,有什么好处?
其实这样做最大的好处是对用户客户端的,假设现在我们有个文件系统类,客户端A与文件系统服务器B,此时文件服务器B添加了一个新的类型的文件比如xxx.exe,如果没有统一接口,客户端就需要添加c新的用来操作exe的逻辑,这就使得客户端与文件服务器深度耦合,但如果用了组合模式,那客户端就不需要在做什么改变,依旧和原来一样操作,方便解耦,后期扩展修改。
用代码说明一下:
#include<iostream>
#include<vector>
using namespace std;
class fileSystem {
public:
string name;
virtual void add(fileSystem* node) = 0;
virtual void rename(string newname) = 0;
virtual void remove(fileSystem* node) = 0;
virtual fileSystem* enter() = 0;
virtual fileSystem* GetNode(int index) {}
void showName() {
cout <<"文件名字:"<<name<<endl;
}
};
class dir :public fileSystem {
private:
vector <fileSystem*> file;
public:
dir(string name) {
this->name = name;
}
void add(fileSystem* node) {
file.push_back(node);
}
void remove(fileSystem* node) {
for(auto it=file.begin();it!=file.end();it++) {
if(*it == node)
file.erase(it);
}
}
void rename(string newName) {
name = newName;
}
fileSystem* enter() { //目录点击去就是显示里面有什么
cout<<"打开名叫"<<name<<"的目录"<<endl;
for(auto it=file.begin();it!=file.end();it++) {
cout<<(*it)->name<<endl;
}
return this;
}
fileSystem* GetNode(int index) { //电脑会进行按键扫描返回是第几个文件也就是index
return file[index];
}
};
class cppfile :public fileSystem {
public:
cppfile(string name) {
this->name = name;
}
void add(fileSystem* node) {
cout<<"不支持这个操作"<<endl;
}
void remove(fileSystem* node) {
cout<<"不支持这个操作"<<endl;
}
void rename(string newName) {
name = newName;
}
fileSystem* enter() { //文件就是打开
cout<<"打开名叫"<<name<<"的文件"<<endl;
return this;
}
};
class txtfile :public fileSystem {
public:
txtfile(string name) {
this->name = name;
}
void add(fileSystem* node) {
cout<<"不支持这个操作"<<endl;
}
void remove(fileSystem* node) {
cout<<"不支持这个操作"<<endl;
}
void rename(string newName) {
name = newName;
}
fileSystem* enter() { //文件就是打开
cout<<"打开名叫"<<name<<"的文件"<<endl;
return this;
}
};
int main() {
fileSystem * d = new dir("1dir"); //创建根目录1dir
d->showName();
d->add(new txtfile("2txt")); //创建三个类型文件
d->add(new cppfile("3cpp"));
d->add(new dir("4dir"));
d = d->enter(); //进入1dir目录,展示下面有哪些文件
d = d->GetNode(2); //按键扫描,表示接下来操作的是哪个位置上的文件
d = d->enter(); //进入4dir目录,展示下面有哪些文件
d->showName();
d->add(new cppfile("5cpp")); //创建5cpp
d = d->GetNode(0); //按键扫描,表示接下来操作的是哪个位置上的文件
d = d->enter();
d->showName();
return 0;
}
add,enter,showname这些接口都是统一的,客户端不需要管到底是谁的,文件系统的Composite会帮它自动调用合适的接口。
它的结构是下面这样的:
fileSystem类就是Component,它抽象了所有公共的方法,dir类就是Composite,他用来管理所有的叶子节点,我们的cpp,txt,exe等等都是Leaf,后期如果继续添加文件类型,只要继承于fileSystem,放到dir里面管理,就完全没有问题,客户端那边的接口也不需要改变。
以上就是组合模式的使用,即优点介绍。