行为型模式—职责链模式
Posted 浅墨浓香
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了行为型模式—职责链模式相关的知识,希望对你有一定的参考价值。
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)职责链模式和装饰模式
①这两个模式相似,从某个角度讲,可以相互模式实现对象的功能。
②装饰模式能够动态地给被装饰的对象添加功能,要求装饰器对象和被装饰器对象实现相同的接口。而职责链模式可以实现动态的职责组合,标准处理是有一个对象处理就结束,但如果处理完本职责不急于结束,而是继续向下一个后继传递请求,那就和装饰器模式的功能差不多了,每个职责对象就类似于装饰器。
③主要区别:装饰模式是无限递归调用,可以有任意多个对象来装饰功能,但职责链模式是有一个处理就结束。
以上是关于行为型模式—职责链模式的主要内容,如果未能解决你的问题,请参考以下文章
设计模式 行为型模式 -- 职责链模式(定义结构纯与不纯的职责链模具体案例)