zoukankan      html  css  js  c++  java
  • [学习笔记]设计模式之Composite

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

    写在前面

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

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

    要点梳理

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

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

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

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

     1 class Component {
     2 public:
     3     virtual ~Component();
     4     
     5     const char* name() { return _name; }
     6     
     7     virtual int myFunction();    // power on the function of the component.
     8     virtual int myValue();    // count the value of the component.
     9     
    10     virtual void add(Component*);
    11     virtual void remove(Component*);
    12     virtual Iterator<Component*>* createIterator();
    13     
    14 protected:
    15     Component(const char*);
    16     
    17 private:
    18     const char* _name;
    19 };

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

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

    1 class Clothes : public Component {
    2 public:
    3     Clothes(const char*);
    4     virtual ~Clothes();
    5     
    6     virtual int myFunction();
    7     virtual int myValue();
    8 };

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

     1 class CompositeComponent : public Component {
     2     virtual ~CompositeComponent();
     3     
     4     virtual int myFunction();
     5     virtual int myValue();
     6     
     7     virtual void add(Component*);
     8     virtual void remove(Component*);
     9     virtual Iterator<Component*>* createIterator();
    10     
    11 protected:
    12     CompositeComponent(const char*);
    13     
    14 private:
    15     List<Component*> _component;
    16 };

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

    1 int CompositeComponent::myValue() {
    2     Iterator<Component*>* iter = createIterator();
    3     int total = 0;
    4     for (iter->first(); !iter->isDone(); iter->next()) {
    5         total += iter->current()->myValue();
    6     }
    7     delete iter;
    8     return total;
    9 }

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

    1 class ProtoBody : public CompositeComponent {
    2 public:
    3     ProtoBody(const char*);
    4     virtual ~ProtoBody();
    5     
    6     virtual int myFunction();
    7     virtual int myValue();
    8 };

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

     1 ProtoBody* body = new ProtoBody("A strong and tough body");
     2 
     3 WeaponBag* bag = new WeaponBag("Weapon bag");
     4 bag->add(new Weapon("Sharpen Sword"));
     5 bag->add(new Shield("Hard Shield"));
     6 
     7 body->add(bag);
     8 body->add(new Drive("Fight For Honor!"));
     9 body->add(new Clothes("Cool Armor"));
    10 
    11 cout << "The total price is " << body->myValue() << endl;

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

    特点总结

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

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

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

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

    写在最后

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

  • 相关阅读:
    Python 存储引擎 数据类型 主键
    Python 数据库
    Python 线程池进程池 异步回调 协程 IO模型
    Python GIL锁 死锁 递归锁 event事件 信号量
    Python 进程间通信 线程
    Python 计算机发展史 多道技术 进程 守护进程 孤儿和僵尸进程 互斥锁
    Python 异常及处理 文件上传事例 UDP socketserver模块
    Python socket 粘包问题 报头
    Django基础,Day7
    Django基础,Day6
  • 原文地址:https://www.cnblogs.com/xieziyu/p/3614283.html
Copyright © 2011-2022 走看看