首页 > 其他 > 详细

[设计模式] 设计模式课程(十七)--组合模式

时间:2020-06-07 21:25:07      阅读:44      评论:0      收藏:0      [点我收藏+]

概述

  • 数据结构模式
    • 常常有一些组件在内部具有特定的数据结构,如果让客户程序依赖这些特定的数据结构,将极大地破坏组件的复用
    • 将这些特定数据模式封装在内部,对外提供统一的接口,来实现与特定数据结构无关的访问
    • 典型模式:Composite, Iterator, Chain of Resposibility
  • 属于结构模式(按目的划分)  
  • 动机:软件在某些情况下,客户代码过多地依赖于对象容器复杂的内部实现结构,对象容器内部实现结构(而非抽象接口)的变化将引起客户代码的频繁变化,带来了代码的维护性、扩展性等弊端
  • 如何将“客户代码与复杂的对象容器结构”解耦?让对象容器自己来实现自身的复杂结构,从而使得客户代码就像处理简单对象一样来处理复杂的对象容器?
  • 定义:将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性(稳定)
  • 采用树形结构来实现普遍存在的对象容器,从而将“一对多”的关系转化成“一对一”的关系,使得客户代码可以一致地(复用)处理对象和对象容器,无需关心处理的是单个的对象,还是组合的对象容器
  • 将“客户代码与复杂的对象容器结构”结构是Composite的核心思想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的内部实现结构——发生依赖,从而更能“应对变化”
  • Composite模式在具体实现中,可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率
  • 如何计算一个复杂订单的总价
  • 采用通用接口与产品和盒子进行交互,在接口中声明一个计算总价的方法
  • 对于产品,直接返回价格,对于盒子,遍历其中所有项目,询问每个项目的价格,然后返回盒子总价格
  • 简单叶子节点和复杂容器实现共享接口,容器中可包含叶子节点和其他容器,从而可构建树状嵌套递归对象
  • 所有元素共用一个接口,客户端不必在意其使用的对象的具体类

技术分享图片  技术分享图片

示例1

  • 34-36:多态调用,将内部数据结构封装
技术分享图片
 1 #include <iostream>
 2 #include <list>
 3 #include <string>
 4 #include <algorithm>
 5 
 6 using namespace std;
 7 
 8 class Component
 9 {
10 public:
11     virtual void process() = 0;
12     virtual ~Component(){}
13 };
14 
15 //树节点
16 class Composite : public Component{
17     
18     string name;
19     list<Component*> elements;
20 public:
21     Composite(const string & s) : name(s) {}
22     
23     void add(Component* element) {
24         elements.push_back(element);
25     }
26     void remove(Component* element){
27         elements.remove(element);
28     }
29     
30     void process(){
31         
32         //1. process current node
33         
34         //2. process leaf nodes
35         for (auto &e : elements)
36             e->process(); //多态调用
37          
38     }
39 };
40 
41 //叶子节点
42 class Leaf : public Component{
43     string name;
44 public:
45     Leaf(string s) : name(s) {}
46             
47     void process(){
48         //process current node
49     }
50 };
51 
52 void Invoke(Component & c){
53     //...
54     c.process();
55     //...
56 }
57 
58 int main()
59 {
60 
61     Composite root("root");
62     Composite treeNode1("treeNode1");
63     Composite treeNode2("treeNode2");
64     Composite treeNode3("treeNode3");
65     Composite treeNode4("treeNode4");
66     Leaf leat1("left1");
67     Leaf leat2("left2");
68     
69     root.add(&treeNode1);
70     treeNode1.add(&treeNode2);
71     treeNode2.add(&leaf1);
72     
73     root.add(&treeNode3);
74     treeNode3.add(&treeNode4);
75     treeNode4.add(&leaf2);
76     
77     process(root);
78     process(leaf2);
79     process(treeNode3);
80   
81 }
View Code

 技术分享图片

示例2

技术分享图片
  1 #include <algorithm>
  2 #include <iostream>
  3 #include <list>
  4 #include <string>
  5 #include <cstdlib>
  6 using namespace std;
  7 /**
  8  * The base Component class declares common operations for both simple and
  9  * complex objects of a composition.
 10  */
 11 class Component {
 12   /**
 13    * @var Component
 14    */
 15  protected:
 16   Component *parent_;
 17   /**
 18    * Optionally, the base Component can declare an interface for setting and
 19    * accessing a parent of the component in a tree structure. It can also
 20    * provide some default implementation for these methods.
 21    */
 22  public:
 23   virtual ~Component() {}
 24   void SetParent(Component *parent) {
 25     this->parent_ = parent;
 26   }
 27   Component *GetParent() const {
 28     return this->parent_;
 29   }
 30   /**
 31    * In some cases, it would be beneficial to define the child-management
 32    * operations right in the base Component class. This way, you won‘t need to
 33    * expose any concrete component classes to the client code, even during the
 34    * object tree assembly. The downside is that these methods will be empty for
 35    * the leaf-level components.
 36    */
 37   virtual void Add(Component *component) {}
 38   virtual void Remove(Component *component) {}
 39   /**
 40    * You can provide a method that lets the client code figure out whether a
 41    * component can bear children.
 42    */
 43   virtual bool IsComposite() const {
 44     return false;
 45   }
 46   /**
 47    * The base Component may implement some default behavior or leave it to
 48    * concrete classes (by declaring the method containing the behavior as
 49    * "abstract").
 50    */
 51   virtual std::string Operation() const = 0;
 52 };
 53 /**
 54  * The Leaf class represents the end objects of a composition. A leaf can‘t have
 55  * any children.
 56  *
 57  * Usually, it‘s the Leaf objects that do the actual work, whereas Composite
 58  * objects only delegate to their sub-components.
 59  */
 60 class Leaf : public Component {
 61  public:
 62   std::string Operation() const override {
 63     return "Leaf";
 64   }
 65 };
 66 /**
 67  * The Composite class represents the complex components that may have children.
 68  * Usually, the Composite objects delegate the actual work to their children and
 69  * then "sum-up" the result.
 70  */
 71 class Composite : public Component {
 72   /**
 73    * @var \SplObjectStorage
 74    */
 75  protected:
 76   std::list<Component *> children_;
 77 
 78  public:
 79   /**
 80    * A composite object can add or remove other components (both simple or
 81    * complex) to or from its child list.
 82    */
 83   void Add(Component *component) override {
 84     this->children_.push_back(component);
 85     component->SetParent(this);
 86   }
 87   /**
 88    * Have in mind that this method removes the pointer to the list but doesn‘t
 89    * frees the
 90    *     memory, you should do it manually or better use smart pointers.
 91    */
 92   void Remove(Component *component) override {
 93     children_.remove(component);
 94     component->SetParent(nullptr);
 95   }
 96   bool IsComposite() const override {
 97     return true;
 98   }
 99   /**
100    * The Composite executes its primary logic in a particular way. It traverses
101    * recursively through all its children, collecting and summing their results.
102    * Since the composite‘s children pass these calls to their children and so
103    * forth, the whole object tree is traversed as a result.
104    */
105   std::string Operation() const override {
106     std::string result;
107     for (const Component *c : children_) {
108       if (c == children_.back()) {
109         result += c->Operation();
110       } else {
111         result += c->Operation() + "+";
112       }
113     }
114     return "Branch(" + result + ")";
115   }
116 };
117 /**
118  * The client code works with all of the components via the base interface.
119  */
120 void ClientCode(Component *component) {
121   // ...
122   std::cout << "RESULT: " << component->Operation();
123   // ...
124 }
125 
126 /**
127  * Thanks to the fact that the child-management operations are declared in the
128  * base Component class, the client code can work with any component, simple or
129  * complex, without depending on their concrete classes.
130  */
131 void ClientCode2(Component *component1, Component *component2) {
132   // ...
133   if (component1->IsComposite()) {
134     component1->Add(component2);
135   }
136   std::cout << "RESULT: " << component1->Operation();
137   // ...
138 }
139 
140 /**
141  * This way the client code can support the simple leaf components...
142  */
143 
144 int main() {
145   Component *simple = new Leaf;
146   std::cout << "Client: I‘ve got a simple component:\n";
147   ClientCode(simple);
148   std::cout << "\n\n";
149   /**
150    * ...as well as the complex composites.
151    */
152 
153   Component *tree = new Composite;
154   Component *branch1 = new Composite;
155 
156   Component *leaf_1 = new Leaf;
157   Component *leaf_2 = new Leaf;
158   Component *leaf_3 = new Leaf;
159   branch1->Add(leaf_1);
160   branch1->Add(leaf_2);
161   Component *branch2 = new Composite;
162   branch2->Add(leaf_3);
163   tree->Add(branch1);
164   tree->Add(branch2);
165   std::cout << "Client: Now I‘ve got a composite tree:\n";
166   ClientCode(tree);
167   std::cout << "\n\n";
168 
169   std::cout << "Client: I don‘t need to check the components classes even when managing the tree:\n";
170   ClientCode2(tree, simple);
171   std::cout << "\n";
172 
173   delete simple;
174   delete tree;
175   delete branch1;
176   delete branch2;
177   delete leaf_1;
178   delete leaf_2;
179   delete leaf_3;
180 
181   return 0;
182 }
View Code

技术分享图片

联系

  • 桥接、状态、策略、适配器模式的接口非常类似,都基于组合模式
  • 创建复杂组合树时使用生成器模式,使其构造步骤以递归的方式进行
  • 通常与责任链模式结合使用,叶组件接收到请求后,可一直传递到对象树的底部
  • 可用迭代器模式遍历组合树
  • 可用访问者模式对组合树进行操作
  • 可用享元模式实现组合树的共享叶节点,以节省内存
  • 和装饰模式的结构图很像,都依赖递归组合来组织无限数目的对象,不同在于,装饰模式只有一个子组件,且为封装对象添加了额外的职责,而组合仅对子节点的结果进行了求和
  • 可使用原型模式复制复杂结构,而非从零开始构造

 

[设计模式] 设计模式课程(十七)--组合模式

原文:https://www.cnblogs.com/cxc1357/p/12320329.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!