1. 职责链模式(Chain Of Responsibility)的定义
(1)定义
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
①标准的职责链模式中,只要有对象处理了请求,这个请求就到此为至,不再被传递和处理了。
②但也可以变形使用职责链,就是让这个请求继续传递,每个职责对象对这个请求进行一定的功能处理,从而形成一个处理请求的功能链。
③当客户端发出请求,它并不知道谁会真正处理他的请求。在职责链模式中,请求不一定会被处理,因为可能没有合适的处理者,这里可以在职责链的末端加一个默认处理的职责对象。
④职责链可以是一条直线、一个环或者一个树形结构,最常见的职责链是直线型,即沿着一条单向的链来传递请求。
(2)职责链模式的结构和说明
①Handler:定义职责的接口,通常在这里定义处理请求的方法,可以在这里实现后继链(successor)。
②ConcreteHandler:实现职责的类,在这个类中,实现对它职责范围内请求的处理,如果不处理,就继续转发请求给后继者。
③Client:职责链的客户端,向链上的具体处理对象提交请求,让职责链负责处理。
【编程实验】HR系统中的请假审批流程
//行为型模式:职责链模式
//场景:请假条审批流程案例
//如果请假天数小于3天,主任审批
//如果请假天数大于等于3天,小于10天,经理审批
//如果大于等于10天,小天30天,总经理审批
//如果大于等于30天,提示拒绝
#include <iostream>
#include <string>
using namespace std;
//***************************辅助类*****************
//请假条
class LeaveRequest
{
private:
string empName; //姓名
int leaveDay; //请假天数
string reason; //请假原因
public:
LeaveRequest(string name,int leaveDay,string reason)
{
this->empName = name;
this->leaveDay = leaveDay;
this->reason = reason;
}
string& getEmpName(){return this->empName;}
void setEmpName(string name){this->empName = name;}
int getLeaveDay(){return this->leaveDay;}
void setLeavDay(int day){this->leaveDay = day;}
string& getReason(){return this->reason;}
void setReason(string reason){this->reason = reason;}
};
//***************************抽象职责类*************
class Leader
{
protected:
string name;
Leader* nextLeader; //职责链上的后继对象
public:
Leader(string name)
{
this->name = name;
nextLeader = NULL;
}
void setNextLeader(Leader* leader){this->nextLeader = leader;}
virtual void handleRequest(LeaveRequest* req) = 0;
};
//***********************具体职责实现类**************************
//主任
class Director : public Leader
{
public:
Director(string name):Leader(name){}
void handleRequest(LeaveRequest* req)
{
if(req->getLeaveDay() < 3)
{
cout <<"Name: " << req->getEmpName() <<", LeaveDay: " << req->getLeaveDay() <<", "
<<"Reason: " << req->getReason()
<<"----->" << "Director: " << name << " Approval!"<<endl;
}
else
{
if (nextLeader != NULL)
nextLeader->handleRequest(req);
}
}
};
//经理
class Manger : public Leader
{
public:
Manger(string name):Leader(name){}
void handleRequest(LeaveRequest* req)
{
if(3 <= req->getLeaveDay() && req->getLeaveDay() < 10)
{
cout <<"Name: " << req->getEmpName() <<", LeaveDay: " << req->getLeaveDay() <<", "
<<"Reason: " << req->getReason()
<<"----->" << "Manger: " << name << " Approval!"<<endl;
}
else
{
if (nextLeader != NULL)
nextLeader->handleRequest(req);
}
}
};
//总经理
class GeneralManger : public Leader
{
public:
GeneralManger(string name):Leader(name){}
void handleRequest(LeaveRequest* req)
{
if(10 <= req->getLeaveDay() && req->getLeaveDay() < 30)
{
cout <<"Name: " << req->getEmpName() <<", LeaveDay: " << req->getLeaveDay() <<", "
<<"Reason: " << req->getReason()
<<"----->" << "GeneralManger: " << name << " Approval!"<<endl;
}
else
{
cout <<"Name: " << req->getEmpName() <<", LeaveDay: " << req->getLeaveDay() <<" Are Refused!" << endl;
}
}
};
int main()
{
Leader* director= new Director("ZhangSan");
Leader* manager = new Manger("LiSi");
Leader* generalManage = new GeneralManger("WangWu");
//组织职责链对象的关系
director->setNextLeader(manager);
manager->setNextLeader(generalManage);
//开始请假操作
LeaveRequest req("Tom", 30, "go home!");
director->handleRequest(&req);
delete director;
delete manager;
delete generalManage;
return 0;
}
(3)思考职责链模式
①职责链的本质:分离职责,动态组合。分离职责是前提,动态组合才是职责链模式的精华所在,因为这意味着可以很方便地修改和添加新的处理对象,从而让系统更加灵活和具有更好的扩展性。
②职责链的动机:在软件构建过程中,一个请求可能被多个对象处理,但是每个请求在运行时需要有接受者,如果显式指定,将必不可少地带来请求发送者与接受者之间的紧耦合。而职责链模式可以将这两者解耦,使得发送者不必知道具体的接受者(处理者)
2. 功能链(如过滤器)
(1)概念:实际开发中,经常将一个请求在职责链中传递,每个职责对象负责处理请求的某一方面的功能,处理完后不是停止,而是继续向下传递请求,当请求通过很多职责对象处理后,功能也就完成了,把这样的职责链称为功能链。
(2)应用举例
①实际开发中,在业务处理之前,通常需要进行权限检查、数据校验、逻辑检查等,然后才开始真正的业务逻辑。可以把这些功能分散到一个功能链中。
②过滤器:每个过滤器负责自己的处理,然后转交给下一个过滤器,直到把所有的过滤器都走完(如权限检查、字符转换等)。
【编程实验】字符过滤器(模拟JavaWeb的双向过滤器,变式的职责链模式)
//行为型模式:职责链模式
//场景:字符过滤(模拟JavaWeb的双向过滤器)
//当客户端发送给服务器时,request字符串被各个过滤器按顺序处理,
//而返回的response是逆着过滤器被调用顺序被处理的。(可参考下面的技巧)
//思路细节技巧:
//(1)Filter的doFilter方法改为doFilter(Request*,Resopnse*,FilterChain*),有
//FilterChain指针,为利用FilterChain调用下一个Filter做准备
//(2)FilterChain继承Filter,这样,FilterChain既是FilterChain又是Filter,那么
//FilterChain就可以调用Filter的方法doFilter(Request*,Resopnse*,FilterChain*)
//(3)FilterChain的doFilter(Request*,Resopnse*,FilterChain*)中,有index标记了执
//行到第几个Filter,当所有Filter执行完后request处理后,就会return,以倒序继续执
//行response处理
#include <iostream>
#include <string>
#include <vector>
using namespace std;
//*************************************************字符串替换函数*********************************
void Replace(std::basic_string<char>& s, const std::basic_string<char>& src, const std::basic_string<char>& dest)
{
std::basic_string<char>::size_type pos = 0;
while (true)
{
pos = s.find(src, pos);
if (std::basic_string<char>::npos == pos)
break;
s.replace(pos, src.size(), dest);
pos += src.size();
}
}
//***************************辅助类*****************
//请求类
class Request
{
private:
string context; //请求的内容
public:
Request(string context)
{
this->context = context;
}
string& getContext(){return this->context;}
void setContext(string context){this->context = context;}
};
//响应类
class Response
{
private:
string context; //请求的内容
public:
Response(string context)
{
this->context = context;
}
string& getContext(){return this->context;}
void setContext(string context){this->context = context;}
};
//***************************抽象职责类*************
//职责链
class FilterChain; //前向声明
//Filter(抽象过滤器)
class Filter
{
public:
virtual void doFilter(Request* request,Response* response, FilterChain* chain) = 0;
};
//FilterChain(过滤器链)
//注意,FilterChain继承自Filter,是为了这个链可以像其他过滤器(如HTMLFilter)一样地被加入到其他链中
//从而,可以将两条FilterChain连接成一条。
class FilterChain : public Filter
{
private:
vector<Filter*> filters;
int index;
public:
FilterChain(){index = 0;}
FilterChain& addFilter(Filter* f)
{
filters.push_back(f);
return *this;
}
//将链本身作为一个过滤器,也实现了doFilter功能,但这里只是简单地调用链中各个
//过滤器并记录下一个过滤器的索引
void doFilter(Request* request,Response* response, FilterChain* chain)
{
if(index == filters.size())
return;
Filter* f = filters[index++];
f->doFilter(request,response,chain);
}
};
//HtmlFilter
class HTMLFilter : public Filter
{
public:
//当客户端发送给服务器时,request字符串被各个过滤器后按顺序处理,
//而返回的response逆着过滤器被调用顺序被处理。(可参考下面的技巧)
void doFilter(Request* request,Response* response, FilterChain* chain)
{
//1、处理html标签
Replace(request->getContext(), "<", "[");
Replace(request->getContext(), ">", "]");
request->getContext() +="-->HTMLFilter()";
//2、调用下一个过滤器(注意里面可能会递归)
chain->doFilter(request, response, chain);
//3、在前2步之后,再处理response,可以达到栈式(逆序)处理response字符串-->技巧!
response->getContext() += "-->HTMLFilter()";
}
};
//SensitiveFilter(敏感关键字过滤器)
class SensitiveFilter : public Filter
{
public:
void doFilter(Request* request,Response* response, FilterChain* chain)
{
//1、处理敏感关键词
Replace(request->getContext(), "敏感", "");
Replace(request->getContext(), "被就业", "就业");
request->getContext() +="-->SensitiveFilter()";
//2、调用下一个过滤器(注意里面可能会递归)
chain->doFilter(request, response, chain);
//3、在前2步之后,再处理response,可以达到栈式(逆序)处理response字符串-->技巧!
response->getContext() += "-->SensitiveFilter()";
}
};
//FaceFilter(表情过滤器)
class FaceFilter : public Filter
{
public:
void doFilter(Request* request,Response* response, FilterChain* chain)
{
//1、处理敏感关键词
Replace(request->getContext(), ":)", "^V^");
request->getContext() +="-->FaceFilter()";
//2、调用下一个过滤器(注意里面可能会递归)
chain->doFilter(request, response, chain);
//3、在前2步之后,再处理response,可以达到栈式(逆序)处理response字符串-->技巧!
response->getContext() += "-->FaceFilter()";
}
};
int main()
{
string msg = "大家好:),<script>,敏感,被就业,网络授课没感觉,因为看不见大家伙儿";
Request request("Request:" + msg);
Response response("Response: ");
//创建3个过滤器
Filter* htmlFilter = new HTMLFilter(); //HTML标签过滤
Filter* sensitiveFilter = new SensitiveFilter(); //敏感关键词过滤
Filter* faceFilter = new FaceFilter(); //表情过滤
//第1条过滤链(有两个过滤器)
FilterChain fc;
fc.addFilter(htmlFilter);
fc.addFilter(sensitiveFilter);
//第2条过滤链(只有一个过滤器)
FilterChain fc2;
fc2.addFilter(faceFilter);
//交两条过滤链合成一条过滤链(共有3个过滤器)
fc.addFilter(&fc2);
//开始过滤
fc.doFilter(&request, &response, &fc);
cout << request.getContext() << endl;
cout << response.getContext() << endl;
delete htmlFilter;
delete sensitiveFilter;
delete faceFilter;
return 0;
}
/*
输出结果:(注意request和response中过滤器被调用的顺序是相反的)
Request:大家好^V^,[script],,就业,网络授课没感觉,因为看不见大家伙儿-->HTMLFilter()-->SensitiveFilter()-->FaceFilter()
Response: -->FaceFilter()-->SensitiveFilter()-->HTMLFilter()
*/
3. 职责链的优缺点
(1)优点
①请求者和接收者的松散耦合
②动态组合职责:职责链模式把功能处理分散到单独的职责对象中,然后在使用的时候可以动态组合职责形成职责链,从而可以灵活地给对象分配职责,也可以灵活地实现和改变对象的职责。
(2)缺点
①产生很多细粒度对象
②不一定能处理:职责链模式的每个职责对象只负责自己处理的那一部分,因此可能会出现某个请求在整个链中找到不处理的合适对象。这就需要在使用职责链模式的时候,提供默认的处理,并且注意构建的链的有效性。
③性能问题:每个请求都是从链头遍历到链尾,特别是在链比较长的时候,性能是一个非常大的问题。
④调试不方便,特别是链条比较长时,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。
4. 职责链的应用场景
(1)UI中处理用户事件,当用户点击鼠标或按键盘,一个事件产生并沿链传播。
(2)过滤器
(3)人事管理系统中的请求、审批流程管理
(4)有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
(5)如果不想明确指定接收者,向多个对象中的其中一个提交请求。
(6)动态指定一个请求的处理对象集合,在运行时动态地决定到底由哪些对象参与到处理请求中来。
5. 相关模式
(1)职责链模式和组合模式
①两个模式可以组合使用,可以把职责对象通过组合模式来组合,这样可以通过组合对象自动递归地向上调用,由父组件作为子组件的后继,从而形成链。
②这时,客户端使用时就不再需要构造链了(但需要构造组合对象树)。
(2)职责链模式和装饰模式
①这两个模式相似,从某个角度讲,可以相互模式实现对象的功能。
②装饰模式能够动态地给被装饰的对象添加功能,要求装饰器对象和被装饰器对象实现相同的接口。而职责链模式可以实现动态的职责组合,标准处理是有一个对象处理就结束,但如果处理完本职责不急于结束,而是继续向下一个后继传递请求,那就和装饰器模式的功能差不多了,每个职责对象就类似于装饰器。
③主要区别:装饰模式是无限递归调用,可以有任意多个对象来装饰功能,但职责链模式是有一个处理就结束。