C++组合模式

简述

组合模式(Composite Pattern)组合多个对象形成树形结构以表示具有“整体-部分”关系的层次结构。组合模式对单个对象(即:叶子构件)和组合对象(即:容器构件)的使用具有一致性,组合模式又被称为“整体-部分”(Part-Whole)模式,属于对象结构型模式。

  • 简述
  • 模式结构
  • 透明组合模式和安全组合模式
  • 优缺点
  • 使用场景
  • 案例分析
  • 代码实现
    • 透明组合模式
    • 安全组合模式

版权所有:一去丶二三里,转载请注明出处:http://blog.csdn.net/liang19890820

模式结构

UML 结构图(透明组合模式):

UML 结构图(安全组合模式):

  • 抽象构件(Component):为叶子构件和容器构件对象定义接口,可以包含所有子类共有行为的声明和实现。在抽象构件中,声明了访问及管理子构件的接口(例如:Add()、Remove()、GetChild() 等)。
  • 叶子构件(Leaf):叶子节点没有子节点。它实现了 Component 中定义的行为,对于访问及管理子构件的接口,可以通过异常等方式进行处理。
  • 容器构件(Composite):容器节点包含子节点(可以是叶子构件,也可以是容器构件)。它提供了一个集合用于存储子节点,实现了 Component 中定义的行为,包括访问及管理子构件的接口,在其业务方法中可以递归调用其子节点的业务方法。

透明组合模式和安全组合模式

根据 Component 的定义形式,可将组合模式分为两种形式:

  • 透明组合模式
  • 安全组合模式

透明组合模式包含以下特点:

  • 在 Component 中定义了用于访问和管理子构建的接口,这样做的好处是确保所有的构件类都有相同的接口。
  • 在 Client 看来,Leaf 与 Composite 所提供的接口一致,Client 可以相同地对待所有的对象。

安全组合模式包含以下特点:

  • 在 Component 中不定义任何用于访问和管理子构建的接口,而在 Composite 中声明并实现。
  • 这种做法是安全的,因为不需要向 Leaf 提供这些管理成员对象的接口,对于 Leaf 来说,Client 不可能调用到这些接口。

透明组合模式的缺点是不够安全,因为 Leaf 和 Composite 在本质上是有区别的。Leaf 不可能有下一个层级,因此为其提供 Add()、Remove()、GetChild() 等接口没有意义。这在编译阶段不会出错,但在运行阶段如果调用这些接口可能会出错(如果没有提供相应的异常处理)。

安全组合模式的缺点是不够透明,因为 Leaf 和 Composite 具有不同的接口,且 Composite 中那些用于访问和管理子构建的接口没有在 Component 中定义,因此 Client 不能完全针对抽象编程,必须有区别地对待 Leaf 和 Composite。

PS: 透明组合模式是组合模式的标准形式,但在实际应用中,安全组合模式的使用频率也非常高。

优缺点

优点:

  • 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让 Client 忽略了层次的差异,方便对整个层次结构进行控制。
  • Client 可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了 Client 的代码。
  • 在组合模式中,增加新的叶子构件和容器构件很方便,无须对现有类进行任何修改,符合“开闭原则”。
  • 为树形结构提供了一种灵活的解决方案,通过递归组合容器对象和叶子对象,可以形成复杂的树形结构,但对树形结构的控制却非常简单。

缺点:

  • 使设计变得更加抽象,对象的业务规则如果很复杂,则实现组合模式具有很大挑战性,而且不是所有的方法都与叶子对象子类都有关联。

使用场景

  • 表示对象的“整体-部分”层次结构(树形结构)
  • 希望用户忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象。

案例分析

只要有人的地方就有恩怨,有恩怨就会有江湖,人就是江湖!
– 金庸《笑傲江湖》

江湖公司由任我行一手创建,理所当然,他就是董事长。下设总经理一职,原本让令狐冲担任,却被婉拒,所以只能由自己兼任。再往下就是各事业部:日月神教、五岳剑派、以及其他门派等。

日月神教的头儿叫做教主(东方不败),底下有光明左右使、十大长老、堂主、舵主、香主等。

五岳剑派的头儿叫做盟主(左冷蝉),各派分别为:嵩山(左冷蝉)、泰山(天门道长)、衡山(莫大)、华山(岳不群)、恒山(定闲师太),各头目被称为掌门。

另外,还有一些很 NB 的部门,不仅历史悠久,而且威望超高,是武林中的泰山北斗:少林(方证大师)、武当(冲虚道长)。。。

代码实现

透明组合模式

创建抽象构件

Component 需要定义访问及管理子构件的接口:

// component.h
#ifndef COMPONENT_H
#define COMPONENT_H

#include <iostream>
#include <string>

using namespace std;

class Component
{
public:
    Component(string name) : m_strName(name) {}
    virtual ~Component() {}
    virtual void Add(Component *cmpt) = 0;  // 添加构件
    virtual void Remove(Component *cmpt) = 0;  // 删除构件
    virtual Component* GetChild(int index) = 0;  // 获取构件
    virtual void Operation(int indent) = 0;  // 显示构件(以缩进的方式)

private:
    Component();  // 不允许

protected:
    string m_strName;
};

#endif // COMPONENT_H

创建叶子构件

作为 Component 的子类,Leaf 需要实现 Component 中定义的所有接口,但是 Leaf 不能包含子构件。因此,在 Leaf 中实现访问和管理子构件的函数时,需要进行异常处理或错误提示。 当然,这无疑会给 Leaf 的实现带来麻烦。

// leaf.h
#ifndef LEAF_H
#define LEAF_H

#include "component.h"

class Leaf : public Component
{
public:
    Leaf(string name) : Component(name){}
    virtual ~Leaf(){}
    void Add(Component *cmpt) {
        cout << "Can't add to a Leaf" << endl;
    }
    void Remove(Component *cmpt) {
        cout << "Can't remove from a Leaf" << endl;
    }
    Component* GetChild(int index) {
        cout << "Can't get child from a Leaf" << endl;
        return NULL;
    }
    void Operation(int indent) {
        string newStr(indent, '-');
        cout << newStr << " " << m_strName <<endl;
    }

private:
    Leaf();  // 不允许
};

#endif // LEAF_H

创建容器构件

由于容器构件中可以包含子节点,因此对容器构件进行处理时可以使用递归方式。

// composite.h
#ifndef COMPOSITE_H
#define COMPOSITE_H

#include <vector>
#include "component.h"

#ifndef SAFE_DELETE
#define SAFE_DELETE(p) { if(p){delete(p); (p)=NULL;} }
#endif

class Composite : public Component
{
public:
    Composite (string name) : Component(name) {}
    virtual ~Composite() {
        while (!m_elements.empty()) {
            vector<Component*>::iterator it = m_elements.begin();
            SAFE_DELETE(*it);
            m_elements.erase(it);
        }
    }
    void Add(Component *cmpt) {
        m_elements.push_back(cmpt);
    }
    void Remove(Component *cmpt) {
        vector<Component*>::iterator it = m_elements.begin();
        while (it != m_elements.end())  {
            if (*it == cmpt) {
                SAFE_DELETE(cmpt);
                m_elements.erase(it);
                break;
            }
            ++it;
        }
    }
    Component* GetChild(int index) {
        if (index >= m_elements.size())
            return NULL;

        return m_elements[index];
    }
    // 递归显示
    void Operation(int indent) {
        string newStr(indent, '-');
        cout << newStr << "+ " << m_strName << endl;
        // 显示每个节点的孩子
        vector<Component*>::iterator it = m_elements.begin();
        while (it != m_elements.end()) {
            (*it)->Operation(indent + 2);
            ++it;
        }
    }

private:
    Composite();  // 不允许

private:
    vector<Component *> m_elements;
};

#endif // COMPOSITE_H

创建客户端

最终,来看看任大教主的组织结构:

// main.cpp
#include "composite.h"
#include "leaf.h"

int main()
{
    // 创建一个树形结构
    // 创建根节点
    Component *pRoot = new Composite("江湖公司(任我行)");

    // 创建分支
    Component *pDepart1 = new Composite("日月神教(东方不败)");
    pDepart1->Add(new Leaf("光明左使(向问天)"));
    pDepart1->Add(new Leaf("光明右使(曲洋)"));
    pRoot->Add(pDepart1);

    Component *pDepart2 = new Composite("五岳剑派(左冷蝉)");
    pDepart2->Add(new Leaf("嵩山(左冷蝉)"));
    pDepart2->Add(new Leaf("衡山(莫大)"));
    pDepart2->Add(new Leaf("华山(岳不群)"));
    pDepart2->Add(new Leaf("泰山(天门道长)"));
    pDepart2->Add(new Leaf("恒山(定闲师太)"));
    pRoot->Add(pDepart2);

    // 添加和删除叶子
    pRoot->Add(new Leaf("少林(方证大师)"));
    pRoot->Add(new Leaf("武当(冲虚道长)"));
    Component *pLeaf = new Leaf("青城(余沧海)");
    pRoot->Add(pLeaf);

    // 小丑,直接裁掉
    pRoot->Remove(pLeaf);

    // 递归地显示组织架构
    pRoot->Operation(1);

    // 删除分配的内存
    SAFE_DELETE(pRoot);

    return 0;
}

输出如下:

-+ 江湖公司(任我行)
—+ 日月神教(东方不败)
—– 光明左使(向问天)
—– 光明右使(曲洋)
—+ 五岳剑派(左冷蝉)
—– 嵩山(左冷蝉)
—– 衡山(莫大)
—– 华山(岳不群)
—– 泰山(天门道长)
—– 恒山(定闲师太)
— 少林(方证大师)
— 武当(冲虚道长)

安全组合模式

创建抽象构件

// component.h
#ifndef COMPONENT_H
#define COMPONENT_H

#include <iostream>
#include <string>

using namespace std;

class Component
{
public:
    Component(string name) : m_strName(name) {}
    virtual ~Component() {}
    virtual void Operation(int indent) = 0;  // 显示构件(以缩进的方式)

private:
    Component();  // 不允许

protected:
    string m_strName;
};

#endif // COMPONENT_H

创建叶子构件

// leaf.h
#ifndef LEAF_H
#define LEAF_H

#include "component.h"

class Leaf : public Component
{
public:
    Leaf(string name) : Component(name){}
    virtual ~Leaf(){}
    void Operation(int indent) {
        string newStr(indent, '-');
        cout << newStr << " " << m_strName <<endl;
    }

private:
    Leaf();  // 不允许
};

#endif // LEAF_H

注意: 与透明模式不同,这里已经没有了访问及管理子构件的接口,所有的接口都在 Composite 中,不再赘述(同上)。

时间: 2024-09-20 00:07:22

C++组合模式的相关文章

.net设计模式实例之组合模式(Composite Pattern)

一.组合模式简介(Brief Introduction) 组合模式,将对象组合成树形结构以表示"部分-整体"的层次结构,组合模式使得用户 对单个对象和组合对象的使用具有一致性. 二.解决的问题(What To Solve) 解决整合与部分可以被一致对待问题. 三.组合模式分析(Analysis)1.组合模式结构 Component类:组合中的对象声明接口,在适当情况下,实现所有类共有接口的行为.声 明一个接口用于访问和管理Component的子部件 Leaf类:叶节点对象,叶节点没有子

php设计模式 Composite (组合模式)

复制代码 代码如下: <?php /** * 组合模式 * * 将对象组合成树形结构以表示"部分-整体"的层次结构,使得客户对单个对象和复合对象的使用具有一致性 */ abstract class MenuComponent { public function add($component){} public function remove($component){} public function getName(){} public function getUrl(){} p

.NET中的设计模式三:组合模式

设计 组合模式(Composite)是一种"结构型"模式(Structural).结构型模式涉及的对象为两个或两个以上,表示对象之间的活动,与对象的结构有关. 先举一个组合模式的小小例子: 如图:系统中有两种Box:Game Box和Internet Box,客户需要了解者两个类的接口分别进行调用.为了简化客户的工作,创建了XBox类,程序代码如下: GameBox的代码: public class GameBox { public void PlayGame() { Console.

设计模式学习:组合模式

介绍: 想必你已经了解了数据结构中的树,ok,组合模式对于你就是一会儿的功夫了.组合模式相对来说比较简单.看一下定义 组合模式:将对象组合成树形结构以表示"部分-整体"的层次结构.使得用户对单个对象和组合对象的使用具有一致性. 暂时没有想到好的例子,如果你有,请告诉我.下面我用树来对组合模式进行解释.树的结构是下面的这样的: 没棵树有一个根节点,也有叶子节点和树枝节点,一些结构都是用树结构表示的,例如树形菜单,文件和文件夹目录.那么如何存储管理这样的树结构,可以组合模式来解决. 组合模

设计模式的C++实现之组合模式

解决的问题: 我们PC用到的文件系统,其实就是我们数据结构里的树形结构,我们处理树中的每个节点时,其实 不用考虑他是叶子节点还是根节点,因为他们的成员函数都是一样的,这个就是组合模式的精髓.他模糊了简单元素和复杂 元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦. 将 对象组合成树形结构以表示"部分-整体"的层次结构.组合模式使得用户对单个对象和组合对象的使用具有一致性. 注明:树形结构里的叶子节点也有左右孩子,只不过他的孩子都是空.

hand first设计模式 -组合模式-1

组合模式:允许你将对象组成树形结构来表现"整体/部份"的层次结构.组合能让客户以一致的方式处理个别对象和对象组合. 下面程序的目的是打印所有菜单和子菜单的信息. 菜单和子菜单都继承自MenuComponent,所以在打印信息的时候以一致的方式处理(见组合模式定义). 菜单组件抽象类 Java代码 public abstract class MenuComponent { //添加菜单组件 public void add(MenuComponent menuComponent) { th

.NET设计模式(11):组合模式(Composite Pattern)

概述 组合模式有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦. 意图 将对象组合成树形结构以表示"部分-整体"的层次结构.Composite模式使得用户对单个对象和组合对象的使用具有一致性.[GOF <设计模式>] 结构图 图1 Composite模式结构图 生活中的例子 组合模式将对象组合成树形结构以表示"部分-整体"的层次结构

iOS设计模式之组合模式

组合模式(Composite) 基本理解 整体和部分可以一直对待. 组合模式:将对象组合成树形结构以表示"部分--整体"的层次结构.组合模式使得用户对单个对象和组合独享的使用具有一致性. 透明方式和安全方式 透明方式:在Component(为组合中的对象声明接口)中声明所有用来管理子对象的方法 .这样实现该接口的子类都具有了该接口中的方法.这样的好处就是叶节点和枝节点对于外界没有区别,他们具有完全一致的行为接口.但问题也很明显,因为Leaf类本身不具备添加删除方法的功能,所以实现它是没

深入理解JavaScript系列(40):设计模式之组合模式详解_基础知识

介绍 组合模式(Composite)将对象组合成树形结构以表示"部分-整体"的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性. 常见的场景有asp.net里的控件机制(即control里可以包含子control,可以递归操作.添加.删除子control),类似的还有DOM的机制,一个DOM节点可以包含子节点,不管是父节点还是子节点都有添加.删除.遍历子节点的通用功能.所以说组合模式的关键是要有一个抽象类,它既可以表示子元素,又可以表示父元素. 正文 举个例子,有家餐厅提

Android设计模式系列之组合模式_Android

Android中对组合模式的应用,可谓是泛滥成粥,随处可见,那就是View和ViewGroup类的使用.在android UI设计,几乎所有的widget和布局类都依靠这两个类. 组合模式,Composite Pattern,是一个非常巧妙的模式.几乎所有的面向对象系统都应用到了组合模式. 1.意图 将对象View和ViewGroup组合成树形结构以表示"部分-整体"的层次结构(View可以做为ViewGroup的一部分). 组合模式使得用户对单个对象View和组合对象ViewGrou