设计模式8:“数据结构”模式——Composite,Iterator,Chain of Responsibility

“数据结构”模式

      常常有一些组建在内部具有特定的数据结构,如果让客户程序依赖这些特定的数据结构,将极大的破坏组件的复用。这时候,将这些数据结构封装在内部,在外部提供统一的接口,来实现与特定数据结构无关的访问,是一种行之有效的解决方案。

典型模式

  • Composite
  • Iterator
  • Chain of Responsibility

Composite 组合模式

动机(Motivation)

  • 软件在某些情况下,客户代码过多地依赖于对象容器复杂的内部实现结构,对象容器内部实现结构(而非抽象接口)的变化将因其客户代码的频繁变化,带来了代码的维护性、扩展性等弊端。
  • 如何将“客户代码与复杂的对象容器结构”解耦?让对象容器自己来实现自身的复杂结构,从而使得客户代码就像处理简单对象一样来处理复杂的对象容器?

模式定义

    将对象组合成树形结构以表示“部分-整体”的层级结构。Compisite使得用户对单个对象和组合对象的使用具有一致性 (稳定)。

                                                                                                             ——《设计模式》GoF

示例伪代码: 

 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         
35         //2. process leaf nodes
36         for (auto &e : elements)
37             e->process(); //多态调用
38          
39     }
40 };
41 
42 //叶子节点
43 class Leaf : public Component{
44     string name;
45 public:
46     Leaf(string s) : name(s) {}
47             
48     void process(){
49         //process current node
50     }
51 };
52 
53 
54 void Invoke(Component & c){
55     //...
56     c.process();
57     //...
58 }
59 
60 
61 int main()
62 {
63 
64     Composite root("root");
65     Composite treeNode1("treeNode1");
66     Composite treeNode2("treeNode2");
67     Composite treeNode3("treeNode3");
68     Composite treeNode4("treeNode4");
69     Leaf leat1("left1");
70     Leaf leat2("left2");
71     
72     root.add(&treeNode1);
73     treeNode1.add(&treeNode2);
74     treeNode2.add(&leaf1);
75     
76     root.add(&treeNode3);
77     treeNode3.add(&treeNode4);
78     treeNode4.add(&leaf2);
79     
80     process(root);
81     process(leaf2);
82     process(treeNode3);
83   
84 }
View Code

模式应用:类比于文件系统

image

 结构

image

要点总结

  • Composite模式采用树形结构来实现普遍存在的对象容器,从而将“一对多”的关系转化为“一对一”的关系,使得客户代码可以一致地(复用)处理对象和对象容器,无需关心处理的是单个对象还是组合的对象容器。
  • 将“客户代码与复杂的对象容器结构”解耦是Composite的核心思想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的内部实现结构——发生依赖,从而更能“应对变化”。
  • Composite模式在具体实现中,可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率

Iterator 迭代器

动机(Motivation)

  • 在软件构建过程中,集合对象内部结构常常变化各异。但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码透明的访问其中包含的元素;同时这种“透明遍历”也为“同一种算法在多种集合对象上进行操作”提供了可能。
  • 使用面向对象技术将这种遍历机制抽象为“迭代器对象”为“因对变化中的集合对象”提供了一种优雅的方式。

模式定义

     提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露(隔离变化,稳定)该对象的内部表示。

                                                                                                             ——《设计模式》GoF

示例伪代码 

 1 template<typename T>
 2 class Iterator
 3 {
 4 public:
 5     virtual void first() = 0;
 6     virtual void next() = 0;
 7     virtual bool isDone() const = 0;
 8     virtual T& current() = 0;
 9 };
10 
11 template<typename T>
12 class MyCollection{
13     
14 public:
15     
16     Iterator<T> GetIterator(){   // 返回迭代器
17         //...
18     }    
19 };
20 
21 template<typename T>
22 class CollectionIterator : public Iterator<T>{
23     MyCollection<T> mc;
24 public:
25     
26     CollectionIterator(const MyCollection<T> & c): mc(c){ }
27     
28     void first() override {
29         
30     }
31     void next() override {
32         
33     }
34     bool isDone() const override{
35         
36     }
37     T& current() override{
38         
39     }
40 };
41 
42 void MyAlgorithm()
43 {
44     MyCollection<int> mc;
45     
46     Iterator<int> iter= mc.GetIterator();
47     
48     for (iter.first(); !iter.isDone(); iter.next()){
49         cout << iter.current() << endl;
50     }
51     
52 }
53 
54 
55 // 虚函数调用性能成本
56 
57 // stl 迭代器使用模板(编译时多态),性能高于面向对象的迭代器(运行时多态)
58 
59 // 思想不会过时,实现技术已经过时
Iterator

结构

image

 


Chain of Resposibility

动机(Motivation)

  • 在软件构建的过程中,一个请求可能被多个对象处理,但是每个请求在运行时只能有一个接受者,如果显示指定,将必不可少的带来请求发送者与接受者的耦合。
  • 如何使请求的发送者不需要指定具体的接受者?让请求的接受者自己在运行时决定来处理请求,从而使两者解耦。

模式定义

    使多个对像都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对像连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

                                                                                                             ——《设计模式》GoF

示例伪代码 

 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 enum class RequestType
 7 {
 8     REQ_HANDLER1,
 9     REQ_HANDLER2,
10     REQ_HANDLER3
11 };
12 
13 class Reqest
14 {
15     string description;
16     RequestType reqType;
17 public:
18     Reqest(const string & desc, RequestType type) : description(desc), reqType(type) {}
19     RequestType getReqType() const { return reqType; }
20     const string& getDescription() const { return description; }
21 };
22 
23 class ChainHandler{
24     
25     ChainHandler *nextChain;
26     void sendReqestToNextHandler(const Reqest & req)
27     {
28         if (nextChain != nullptr)
29             nextChain->handle(req);
30     }
31 protected:
32     virtual bool canHandleRequest(const Reqest & req) = 0;
33     virtual void processRequest(const Reqest & req) = 0;
34 public:
35     ChainHandler() { nextChain = nullptr; }
36     void setNextChain(ChainHandler *next) { nextChain = next; }
37     
38    
39     void handle(const Reqest & req)
40     {
41         if (canHandleRequest(req))
42             processRequest(req);
43         else
44             sendReqestToNextHandler(req);
45     }
46 };
47 
48 
49 class Handler1 : public ChainHandler{
50 protected:
51     bool canHandleRequest(const Reqest & req) override
52     {
53         return req.getReqType() == RequestType::REQ_HANDLER1;
54     }
55     void processRequest(const Reqest & req) override
56     {
57         cout << "Handler1 is handle reqest: " << req.getDescription() << endl;
58     }
59 };
60         
61 class Handler2 : public ChainHandler{
62 protected:
63     bool canHandleRequest(const Reqest & req) override
64     {
65         return req.getReqType() == RequestType::REQ_HANDLER2;
66     }
67     void processRequest(const Reqest & req) override
68     {
69         cout << "Handler2 is handle reqest: " << req.getDescription() << endl;
70     }
71 };
72 
73 class Handler3 : public ChainHandler{
74 protected:
75     bool canHandleRequest(const Reqest & req) override
76     {
77         return req.getReqType() == RequestType::REQ_HANDLER3;
78     }
79     void processRequest(const Reqest & req) override
80     {
81         cout << "Handler3 is handle reqest: " << req.getDescription() << endl;
82     }
83 };
84 
85 int main(){
86     Handler1 h1;
87     Handler2 h2;
88     Handler3 h3;
89     h1.setNextChain(&h2);
90     h2.setNextChain(&h3);
91     
92     Reqest req("process task ... ", RequestType::REQ_HANDLER3);
93     h1.handle(req);
94     return 0;
95 }
96 
97 // 职责链在今天用的不多,可以说就是一种数据结构
View Code

结构

image

要点总结

  • Chain of Responsibility模式的应用场合在于“一个请求可能有多个接受者,但是最后真正接受者只有一个”,这时候请求发送者与接受者的耦合可能出现“变化脆弱”的症状,职责链的目的就是将二者解耦,从而更好的应对变化。
  • 应用了Chain of Responsibility模式后,对象的职责分派将更具灵活性。我们可以在运行时动态添加/修改请求的处理指责。
  • 如果请求传递到职责链的末尾仍得不到处理,应该有一个合理的缺省机制。这也是每一个接受者对象的责任,而不是发出请求的对象的责任。

小结

Composite

        将对象组合成树形结构以表示“部分-整体”的层次结构。Composite 使得用户对单个对象和组合对象的使用具有一致性。

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

Iterator

意图
        提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。

适用性

  • 访问一个聚合对象的内容而无需暴露它的内部表示。
  • 支持对聚合对象的多种遍历。
  • 为遍历不同的聚合结构提供一个统一的接口(即, 支持多态迭代)。

Chain of Responsibility

意图
        使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

适用性

  • 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
  • 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
  • 可处理一个请求的对象集合应被动态指定。

 

本文内容源自 :C++设计模式 Design Patterns 李建忠 课程

posted on 2017-12-10 15:52  flysong  阅读(278)  评论(0编辑  收藏  举报

导航