设计模式——组合模式

来源:互联网 发布:java在cmd中输入中文 编辑:程序博客网 时间:2024/06/05 05:59

1. 概述

  组合模式(Composite),将对象组合成树形结构以表示“部分——整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。有时候我们又称其为部分——整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素。从而使得客户与复杂元素的内部结构解耦。
  在一些递归或分级数据结构中,组合模式非常有用武之地。例如,文件系统由目录和文件组成。每个目录都可以装内容。目录的内容可以是文件,也可以是目录。按照这种方式,计算机的文件系统就是以递归结构来组织的。如果你想要描述这样的数据结构,那么你就可以使用组合模式。

2. 结构图

  下面是组合模式的结构图:

  • Component:为组合中的对象声明接口;在适当的情况下,实现所有类共有接口的缺省行为;声明一个接口用于访问和管理 Component 的子组件。
  • Leaf:在组合中表示叶节点对象,叶节点没有子节点。
  • Composite:定义有子部件的那些部件的行为;存储子部件;在Component 接口中实现与子部件有关的操作。
  • Client:通过Component 接口操纵组合部件的对象。
  用户使用Component 类接口与组合结构中的对象进行交互。如果接收者是一个叶节点,则直接处理请求。如果接收者是Composite,它通常将请求发送给它的子部件,在转发请求之前与/或之后可能执行一些辅助操作。
  组合模式中又可分为透明方式安全方式。两种各有好处,使用什么方式视情况而定。
  透明方式,也就是说在Component 中声明所有用来管理子对象的方法,其中包括Add、Remove等。这样实现Component 接口的所有子类都具备了Add和Remove。这样做的好处就是叶节点和枝节点对外界没有区别。它们具备完全一致的行为接口。但是问题也很明显。因为Leaf 类本身不具备Add()、Remove()方法功能,所以实现它是没有意义的。
  安全方式,也就是在Component 接口中不去声明Add 和 Remove方法,那么子类的Leaf 也就不需要去实现它,而是在Composite 声明所有用来管理子类的方法,这样做就不会出现透明方式存在的问题,不过由于不够透明,所以树叶和树枝类将不具有相同的接口,客户端的调用需要做相应的判断,带来不便。

3. 案例分析

  在一些绘图编辑器和图形捕捉系统这样的图形应用程序中,用户可以使用简单的组件创建复杂的图表。用户可以组合多个简单组件以形成一些较大的组件,这些组件又可以组合成更大的组件。一个简单的实现方式时为Text和Line这样的图元定义一些类,另外定义一些类作为这些图元的容器类。然而这种方法存在一个问题:使得这些类的代码必须区别对待图元与容器对象,而实际上大多数情况下用户认为它们时一样的。对这些类区别使用,使得程序更加复杂。Composite模式描述了如何使用递归组合,使得用户不必对这些类进行区别。
/**********************************************************************************Copyright(C),Your Company*FileName:  Graphic.h*Author:  Huangjh*Version:*Date:  2017-11-12*Description:  相当于结构图中的Component角色——声明组合中的对象接口*Others:**********************************************************************************/#ifndef _GRAPHIC_H#define _GRAPHIC_Hclass CGraphic{public:CGraphic() { }virtual ~CGraphic() { }virtual void Draw(void) = 0;virtual void Add(CGraphic *pGraphic) = 0;virtual void Remove(CGraphic *pGraphic) = 0;};#endif#ifndef _GRAPHIC_H
/**********************************************************************************Copyright(C),Your Company*FileName:  Line.h*Author:  Huangjh*Version:*Date:  2017-11-12*Description:  相当于结构图中的Leaf角色——基本图元线*Others:**********************************************************************************/#ifndef _LINE_H#define _LINE_H#include <iostream>#include <string>#include "Graphic.h"class CLine:public CGraphic{public:CLine(std::string strName):m_strName(strName){}virtual ~CLine() { }void Draw(void){std::cout << "基本图元——绘制" << m_strName << std::endl;}void Add(CGraphic *pGraphic){std::cout << "基本图元线——Add方法不使用" << std::endl;}void Remove(CGraphic *pGraphic){std::cout << "基本图元线——Remove方法不使用" << std::endl;}private:std::string m_strName;};#endif//#ifndef _LINE_H
/**********************************************************************************Copyright(C),Your Company*FileName:  Text.h*Author:  Huangjh*Version:*Date:  2017-11-12*Description:  相当于结构图中的Leaf角色——基本图元文本*Others:**********************************************************************************/#ifndef _TEXT_H#define _TEXT_H#include <iostream>#include <string>#include "Graphic.h"class CText : public CGraphic{public:CText(std::string strName):m_strName(strName){}virtual ~CText() { }void Draw(void){std::cout << "基本图元——绘制" << m_strName << std::endl;}void Add(CGraphic *pGraphic){std::cout << "基本图元文本——Add方法不使用" << std::endl;}void Remove(CGraphic *pGraphic){std::cout << "基本图元文本——Remove方法不使用" << std::endl;}private:std::string m_strName;};#endif//#ifndef _TEXT_H
/**********************************************************************************Copyright(C),Your Company*FileName:  Rectangle.h*Author:  Huangjh*Version:*Date:  2017-11-12*Description:  相当于结构图中的Composite角色——组合对象矩形(简单的认为矩形有4条线组成)*Others:**********************************************************************************/#ifndef _RECTANGLE_H#define _RECTANGLE_H#include <iostream>#include <string>#include <set>#include "Graphic.h"class CRectangle:public CGraphic{public:CRectangle(std::string strName):m_strName(strName){}~CRectangle() { }void Draw(void){std::cout << "组合对象矩形:" << m_strName << "由下面图元组成:" << std::endl;for (setGraphic::iterator iter = m_setGraphic.begin();iter != m_setGraphic.end(); ++iter){(*iter)->Draw();}}void Add(CGraphic *pGraphic){m_setGraphic.insert(pGraphic);}void Remove(CGraphic *pGraphic){setGraphic::iterator iter = m_setGraphic.find(pGraphic);if (iter != m_setGraphic.end()){m_setGraphic.erase(iter);}}private:typedef std::set<CGraphic *> setGraphic;setGraphic m_setGraphic;std::string m_strName;};#endif//#ifndef _RECTANGLE_H
/**********************************************************************************Copyright(C),Your Company*FileName:  Label.h*Author:  Huangjh*Version:*Date:  2017-11-12*Description:  相当于结构图中的Composite角色——组合对象标签( 简单的任务标签由矩形和文本组成)*Others:**********************************************************************************/#ifndef _LABEL_H#define _LABEL_H#include <iostream>#include <string>#include <set>#include "Graphic.h"class CLabel:public CGraphic{public:CLabel(std::string strName):m_strName(strName){}~CLabel() { }void Draw(void){std::cout << "组合对象标签:" << m_strName <<"由下面图元组成:" << std::endl;for (setCraphic::iterator iter = m_setLabelGraphic.begin();iter != m_setLabelGraphic.end(); ++iter){(*iter)->Draw();}}void Add(CGraphic *pGraphic){m_setLabelGraphic.insert(pGraphic);}void Remove(CGraphic *pGraphic){setCraphic::iterator iter = m_setLabelGraphic.find(pGraphic);if (iter != m_setLabelGraphic.end()){m_setLabelGraphic.erase(iter);}}private:typedef std::set<CGraphic *> setCraphic;setCraphic m_setLabelGraphic;std::string m_strName;};#endif#ifndef _LABEL_H
  下面是运行的结果:
组合对象标签:一个标签由下面图元组成:组合对象矩形:一个矩形由下面图元组成:基本图元——绘制矩形顶部的直线基本图元——绘制矩形底部的直线基本图元——绘制矩形左边的直线基本图元——绘制矩形右边的直线基本图元——绘制标签中的文本

4. 优缺点

4.1 优点

  • 定义了包含基本对象和组合对象的类层次结构 基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断递归下去。客户代码中,任何用到基本对象的地方都可以使用组合对象。
  • 简化客户代码 客户可以一致地使用组合结构和单个对象。通常用户不知道(也不关心)处理的是一个叶节点还是一个组合组件。这就简化了客户代码,因为在定义组合的那些类中不需要写一些充斥着选择语句的函数。
  • 使得更容易增加新类型的组件 新定义的Composite 或Leaf 子类自动地与已有的结构和客户代码一起工作,客户程序不需因新的Component 类而改变。
  • 使你的设计变得更加一般化

4.2 缺点

  • 容易增加新组件也会产生一些问题,那就是很难限制组合中的组件。有时你希望一个组合只能有某些特定的组件。使用Composite 时,你不能依赖类型系统施加这些约束,而必须在运行时刻进行检查。

5. 适用性

  以下情况使用Composite 模式:
  • 你想表示对象的部分——整体层次结构。
  • 你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

6. 参考资料

  • 《大话设计模式》
  • 《设计模式——可复用的面向对象软件的基础》

原创粉丝点击