不落辰

知不可乎骤得,托遗响于悲风

0%

组件协作模式

组件协作模式:模板方法、策略模式、观察者模式

  • 前言
    • 设计模式的假定条件:必须有一个稳定点!
    • 所有都是变化的,那么没有任何一个设计莫斯是有用的
    • 所有都是稳定的,那么就没有必要用设计模式了。
    • 设计模式就是在稳定和变化中寻求隔离
    • 大多数设计模式都追求:稳定中有变化

      组件协作模式

模板方法

动机

  • 在软件构建过程中,对于某一项任务,它常常有稳定的整体操作
    结构,但各个子步骤却有很多改变的需求,或者由于固有的原因
    (比如框架与应用之间的关系)而无法和任务的整体结构同时实现
  • 如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变
    化或者晚期实现需求

定义

  • 定义一个操作中的算法的骨架 (稳定),而将一些步骤延迟(变化)到子类中。Template Method使得子类可以不改变(复用)一个算法的结构即可重定义(override 重写)该算法的
    某些特定步骤
    • 延迟到子类:让子类实现或override父类的virtual
    • 如果没有稳定的骨架(Run)怎么办?那么前提就不成立了,就要换一种设计模式

早/晚绑定

  • 反向控制结构

代码

  • 稳定的函数:设非虚
  • 变化的函数:设virtual
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    //程序库开发人员
    class Library{
    public:
    // 非虚函数Run:稳定
    // 虚函数:变化(step3,step4)
    //稳定 template method
    void Run(){

    Step1();

    if (Step2()) { //支持变化 ==> 虚函数的多态调用
    Step3();
    }

    for (int i = 0; i < 4; i++){
    Step4(); //支持变化 ==> 虚函数的多态调用
    }

    Step5();

    }
    virtual ~Library(){ }

    protected:

    void Step1() { //稳定
    //.....
    }
    void Step3() {//稳定
    //.....
    }
    void Step5() { //稳定
    //.....
    }

    virtual bool Step2() = 0;//变化
    virtual void Step4() =0; //变化
    };


    //应用程序开发人员
    class Application : public Library {
    protected:
    virtual bool Step2(){
    //... 子类重写实现
    }

    virtual void Step4() {
    //... 子类重写实现
    }
    };

    int main()
    {
    Library* pLib=new Application();
    lib->Run();

    delete pLib;
    }
    }

结构

总结

  • Template Method模式是一种非常基础性的设计模式,在面向对
    象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性)
    为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本
    实现结构。
  • 除了可以灵活应对子步骤的变化外,“不要调用我,让我来调用
    你”的反向控制结构Template Method的典型应用。
  • 在具体实现方面,被Template Method调用的虚方法可以具有实
    现,也可以没有任何实现(抽象方法、纯虚方法),但一般推荐将
    它们设置为protected方法(一般在父类所写的核心流程里的上下文中调用才有意义,类外调用没意义)。

策略模式

动机

  • 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担
  • 如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题?

定义

  • 定义一系列算法,把它们一个个封装起来,并且使它们可互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)变化(扩展,子类化)

什么是复用,什么是扩展/修改?

  • 扩展是为了实现复用.

    复用

  • 拷贝粘贴不叫复用
  • 不是源代码级别的复用
  • 复用而是编译、部署之后,二进制级别的复用。是编译部署之后,原封不动的。
  • 所以,在一堆if-else后面再加一个if-else,是修改而不是扩展
    • 不是扩展(改变了此文件的源代码,需要重新编译)
    • 因此没实现复用(复用之前的if-else?放屁!)

扩展

  • 不改变现有类的代码,而是通过新增加一个文件来增加一个类等.

修改

  • 修改现有类的源代码

  • 开闭原则

    • 对扩展开放
    • 对修改关闭
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
enum TaxBase {
CN_Tax,
US_Tax,
DE_Tax,
FR_Tax //更改
};

// 增加新算法时,需要修改代码(加if-else),违反开闭原则
class SalesOrder{
TaxBase tax;
public:
double CalculateTax(){
//...
if (tax == CN_Tax){
//CN***********
}
else if (tax == US_Tax){
//US***********
}
else if (tax == DE_Tax){
//DE***********
}
else if (tax == FR_Tax){ //更改
// 这里叫做修改 而非扩展!!
//...
}
//....
}
};

最终

  • 扩展
  • 如果有改变的话,我们新增文件FRTax.cpp , 在里面写要增加的类即可 , 其他类文件的代码不会改变 , 实现了(二进制级别的)复用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    class TaxStrategy{
    public:
    virtual double Calculate(const Context& context)=0;
    virtual ~TaxStrategy(){}
    };

    class CNTax : public TaxStrategy{
    public:
    virtual double Calculate(const Context& context){
    //***********
    }
    };

    class USTax : public TaxStrategy{
    public:
    virtual double Calculate(const Context& context){
    //***********
    }
    };

    class DETax : public TaxStrategy{
    public:
    virtual double Calculate(const Context& context){
    //***********
    }
    };

    // 扩展
    // 如果有改变的话,我们新增文件FRTax.cpp , 在里面写要增加的类即可.
    // 其他类文件的代码不会改变
    // 实现了(二进制级别的)复用
    //*********************************
    class FRTax : public TaxStrategy{
    public:
    virtual double Calculate(const Context& context){
    //.........
    }
    };

    class SalesOrder{
    private:
    TaxStrategy* strategy; // 常用指针而非引用

    public:
    SalesOrder(StrategyFactory* strategyFactory){ // 工厂
    this->strategy = strategyFactory->NewStrategy();
    }
    ~SalesOrder(){
    delete this->strategy;
    }

    public double CalculateTax(){
    //...
    Context context;
    double val =
    strategy->Calculate(context); //多态调用
    //...
    }
    };

结构

总结

  • 用扩展的方式,去面对需求的变化

  • Strategy及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换

    • 运行时:多态,虚函数调用
  • Strategy模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式!!!

  • 如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省对象开销.

  • 出现很多if-else的地方,且可能需要增加改变时,就是我们需要用到策略模式的地方.

  • if-else : bad smile

  • 实际上的算法远比上面的+-复杂。

补充

  • 策略模式仅仅封装算法
  • 方便新的算法插入到已有系统中,以及老算法从系统中“退休”,实现方法替换
  • 策略模式并不决定何时使用何种算法,什么情况下使用什么算法由用户决定。
  • 策略模式的重心:策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。
  • 算法的平等性:策略模式一个很大的特点就是各个策略算法的平等性。对于一系列具体的策略算法,大家的地位是完全一样的,正因为这个平等性,才能实现算法之间可以相互替换。所有的策略算法在实现上也是相互独立的,相互之间是没有依赖的
  • 运行时策略的唯一性:运行期间,策略模式在每一个时刻只能使用一个具体的策略实现对象,虽然可以动态地在不同的策略实现中切换,但是同时只能使用一个
  • 它把采取哪一种算法或采取哪一种行为的逻辑与算法本身分离,避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展

缺点

  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的情况。
  • 由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观

观察者模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169

/*
行为型模式:主要关注的是对象之间的通信
观察者模式Observer Pattern(发布-订阅模式)设计模式:主要关注的是对象的一对多的关系,也就是多个对象
都依赖一个对象,当该对象的状态发生改变时,其它对象都能够接收到相应的通知。

一组数据(数据对象) => 通过这一组数据 =》 曲线图(对象1)/柱状图(对象2)/圆饼图(对象3)
当数据对象改变时,对象1、对象2、对象3应该及时的收到相应的通知!

Observer1 Observer2 Observer3

Subject(主题)主题有更改,应该及时通知相应的观察者,去处理相应的事件
*/
#include<iostream>
#include<unordered_map>
#include<list>

using namespace std;

// 观察者模式 / 监听者模式

// 发布-订阅

// 观察者 Observer
class Observer
{
public:
virtual void handler(int msg) = 0;
virtual ~Observer(){}
};

class Observer1 : public Observer
{
public:
void handler(int msg) override
{
switch(msg)
{
case 1:
cout<<"Observer1 recv 1 msg"<<endl;
break;
case 2:
cout<<"Observer1 recv 2 msg"<<endl;
break;
default:
cout<<"Observer1 recv unknown msg"<<endl;
}
}
};


class Observer2:public Observer
{
public:
void handler(int msg) override
{
switch(msg)
{
case 1:
cout<<"Observer2 recv 1 msg"<<endl;
break;
default:
cout<<"Observer2 recv unknown msg"<<endl;
}
}

};


class Observer3:public Observer
{
public:
void handler(int msg) override
{
switch(msg)
{
case 1:
cout<<"Observer3 recv 1 msg"<<endl;
break;
case 2:
cout<<"Observer3 recv 2 msg"<<endl;
break;
case 3:
cout<<"Observer3 recv 3 msg"<<endl;
break;
default:
cout<<"Observer3 recv unknown msg"<<endl;
}
}
};

// 主题类
// 记录了每个要观察的对象int msg和观察者Observe的关联情况
// 内含会发生变化的主体。也就是观察者要观察的东西
class Subject
{
public:
void addObserver(Observer *obs,int msg)
{
// m_subMap[msg].push_back(obs); // 原先没有:创建并加入;原先有:加入
unordered_map<int,list<Observer*>> ::iterator iter = m_subMap.find(msg);
if(iter==m_subMap.end())
{
// m_subMap.insert({msg,list<Observer*>{obs}});
list<Observer*> l;
l.push_back(obs);
m_subMap.insert({msg,l});
}
else
{
m_subMap[msg].push_back(obs);
}
return ;
}

// 检测发生改变,通知相应观察者对象处理事件
// msg:要改变的对象(这里用int代替,实际上会是一个事件对象,对象内部的属性发生改变)
void dispatch(int msg) // 派遣
{
auto iter = m_subMap.find(msg);
if(iter==m_subMap.end()) return ;
for(Observer *obs : iter->second)
{
obs->handler(msg); // 处理变化
}
}

private:
unordered_map<int,list<Observer*> > m_subMap;
};

int main()
{
Subject s;
Observer1 obs1;
Observer2 obs2;
Observer3 obs3;

s.addObserver(&obs1,1);
s.addObserver(&obs1,2);
s.addObserver(&obs2,1);
s.addObserver(&obs3,1);
s.addObserver(&obs3,2);
s.addObserver(&obs3,3);

int ch;
while(1)
{
cout<<"几号事件发生改变"<<endl;
cin>>ch;
if(ch==-1) break;
s.dispatch(ch); // 主体通知事件
}
}

鍑犲彿浜嬩欢鍙戠敓鏀瑰彉
7
鍑犲彿浜嬩欢鍙戠敓鏀瑰彉
1
Observer1 recv 1 msg
Observer2 recv 1 msg
Observer3 recv 1 msg
鍑犲彿浜嬩欢鍙戠敓鏀瑰彉
2
Observer1 recv 2 msg
Observer3 recv 2 msg
鍑犲彿浜嬩欢鍙戠敓鏀瑰彉
3
Observer3 recv 3 msg