为方便读者,本文已添加至索引:

写在前面

Composite(组合)模式中,用户可以使用多个简单的组件以形成较大的组件,而这些组件还可能进一步组合成更大的。它重要的特性是能够让用户一致地对待单个对象和组合对象。不知大家是否还记得女巫格琳达(见笔记Facade模式),她的小屋经营得很顺利,给小伙伴们的生活带来了极大地便利。今天,她又推出了一项全新的销售项目,那就是“私人订制自主行动型魔法小人偶-I”。乍看之下是个稻草人模样,但其实客人们能够通过自己订制小人偶的不同部件,组装成一个可以按照预先设定的期望目标而自主行动的魔法人偶。他们可以帮忙播种,耕地,烹饪,甚至战斗等等等等。

女巫格琳达在设计这个小人偶原型时,采用了Composite模式从而简化了客户们的操作,同时也能利用清晰的树形结构来展示订制的全部内容。在我们进入到示例部分讲解小人偶相关代码之前,先来具体了解下Composite模式的基本知识:

要点梳理

  • 目的分类

    • 对象结构型模式
  • 范围准则
    • 对象(该模式处理对象间的关系,这些关系在运行时刻是可以变化的,更具动态性)
  • 主要功能
    • 将对象组合成树形结构以表示“部分-整体”的层次结构。这可以使得用户在使用单个对象和组合对象时有一致性。
  • 适用情况
    • 当我们想表示对象的部分-整体层次结构时;
    • 当我们希望用户忽略组合对象与单个对象的不同,让他们能够统一地去使用时
  • 参与部分
    • Component:为组合中的对象声明接口;或者在适当的情况下,实现所有类共有接口的缺省行为;声明一个接口用于访问和管理Component的子组件;在递归结构中定义一个接口,用于访问一个父部件,并在合适的情况下实现它,当然这个是可选的。
    • Leaf:在组合中表示叶节点对象,需要说明的是,叶节点没有子节点。
    • Composite:定义有子部件的那些部件的行为,同时存储子部件,实现Component中与子部件有关的接口。
    • Client:通过Component接口,操纵组合部件的对象。
  • 协作过程
    • 用户使用Component类接口与组合结构中的对象进行交互。如果接收者是一个叶节点,则直接处理请求。如果接收者是Composite, 它通常将请求发送给它的子部件,在转发请求之前与/或之后可能执行一些辅助操作。
  • UML图

示例分析 - 私人订制魔法小人偶-I

从上文的UML图中,可以看到Composite模式描述了如何使用递归组合,使得用户不必对单个类与组合类进行区别。而实现这一点的关键,在于Component抽象类,它既能代表单个组件,又能代表容器。

对于我们的魔法小人偶-I型而言,我们同样定义一个Component类为接下来所有的部件定义一个接口。

 class Component {
public:
virtual ~Component(); const char* name() { return _name; } virtual int myFunction(); // power on the function of the component.
virtual int myValue(); // count the value of the component. virtual void add(Component*);
virtual void remove(Component*);
virtual Iterator<Component*>* createIterator(); protected:
Component(const char*); private:
const char* _name;
};

我们看到Component声明了一些操作来返回一个部件的属性,譬如说它的价格等。子类将为指定的部件实现这些接口。而对于createIterator操作,它能够返回一个访问它零件的Iterator,从而能够在管理零件的时候,进行遍历。

Component类的子类有很多,我们将不会全部讲述(那会显得很冗余)。我们首先看到的是Leaf型的组件,它们不是容器,比如装在原型里面的驱动装置,穿在外面的衣服,戴在头上的帽子,可以使用的武器、工具等等。比如我们Clothes类:

 class Clothes : public Component {
public:
Clothes(const char*);
virtual ~Clothes(); virtual int myFunction();
virtual int myValue();
};

另一些是Composite型的组件,它们作为容器能够包含其他部件。这种组件的基类CompositeComponent

 class CompositeComponent : public Component {
virtual ~CompositeComponent(); virtual int myFunction();
virtual int myValue(); virtual void add(Component*);
virtual void remove(Component*);
virtual Iterator<Component*>* createIterator(); protected:
CompositeComponent(const char*); private:
List<Component*> _component;
};

它为访问和管理子部件定义了一些操作。操作add/remove将从存储在_component成员变量中的部件列表中插入/删除部件。而作为容器的myValue操作,通过使用createIterator,来累加子部件的价格。

 int CompositeComponent::myValue() {
Iterator<Component*>* iter = createIterator();
int total = ;
for (iter->first(); !iter->isDone(); iter->next()) {
total += iter->current()->myValue();
}
delete iter;
return total;
}

CompositeComponent类的子类之一ProtoBody,是最基本的人偶外壳容器,格琳达可以通过在里面安置一些驱动器Drive,传感器Sensor等来使得它得以正常运作。

 class ProtoBody : public CompositeComponent {
public:
ProtoBody(const char*);
virtual ~ProtoBody(); virtual int myFunction();
virtual int myValue();
};

好啦,我们还可以定义一些相似的其他容器,譬如战斗人偶的武器袋WeaponBag等等。我们可以简单地来模拟订制一个能够保卫领土的战斗机器人:

 ProtoBody* body = new ProtoBody("A strong and tough body");

 WeaponBag* bag = new WeaponBag("Weapon bag");
bag->add(new Weapon("Sharpen Sword"));
bag->add(new Shield("Hard Shield")); body->add(bag);
body->add(new Drive("Fight For Honor!"));
body->add(new Clothes("Cool Armor")); cout << "The total price is " << body->myValue() << endl;

对于我们的设计可以看下面这张图:

特点总结

从上面我们可以看到,Composite模式有如下一些特点:

  1. Composite模式定义了包含基本对象和组合对象的类层次结构。基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断的递归下去。客户代码中,任何用到基本对象的地方都可以使用组合对象。
  2. 简化客户代码。客户可以一致地使用组合结构和单个对象。通常用户不知道(也不关心)处理的是一个叶节点还是一个组合组件。这就简化了客户代码, 因为在定义组合的那些类中不需要写一些充斥着选择语句的函数。
  3. 使得更容易增加新类型的组件
  4. 使我们的设计变得更加一般化

同时,我们在实现的时候需要注意以下几点:

  1. 显式的父部件引用。这可以简化组合结构的遍历和管理。
  2. 共享组件。一般来说这都很有用,比如它可以减少对存贮的需求。
  3. 最大化Component接口。因为它的目的是使得用户不知道他们正在使用的到底是Leaf还是Composite,因此,Component应尽量多定义一些公共操作。
  4. 注意子部件的排序问题。
  5. 使用高速缓冲存贮改善性能,特别是当我们需要对组合进行频繁的遍历或查找。

写在最后

今天的笔记就到这里了,欢迎大家批评指正!如果觉得可以的话,好文推荐一下,我会非常感谢的!

[学习笔记]设计模式之Composite的更多相关文章

  1. [学习笔记]设计模式之Command

    为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 在上篇Chain of Responsibility(职责链)模式笔记中,我们学习了一种行为型设计模式.今天,我们继续这一主题,来学习 ...

  2. [学习笔记]设计模式之Abstract Factory

    写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 在上篇笔记Builder设计模式中,时の魔导士祭出了自己的WorldCreator.尽管它因此能创造出一个有山有树有房子的世界,但是白 ...

  3. [学习笔记]设计模式之Builder

    写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 作为一个新入职的魔导士呢,哦不,是程序员,我以为并没有太多机会去设计项目的软件架构.但是,工作一段时间之后,自己渐渐意识到,哪怕是自己 ...

  4. [学习笔记]设计模式之Adapter

    写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 Adapter(适配器)模式主要解决接口不匹配的问题.为此,让我们要回到最初Builder模式创建平行世界时,白雪公主和小霍比特人的谜 ...

  5. [学习笔记]设计模式之Bridge

    写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 “魔镜啊魔镜,谁是这个世界上最美丽的人?”月光中,一个低沉的声音回荡在女王的卧室.“是美丽的白雪公主,她正和小霍比特人们幸福快乐地生活 ...

  6. [学习笔记]设计模式之Prototype

    写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 在笔记Builder模式中,我们曾见到了最初用于创建平行世界的函数createWorld,并且它是Mage类的成员函数(毕竟是专属于魔 ...

  7. [学习笔记]设计模式之Chain of Responsibility

    为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 最近时间比较紧,所以发文的速度相对较慢了.但是看到园子里有很多朋友对设计模式感兴趣,我感觉很高兴,能够和大家一起学习这些知识. 之前的 ...

  8. [学习笔记]设计模式之Proxy

    为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 “魔镜啊魔镜,谁是这个世界上最美丽的人?” 每到晚上,女王都会问魔镜相同的问题(见Decorator模式).这是她还曾身为女巫时留下的 ...

  9. [学习笔记]设计模式之Flyweight

    为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 Flyweight(享元)模式运用共享技术,可以有效地支持大量细粒度的对象.今天我们会去参观小霍比特人们的酿酒工坊……等等,不是享元模 ...

随机推荐

  1. vijosP1006 晴天小猪历险记之Hill

    vijosP1006 晴天小猪历险记之Hill 链接:https://vijos.org/p/1006 [思路] 图上DP. 这个题的递推顺序是关键.先从上一行得到最小值,然后从本行比较最小值,注意本 ...

  2. GCC内联汇编入门

    原文为GCC-Inline-Assembly-HOWTO,在google上可以找到原文,欢迎指出翻译错误. 中文版说明 由于译者水平有限,故译文出错之处,还请见谅.C语言的关键字不译,一些单词或词组( ...

  3. GCC基本知识

    掌握下面的对GCC会有一个比较清晰的大致的了解: 不经意间,GCC已发展到了4.3的版本,尽管在软件开发社区之外乏人闻问,但因为GCC在几乎所有开源软件和自由软件中都会用到,因此它的编译性能的涨落会直 ...

  4. python Day 1 - 搭建开发环境

    搭建开发环境 首先,确认系统安装的Python版本是2.7.x: $ python --version Python 2.7.5 然后,安装开发Web App需要的第三方库: 前端模板引擎jinja2 ...

  5. jmeter控制器

    1.仅一次控制器  这个控制器可以保证线程在多次循环跑得情况下只登陆一次 2.循环控制器(Loop Controller:设置循环次数 结果: 3. ForEach控制器(ForEach Contro ...

  6. 学习:Linux基础知识<一>

    >>硬盘分区模式 硬盘分区模式一般如下: -- /  (根目录) -- /usr    (操作系统) --/home (用户信息) -- /var    (默认服务器的登录文件,邮件与WW ...

  7. PHP如何取出数组最后一个元素?

    <?php $array=array("first","sencond","third"); #1.echo end($array); ...

  8. C++ 让控制台运行到最后按下回车键才结束的3种方法

    初学C++,弄懂了3种在控制台最后需要按回车键才退出返回编辑框的方法 1.最简单的编译生成后,通过Ctrl+F5运行即可(推荐自己调试的时候用,因为程序在别人那里是一闪而过的) 2.先添加头文件“#i ...

  9. LeetCode153:Find Minimum in Rotated Sorted Array

    Suppose a sorted array is rotated at some pivot unknown to you beforehand. (i.e., 0 1 2 4 5 6 7 migh ...

  10. PureMVC(JS版)源码解析(五):SimpleCommand类

          之前我们对PureMVC中涉及到观察者模式的三个基本类(Notification/Observer/Notifier)进行了分析,接下来将对PureMVC源码中的其他类进行分析,首先我们讲 ...