3.8 组合模式(代码见vs)
将对象组合成树形结构以表示"部分-整体"的层次结构,又称为部分-整体模式。
把一组相似的对象(这些对象呈现树形结构,具有继承关系)当作单一的对象处理,模糊了简单对象和复杂对象的概念,客户可以像处理单一对象一样来处理所有对象。
组合模式应用场景
希望客户端可以忽略简单对象与复杂对象的差异时,它们都是相似的对象,呈现继承关系;
对象层次具备整体和部分的关系,呈树形结构(比如树形菜单,公司组织架构等);
对象有一个或多个共同特点,它们有一个主线。
组合模式举例
比如:操作系统的文件系统就用了组合模式,大家平时在windows中操作文件的时候,无论是文件还是目录,无论是什么类型的文件,我们都可以用删除,添加,重命名等统一的操作,不需要区别对待。我们把各种类型的文件和目录都当作了单一的对象处理了。
为什么使用组合模式?
接下来我们就用文件系统来说明一下我们为什么要用组合模式来设计,有什么好处?
其实这样做最大的好处是对用户客户端的,假设现在我们有个文件系统类,客户端A与文件系统服务器B,此时文件服务器B添加了一个新的类型的文件比如xxx.exe,如果没有统一接口,客户端就需要添加新的方法用来操作exe,这就使得客户端与文件服务器深度耦合,但如果用了组合模式,那客户端就不需要在做什么改变,依旧和原来一样操作,实现了解耦,便于后期扩展修改。
案例:
A公司旗下有两个子公司 S1和S2,A公司还有四个部门(A1-A4),S1自己也有3个部门(S11 - S13)。A管理S1 -S2和A1- A4的方法与S1管理S11- S13的方法相同。也就是说,我们管理分公司、总公司部门、分公司部门这些对象的方法都一样,一样的接口。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
//组合模式
//部门抽象类,树的结点
class Component
{
string m_compname;//结点的名字
public:
Component(string name) :m_compname(name) {};
virtual ~Component() { cout << "父类析构:" << m_compname << endl; }
virtual void operation() = 0;//结点的操作方法,在当前抽象类中定义成接口
virtual void add(Component* comp) = 0;//添加子节点的方法
virtual void remove(Component* comp) = 0;//移除子节点的方法
virtual Component* getChild(int index) = 0;//根据下标索引返回子节点对象
virtual string getName() { return m_compname; }//获取名称
};
//叶子节点类,它是最下面一层,没有子节点了,所以不需要子节点相关的方法
class Leaf :public Component
{
public:
Leaf(string name) :Component(name) {};
virtual void operation() { cout << "这是" << getName() << ",是叶子部门" << endl; }
//接下来实现父类中的纯虚函数,否则当前类就是抽象类,就无法创建对象了,因为叶子节点用不到这些方法,所以函数体为空
virtual void add(Component* comp) {};//添加子节点的方法
virtual void remove(Component* comp) {};//移除子节点的方法
virtual Component* getChild(int index) { return nullptr; };//根据下标索引返回子节点对象
};
//复合结点,节点下面还有子节点
class Composite:public Component
{
vector<Component*> m_vecComp;//存放子节点的容器
public:
Composite(string name) :Component(name) {};
~Composite()//重写父类的析构,析构旗下的所有子节点
{
for (auto c:m_vecComp)
{
cout << "delete" << c->getName() << endl;
delete c;
}
m_vecComp.clear();//清空容器
}
virtual void operation() { cout << "这是" << getName() << ",是复合部门" << endl; }
virtual void add(Component* comp) { m_vecComp.push_back(comp); };//添加子节点的方法
virtual void remove(Component* comp) //移除子节点的方法
{
//从vector中移除某个元素,先找到,再删除erase,需要使用迭代器
for (vector<Component*>::iterator it = m_vecComp.begin(); it != m_vecComp.end(); it++)
{
if ((*it)==comp)
{
delete comp;//先释放对象
it = m_vecComp.erase(it);//更新迭代器,否则会导致迭代器失效
break;
}
}
};
virtual Component* getChild(int index)//根据下标索引返回子节点对象
{
return m_vecComp[index];
}
};
void test01()
{
//构造所需要的对象
Component* A = new Composite("总公司A");
Component* S1 = new Composite("分公司S1");
Component* S2 = new Composite("分公司S2");
Component* A1 = new Composite("总公司部门A1");
Component* A2 = new Composite("总公司部门A2");
Component* A3 = new Composite("总公司部门A3");
Component* A4 = new Composite("总公司部门A4");
Component* S11 = new Leaf("分公司S1的部门S11");
Component* S12 = new Leaf("分公司S1的部门S12");
Component* S13 = new Leaf("分公司S1的部门S13");
//接下来让所有节点构成一个树的关系
A->add(S1);
A->add(S2);
A->add(A1);
A->add(A2);
A->add(A3);
A->add(A4);
S1->add(S11);
S1->add(S12);
S1->add(S13);
//展示
A->operation();
S1->operation();
S11->operation();
cout << A->getChild(1)->getName() << endl;//查看总公司的下标为1的结点,应该是S2
A->remove(S2);//总公司移除S2分公司
cout << A->getChild(1)->getName() << endl;//查看总公司的下标为1的结点,应该是A1
delete A;
}
总结:
优点:
可以利用多态和递归机制更方便的使用复杂树结构。
遵循开闭原则,无需更改现有的代码,就可以在应用中添加新元素,使其成为对象树的一部分。
缺点:
对于功能差异较大的类,提供公共接口困难。
练习:
#include <iostream>
#include <vector>
using namespace std;
class Ancestor
{
protected:
string m_name;
public:
Ancestor(string name) :m_name(name) {};
virtual ~Ancestor() { cout << "析构父类" << m_name << endl; }
virtual void operation() = 0;
virtual void add(Ancestor* anc) = 0;
virtual void remove(Ancestor* anc) = 0;
virtual Ancestor* getChild(int index) = 0;
virtual string getName() { return m_name; }//获取名称
};
//叶子类
class Fourth :public Ancestor
{
public:
Fourth(string name) :Ancestor(name) {};
virtual ~Fourth() { cout << "析构叶子节点" << m_name << endl; }
virtual void operation()
{
cout << getName() << "属于叶子部门" << endl;
}
virtual void add(Ancestor* anc) {};
virtual void remove(Ancestor* anc) {};
virtual Ancestor* getChild(int index) { return nullptr; };
};
//复合类
class Compo :public Ancestor
{
vector< Ancestor*>vec;
public:
Compo(string name) :Ancestor(name) {};
virtual ~Compo()
{
for (auto v : vec)
{
delete v;
}
vec.clear();
}
virtual void operation()
{
cout << getName() << "属于复合部门" << endl;
}
virtual void add(Ancestor* anc)
{
vec.push_back(anc);
}
virtual void remove(Ancestor* anc)
{
for (vector< Ancestor*>::iterator it = vec.begin(); it != vec.end(); it++)
{
if ((*it) == anc)
{
delete anc;
it = vec.erase(it);
}
}
}
virtual Ancestor* getChild(int index)
{
return vec[index];
}
};
void test02()
{
Ancestor* zuxian = new Compo("第一代祖先");
Ancestor* daye = new Compo("第二代大爷");
Ancestor* yeye = new Compo("第二代爷爷");
Ancestor* bobo = new Compo("第三代伯伯");
Ancestor* baba = new Compo("第三代爸爸");
Ancestor* shushu = new Compo("第三代叔叔");
Ancestor* tangge = new Fourth("第四代堂哥");
Ancestor* tangjie = new Fourth("第四代堂姐");
Ancestor* lizhe = new Fourth("第四代李哲");
Ancestor* wo = new Fourth("第四代我");
Ancestor* lihua = new Fourth("第四代李华");
Ancestor* lihui = new Fourth("第四代李辉");
zuxian->add(daye);
zuxian->add(yeye);
yeye->add(bobo);
yeye->add(baba);
yeye->add(shushu);
bobo->add(tangge);
bobo->add(tangjie);
baba ->add(lizhe);
baba->add(wo);
shushu->add(lihua);
shushu->add(lihui);
yeye->operation();
shushu->operation();
wo->operation();
cout << zuxian->getChild(0)->getName() << endl;
zuxian->remove(daye);
cout << zuxian->getChild(0)->getName() << endl;
delete zuxian;
}