职责链模式

– 组长,我想请5天假? – 我没权利批准,我去帮你问问老板~

意图

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

UML 图

uml

  • Handler
    • 定义一个处理请求的接口
    • (可选) 实现后继链
  • ConcreteHander
    • 处理它负责的请求
    • 可访问它的后继者
    • 如果可处理该请求,就处理之;否则将该请求转发给它的后继者
  • Client : 向链上的具体处理者对象提交请求

其实看完这个 UML 图基本上已经确定了代码的核心

  • 类的成员变量中需要维护它的后继者
  • 如果可处理该请求,就处理之;否则将该请求转发给它的后继者

这个模式的设计的难点可能就是处理问题权限或者能力的范围划定的问题

应用场景

  • 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
  • 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
  • 可处理一个请求的对象集合应被动态指定。

举个例子

很喜欢 《大话设计模式》 中举的例子,我稍微改动一下

请 3 天以内的假 项目组长 直接就能完成审批,请 3-7 天假就需要项目组长上面的 部门组长 签字,7 天以上, 不予批准

这里先实现处理请求的基类 Leader, 定义设置上级的 setSupLeader() 函数, 以及处理请假请求的 askForLeave()

  • m_supLeader : 来管理后继者
  • m_supportNumberDays : 简单的保存 Leader 支持的请假天数
#define UNUSED(x) (void)x

#include <iostream>
using namespace std;

class Leader {
public:
    Leader() : m_supLeader(nullptr), m_supportNumberDays(0) {}
    virtual ~Leader() {}

    void setSupLeader(Leader *leader) {
        m_supLeader = leader;
    }

    virtual void askForLeave(int day) {
        if ( day <= m_supportNumberDays)
            allowedToLeave(day);
        else
            noAuthority(day);
    }

private:
    virtual void allowedToLeave(int day) { UNUSED(day);}
    virtual void noAuthority(int day) { UNUSED(day);}

protected:
    Leader *m_supLeader;
    int m_supportNumberDays;
};

接下来先实现项目组长, 3天以内的直接批准了, 3天以上的, 查看一下上级是否存在, 存在就递给上级, 不存在, 直接打印 上级不在, 请重新申请!

class ProjectLeader : public Leader {
public:
    ProjectLeader() : Leader() {
        m_supportNumberDays = 3;
    }

private:
    virtual void allowedToLeave(int day) {
        cout << "项目组长批准 " << day << " 天请假!" << endl;
    }

    virtual void noAuthority(int day) {
        if (m_supLeader)
            m_supLeader->askForLeave(day);
        else
            cout << "上级不在, 请重新申请!" << endl;
    }
};

再来就是部门组长的代码, 他处理 7 天以内的假期, 超过 7 天的, 直接打印 请假时间过长! 不予批准

class GroupLeader : public Leader {
public:
    GroupLeader() : Leader() {
        m_supportNumberDays = 7;
    }
private:
    virtual void allowedToLeave(int day) {
        cout << "部门组长批准 " << day << " 天请假!" << endl;
    }

    virtual void noAuthority(int day) {
        UNUSED(day);
        cout << "请假 " << day << " 天过长! 不予批准" << endl;
    }
};

最后就是我们的客户端调用部分, 先请 2 天的, 再请 5 天, 最后请 10 天

#include <leader.h>

#define SAFE_DELETE(p) if(p) { delete p; p=nullptr;}

int main()
{
    ProjectLeader *projectLeader = new ProjectLeader;
    projectLeader->askForLeave(2);

    projectLeader->askForLeave(5);

    GroupLeader *groupLeader = new GroupLeader;
    projectLeader->setSupLeader(groupLeader);
    projectLeader->askForLeave(5);

    projectLeader->askForLeave(10);

    SAFE_DELETE(groupLeader);
    SAFE_DELETE(projectLeader)
    return 0;
}

运行结果

项目组长批准 2 天请假!
上级不在, 请重新申请!
部门组长批准 5 天请假!
请假 10 天过长! 不予批准

这个例子比较简单,职责的链条只有 ProjectLeaderGroupLeader 这2个对象,而且对处理请假的请求使用 模板方法模式 的思路,子类只需要实现,有权限时的函数和没有权限时2个函数即可

有一点可以再提一下:

举的例子中 项目组长 能完成审批就自己承担起职责, 不能处理则把职责推给下一级 部门组长
而有时还有另一种职责链, 自己处理一部分职责, 把剩下一部分职责传给下级, 而这种职责链模式有点神似 建造者模式 模式中的 Director(指挥者), 定义一个协作处理的流程

注意事项

职责链是动态的, 或者说客户端手动创建的, 并且一个请求没有明确的接收者,那么就不能保证它一定会被处理, 该请求可能一直到链的末端都得不到处理。

简单点说就是: 一个请求也可能因该链没有被正确配置而得不到处理

总结

职责链模式将职责的处理者独立出来, 外加通过 链条 的形式, 可以让这些处理者很灵活的串联起来, 动态的增加或修改或改变, 但是链条不能保证请求一定被处理~