什么是组合模式?
- 组合模式是一种结构型设计模式,通过组合多个相同或不同的对象最终构成的树形结构。
- 具有整体-部分关系的层次结构,无论是对叶子节点还是对于树节点的调用接口具有一致性。
- 组合模式又称为部分-整体模式
如何理解组合模式?
这里以电脑的“文件夹”和“文件”为例:
一个文件夹中可以包含零到多个文件夹和文件,而每个子文件夹又可以包含孙子文件夹和文件,以此类推。
那如何知道当前文件夹的大小呢?这时候我们可以把文件夹和文件的大小抽象成一个接口。文件夹作为树(Composite)节点,文件作为叶子(leaf)节点
- 文件夹的大小=所有当前子文件夹的大小+所有当前子文件大小
- 文件大小=当前文件大小
看到下面的图,有没有觉得这好像和Qt的元对象树很像呢!!!
代码描述
- 首选是需要统一的抽象接口
class I_Component {
public:
virtual ~I_Component() {}
virtual void Add(std::shared_ptr<I_Component> cmpt){};
virtual void Remove(std::shared_ptr<I_Component> cmpt){};
virtual void Operation() = 0;
};
- 定义树节点,输节点具有遍历当其任意子节点的功能
class CompositeEntity : public I_Component {
public:
CompositeEntity(const string& s) : name(s) {}
virtual void Add(std::shared_ptr<I_Component> element) override {
elements.push_back(element);
}
virtual void Remove(std::shared_ptr<I_Component> element) override {
elements.remove(element);
}
virtual void Operation()
{
cout << name << endl;
cout << "this is Composite has children size=" << elements.size() << endl;
for (auto& leaf : elements) {
leaf->Operation();
}
}
private:
string name;
list<std::shared_ptr<I_Component>> elements;
};
- 定义叶子点
class LeafEntity : public I_Component {
public:
LeafEntity(string s) : name(s) {}
virtual void Operation(){
cout << "this is leaf:" << name<< endl;
}
private:
string name;
};
这时候当我们想获取某一个节点下的详细信息,只需要调用抽象接口Operation方法即可
static void ShowComponent(std::shared_ptr<I_Component> c)
{
c->Operation();
cout << "\r" << endl;
}
int main()
{
std::shared_ptr<I_Component> pRoot = std::make_shared<CompositeEntity>("pRoot");
std::shared_ptr<I_Component> pPart1 = std::make_shared<CompositeEntity>("pPart1");
pPart1->Add(std::make_shared<LeafEntity>("Leaf1-1"));
pPart1->Add(std::make_shared<LeafEntity>("Leaf1-2"));
std::shared_ptr<I_Component> pPart2 = std::make_shared<CompositeEntity>("pPart2");
std::shared_ptr<I_Component> pPart2_1 = std::make_shared<CompositeEntity>("pPart2_1");
pPart2->Add(std::make_shared<LeafEntity>("Leaf2-1"));
pPart2->Add(std::make_shared<LeafEntity>("Leaf2-2"));
pPart2->Add(pPart2_1);
std::shared_ptr<I_Component> leaf_999 = std::make_shared<LeafEntity>("leaf_999");
pPart2_1->Add(leaf_999);
pRoot->Add(pPart1);
pRoot->Add(pPart2);
//获取根节点下的数据
ShowComponent(pRoot);
//获取某一个节点下的数据
ShowComponent(pPart2_1);
return 0;
}
组合模式的思考
对于系统对象层次具备整体和部分的特征,并且可以构造出呈树形结构,这属于组合模式的适用场景,实际场景如公司的组织架构,电脑的目录结构。
通过组合模式可以最终把不同的叶子节点,抽象成相同的节点,达成了忽略整体-部分差异,最终给用户的是统一的抽象接口。
优势:
- 可以结合C++的多态和递归设计出复杂的树形结构,但接口单一;
- 符合开闭原则,当需要增加新的节点或树时,无需修改原有代码,并且可以成为现有树节点的一部分;
劣势:
- 如果本质上叶子节点差异巨大时,很难抽象,只能通过适配器or桥接的形式给叶子节点提供适配接口,代码会变得难以理解;