行为型模式—职责链模式

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)职责链模式和装饰模式

  ①这两个模式相似,从某个角度讲,可以相互模式实现对象的功能。

  ②装饰模式能够动态地给被装饰的对象添加功能,要求装饰器对象和被装饰器对象实现相同的接口。而职责链模式可以实现动态的职责组合标准处理是有一个对象处理就结束,但如果处理完本职责不急于结束,而是继续向下一个后继传递请求,那就和装饰器模式的功能差不多了,每个职责对象就类似于装饰器。

  ③主要区别:装饰模式是无限递归调用,可以有任意多个对象来装饰功能,但职责链模式是有一个处理就结束。

以上是关于行为型模式—职责链模式的主要内容,如果未能解决你的问题,请参考以下文章

设计模式 行为型模式 -- 职责链模式(定义结构纯与不纯的职责链模具体案例)

设计模式 行为型模式 -- 职责链模式(JDK源码分析:FilterChain(过滤器))

JavaScript设计模式行为型设计模式--职责链模式

设计模式-行为型-职责链模式

行为型模式之职责链模式

设计模式-行为型-职责链设计模式