前言

尊重原创,本系列文本解析部分主要基于Candidate for Master’s Degree School of Computer Wuhan University的K_Eckel([email protected])《设计模式精解-GoF 23 种设计模式解析附 C++实现源码》。为了避免重造轮子,本系列博文对源码在ubuntu16.04下做了验证并上传到了gitee,再次感谢。

如有问题,可邮件联系我([email protected])并共同探讨解决方案。

目录

创建型模式(Creating Pattern)

Factory 模式 | AbstactFactory 模式 | Singleton 模式 | Builder 模式 | Prototype 模式

结构型模式(Structrual Pattern)

Bridge 模式 | Adapter 模式 | Decorator 模式 | Composite 模式 | Flyweight 模式 | Facade 模式 | Proxy 模式

行为型模式(Behavioral Pattern)

Template 模式 | Strategy 模式 | State 模式 | Observer 模式 | Memento 模式 | Mediator 模式 | Command 模式 | Visitor 模式 | Iterator 模式 | Interpreter 模式 | Chain of Responsibility 模式

Chain of Responsibility 模式简介:

   顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。

   在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。比如红楼梦中的"击鼓传花"。

问题:

   熟悉VC/MFC的都知道,VC是“基于消息,事件驱动”,消息在VC开发中起着举足轻重的作用。在MFC中,消息是通过一个向上递交的方式进行处理,例如一个WM_COMMAND消息的处理流程可能为:

  • MDI主窗口(CMDIFrameWnd)收到命令消息WM_COMMAND,其ID位ID_×××
  • MDI主窗口将消息传给当前活动的MDI子窗口(CMDIChildWnd);
  • MDI子窗口给自己的子窗口(View)一个处理机会,将消息交给View
  • View检查自己Message Map
  • 如果View没有发现处理该消息的程序,则将该消息传给其对应的Document对象;否则View处理,消息流程结束。
  • Document检查自己Message Map,如果没有该消息的处理程序,则将该消息传给其对象的DocumentTemplate处理;否则自己处理,消息流程结束;
  • 如果在上一步中消息没有得到处理,则将消息返回给View
  • View再传回给MDI子窗口;
  • MDI子窗口将该消息传给CwinApp对象,CwinApp为所有无主的消息提供了处理。

有关MFC消息处理更加详细信息,请参考候捷先生的《深入浅出MFC》。

   MFC提供了消息的处理的链式处理策略,处理消息的请求将沿着预先定义好的路径依次进行处理。消息的发送者并不知道该消息最后是由那个具体对象处理的,当然它也无须也不想知道,但是结构是该消息被某个对象处理了,或者一直到一个终极的对象进行处理了。

   Chain of Responsibility模式描述其实就是这样一类问题将可能处理一个请求的对象链接成一个链,并将请求在这个链上传递,直到有对象处理该请求(可能需要提供一个默认处理所有请求的类,例如MFC中的CwinApp类)。

模式选择

   Chain of Responsibility模式典型的结构图为:
Chain of Responsibility
图 1:Chain of Responsibility 模式结构示意图

   Chain of Responsibility模式中ConcreteHandler将自己的后继对象(向下传递消息的对象)记录在自己的后继表中,当一个请求到来时,ConcreteHandler会先检查看自己有没有匹配的处理程序,如果有就自己处理,否则传递给它的后继。当然这里示例程序中为了简化,ConcreteHandler只是简单的检查看自己有没有后继,有的话将请求传递给后继进行处理,没有的话就自己处理。

实现

完整代码示例(code)

   Chain of Responsibility模式的实现比较简单,这里为了方便初学者的学习和参考,将给出完整的实现代码(所有代码采用 C++实现,并在 Visual Studio Code,Version: 1.36.1 下测试运行)。

源码gitee地址:点击这里

代码目录结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
PS C:\Users\guopz\Desktop\GiteeBlog\designpatternsbycpipei\behavioralPattern\ChainofResponsibility> tree /F
卷 Windows 的文件夹 PATH 列表
卷序列号为 F0C5-AFA6
C:.
├─include
│ Handle.h

└─src
a.out
Handle.cpp
main.cpp

PS C:\Users\guopz\Desktop\GiteeBlog\designpatternsbycpipei\behavioralPattern\ChainofResponsibility>

Handle.h:

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
// Handle.h

#ifndef _HANDLE_H_
#define _HANDLE_H_

class Handle {
private:
Handle* _succ;

protected:
Handle(/* args */);
Handle(Handle* succ);

public:
virtual ~Handle();
virtual void HandleRequest() = 0;
void SetSuccessor(Handle* succ);
Handle* GetSuccessor();
};

class ConcreteHandleA:public Handle
{
private:
/* data */
public:
ConcreteHandleA(/* args */);
~ConcreteHandleA();
ConcreteHandleA(Handle* succ);
void HandleRequest();
};

class ConcreteHandleB:public Handle
{
private:
/* data */
public:
ConcreteHandleB(/* args */);
~ConcreteHandleB();
ConcreteHandleB(Handle* succ);
void HandleRequest();
};

#endif //~_HANDLE_H_

Handle.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
// Handle.cpp

#include "../include/Handle.h"
#include <iostream>

using namespace std;

Handle::Handle(/* args */) { _succ = 0; }
Handle::Handle(Handle* succ) { this->_succ = succ; }
Handle::~Handle() { delete _succ; }
void Handle::SetSuccessor(Handle* succ) { _succ = succ; }
Handle* Handle::GetSuccessor() { return _succ; }
void Handle::HandleRequest() {}

ConcreteHandleA::ConcreteHandleA(/* args */) {}
ConcreteHandleA::ConcreteHandleA(Handle* succ) : Handle(succ) {}
ConcreteHandleA::~ConcreteHandleA() {}
void ConcreteHandleA::HandleRequest() {
if (this->GetSuccessor() != 0) {
printf("ConcreteHandleA 我把处理权给后继节点..... \n");
this->GetSuccessor()->HandleRequest();
} else {
printf("ConcreteHandleA 没有后继了,我必须自己处理..... \n");
}
}

ConcreteHandleB::ConcreteHandleB(/* args */) {}
ConcreteHandleB::ConcreteHandleB(Handle* succ) : Handle(succ) {}
ConcreteHandleB::~ConcreteHandleB() {}
void ConcreteHandleB::HandleRequest() {
if (this->GetSuccessor() != 0) {
printf("ConcreteHandleB 我把处理权给后继节点..... \n");
this->GetSuccessor()->HandleRequest();
} else {
printf("ConcreteHandleB 没有后继了,我必须自己处理..... \n");
}
}


main.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//main.cpp

#include "../include/Handle.h"
#include <iostream>

using namespace std;

int main(int argc,char* argv[]){
Handle* h1 = new ConcreteHandleA();
Handle* h2 = new ConcreteHandleB();
h1->SetSuccessor(h2);
h1->HandleRequest();

return 0;
}

代码说明

   Chain of Responsibility模式的示例代码实现很简单,这里就其测试结果给出说明:ConcreteHandleA的对象和h1拥有一个后继ConcreteHandleB的对象h2,当一个请求到来时候,h1检查看自己有后继,于是h1直接将请求传递给其后继h2进行处理,h2因为没有后继,当请求到来时候,就只有自己提供响应了。于是程序的输出为:

编译运行结果:

1
2
3
4
5
6
7
8
9
10
guopz@Patten-DESKTOP MINGW64 ~/Desktop/GiteeBlog/designpatternsbycpipei/behavioralPattern/ChainofResponsibility/src (master)
$ g++ *.cpp -std=c++11

guopz@Patten-DESKTOP MINGW64 ~/Desktop/GiteeBlog/designpatternsbycpipei/behavioralPattern/ChainofResponsibility/src (master)
$ ./a.exe
ConcreteHandleA 我把处理权给后继节点.....
ConcreteHandleB 没有后继了,我必须自己处理.....

guopz@Patten-DESKTOP MINGW64 ~/Desktop/GiteeBlog/designpatternsbycpipei/behavioralPattern/ChainofResponsibility/src (master)
$

讨论

   Chain of Responsibility模式的最大的一个点就是给系统降低了耦合性,请求的发送者完全不必知道该请求会被哪个应答对象处理,极大地降低了系统的耦合性。