一、基本概念
1.定义
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
4.类似背景举例
请求流程,1 天内需要主程序批准,3 天内需要项目经理批准,3 天以上需要老板批准;
3.要点
①解耦请求方和处理方,请求方不知道请求是如何被处理,处理方的组成是由相互独立的子处理构成,子处理流程通过链表的方式连接,子处理请求可以按任意顺序组合。
②责任链请求强调请求最终由一个子处理流程处理;通过了各个子处理条件判断。
③责任链扩展就是功能链,功能链强调的是,一个请求依次经由功能链中的子处理流程处理。
④将职责以及职责顺序运行进行抽象,那么职责变化可以任意扩展,同时职责顺序也可以任意扩展。
二、结构图
HandleA和 HandleB是通过指针的指向低度耦合的。结构图一开始看不太懂,但是等代码懂了再来看结构图就恍然大悟了。
三、实例说明
1.普通代码
比如说我们需要模拟公司里的请假批准模式,先给上一层领导递请假条,一层领导没权利批准的话再向上一层递交…
普通代码:
#include<iostream>
#include <string>
using namespace std;
class Context {
public:
std::string name;
int day;
};
class LeaveRequest {
public:
// 随着判断的增加,LeaveRequest类变得不稳定
bool HandleRequest(const Context& ctx) {
if (ctx.day <= 1)
return HandleByMainProgram(ctx);
else if (ctx.day <= 3)
return HandleByProjMgr(ctx);
else
return HandleByBoss(ctx);
}
private:
bool HandleByMainProgram(const Context& ctx) {
cout << "主程序批准" << endl;
return true;
}
bool HandleByProjMgr(const Context& ctx) {
cout << "项目经理批准" << endl;
return true;
}
bool HandleByBoss(const Context& ctx) {
cout << "老板批准" << endl;
return true;
}
};
int main()
{
LeaveRequest LR;
Context A;
A.name = "xiaohua";
A.day = 1;
LR.HandleRequest(A);
Context B;
B.name = "xiaomeng";
B.day = 3;
LR.HandleRequest(B);
return 0;
}
普通代码缺点:所有的人和处理方式都被封装到一个类里面,而且如果有人的职责发生变化(比如项目经历的权力 扩大到可以批10天的假),那么我们就需要去修改这一整个类。除此之外,如果以后新增加职位(比如如果新增老板秘书,项目经理秘书可以批准假期请求的话),那么我们也需要在原来的大类中修改增加代码。显然违背了开闭原则,而且由于在一个类里卖弄,所以耦合度极高。可以通过类比现实中层层上报的实际情况结合链表使用的方式解决这个问题,即责任链模式。
2.责任链代码
责任链模式代码:
#include<iostream>
#include <string>
using namespace std;
class Context {
public:
string name;
int day;
};
class IHandler {
public:
void SetNextHandler(IHandler* Handler){
this->next = Handler;
}
bool Handler(const Context& ctx) {
//能批准就自己批准
if (CanHandler(ctx)) {
return HandlerRequest(ctx);
}
//不能批准就递交给上面领导
else if(GetNextHandler()){
return GetNextHandler()->Handler(ctx);
}
//到了最顶层,仍然没人批准
else {
cout << "你的假期无人批准" << endl; //or other
}
return false;
}
virtual ~IHandler() {}
protected:
virtual bool HandlerRequest(const Context& ctx) = 0;
virtual bool CanHandler(const Context& ctx) = 0;
IHandler* GetNextHandler() {
return this->next;
}
private:
IHandler* next;
};
//1天的假期直接在主程序中批准
class HandlerByMainProgram :public IHandler{
public:
bool CanHandler(const Context& ctx) {
return ctx.day <= 1;
}
bool HandlerRequest(const Context& ctx) {
cout << "主程序批准" << endl;
return true;
}
};
//2-3天的假期项目经理批准
class HandlerByProjMgr :public IHandler {
public:
bool CanHandler(const Context& ctx) {
return ctx.day >= 2 && ctx.day <= 3;
}
bool HandlerRequest(const Context& ctx) {
cout << "项目经理批准" << endl;
return true;
}
};
//4-20的假期需要老板批准
class HandlerByBoss :public IHandler {
public:
bool CanHandler(const Context& ctx) {
return ctx.day >= 4 && ctx.day <= 20;
}
bool HandlerRequest(const Context& ctx) {
cout << "老板批准" << endl;
return true;
}
};
//30天以内的假期,老板秘书都可以批准
class HandlerBySecretary :public IHandler{
public:
bool CanHandler(const Context& ctx) {
return ctx.day <= 30;
}
bool HandlerRequest(const Context& ctx) {
cout << "秘书处理了你的假期" << endl;
return true;
}
};
int main()
{
IHandler* H0 = new HandlerByMainProgram();
IHandler* H1 = new HandlerByProjMgr();
IHandler* H2 = new HandlerByBoss();
IHandler* H3 = new HandlerBySecretary();
//连接顺序可以打乱,只不过正常来说,都是有一定要求的
//比如说公司请假制度,一般先小组组长批,如果在小组组长权限外,再层层向上申报
//如果公司有严格的请假制度,对于请假来说,顺序打乱不影响最终是否被批准,但是会改变批准的人
//比如如果秘书在第一个的话,那么在这里所有能批准的假都将由秘书批准(因为30天以内的假秘书都可以批准)
//但是往往权力大的人会先让手下人解决事情,他解决手下解决不掉的事情
H0->SetNextHandler(H1);
H1->SetNextHandler(H2);
H2->SetNextHandler(H3);
Context A;
A.name = "xiaomeng";
A.day = 2;
H0->Handler(A);
Context B;
B.name = "xiaohua";
B.day = 15;
H0->Handler(B);
Context C;
C.name = "xiaopang";
C.day = 40;//暂时没有处理40天假期的操作
H0->Handler(C);
return 0;
}
运行结果:
可以看见,把不同的领导之间给解耦了,没有完全解耦,通过链表的方式隐式联系,这种方式使得她们之间耦合度大大降低,一个领导的权力变化不需要修改其他领导的代码。