在现实生活中,存在很多“部分-整体”的关系,例如,大学中的部门与学院、总公司中的部门与分公司、学习用品中的书与书包、生活用品中的衣服与衣柜、以及厨房中的锅碗瓢盆等。在软件开发中也是这样,例如,文件系统中的文件与文件夹、窗体程序中的简单控件与容器控件等。对这些简单对象与复合对象的处理,如果用组合模式来实现会很方便。

模式定义

将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性(稳定)。

组合模式一般用来描述整体与部分的关系,它将对象组织到树形结构中,顶层的节点被称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点,树形结构图如下。
image.png
由上图可以看出,其实根节点和树枝节点本质上属于同一种数据类型,可以作为容器使用;而叶子节点与树枝节点在语义上不属于用一种类型。但是在组合模式中,会把树枝节点和叶子节点看作属于同一种数据类型(用统一接口定义),让它们具备一致行为。

优缺点

组合模式的主要优点有:

  1. 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
  2. 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;

其主要缺点是:

  1. 设计较复杂,客户端需要花更多时间理清类之间的层次关系;
  2. 不容易限制容器中的构件;
  3. 不容易用继承的方法来增加构件的新功能;

    示例

    ```cpp

    include

    include

    include

    include

using namespace std;

class Component { public: virtual void process() = 0; virtual ~Component() {} };

//树节点 class Composite : public Component { string name; list elements; public: Composite(const string& s) : name(s) {} void add(Component element) { elements.push_back(element); } void remove(Component element) { elements.remove(element); } void process() { //process current node

  1. //process leaf nodes
  2. for(auto& element : elements)
  3. element->process();
  4. }

}; //叶子结点 class Leaf : public Component { string name; public: Leaf(string s) : name(s) {} void process() { //process current node cout<< name <<endl; }
};

void Invoke(Component& c) { //… c.process(); //… }

int main() { Composite root(“root”); Composite treeNode1(“treeNode1”); Composite treeNode2(“treeNode2”); Composite treeNode3(“treeNode3”); Composite treeNode4(“treeNode4”); Leaf left1(“left1”); Leaf left2(“left2”);

  1. root.add(&treeNode1);
  2. treeNode1.add(&treeNode2);
  3. treeNode2.add(&left1);
  4. root.add(&treeNode3);
  5. treeNode3.add(&treeNode4);
  6. treeNode4.add(&left2);
  7. Invoke(root);

} ```