C++设计模式结构型模式———责任链模式
文章目录
- 一、引言
- 二、责任链模式
- 三、总结
一、引言
责任链模式是一种行为设计模式, 允许将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。
也就是说,将一个请求传递给一个链中的若干对象,哪个对象适合处理这个请求就让哪个对象来处理。
二、责任链模式
举一个例子来解释责任链模式,一个公司的员工要请求加薪,如果员工要求的加薪不超过1000(要求的加薪≤1000)元,则部门经理就可以批准;如果员工要求的加薪在1000元之上但不超过5000(1000<要求的加薪≤5000)元,则技术总监才能批准;如果员工要求的加薪超过5000(要求的加薪>5000)元,则只有总经理才能批准。
如果按照传统做法,可以创建一个薪水处理类SalaryHandler
,在其中可以书写一个raiseRequest
方法来处理加薪请求,代码如下:
class SalaryHandler
{
public:
//处理加薪请求
void raiseRequest(const string& name, int salfigure)
//参数1代表要加薪的员工名字,参数2代表求要加薪多少
{
if (salfigure <= 1000)
{
depManagerSP(name, salfigure);
}
else if (salfigure <= 5000)
{
CTosP(name, salfigure);
}
else
{
genManagerSP(name, salfigure);
}
}
private:
//部门经理审批加薪请求
void depManagerSP(const string& sname, int salfigure)
{
cout << sname << "的加薪要求为:" << salfigure << "元,部门经理审批通过" << endl;
}
//技术总监审批加薪请求
void CTosP(const string& sname, int salfigure)
{
cout << sname << "的加薪要求为:" << salfigure << "元,技术总监审批通过!" << endl;
}
//总经理审批加薪请求
void genManagerSP(const string& sname, int salfigure)
{
cout << sname << "的加薪要求为:" << salfigure << "元,总经理审批通过!" << endl;
}
};
这样就可以根据员工加薪的多少把请求发送给上司。但是显然,这段代码不够灵活。如果将来新增一个副总经理,那么就要修改raiseRequest
成员方法来新增加新的if
判断分支,这显然不符合开闭原则。
如果分别为部门经理、技术总监、总经理创建类并且都继承自同一个父类,然后像链表一样把部门经理、技术总监、总经理(都看成对象)连起来构成一个责任链,当出现一个员工加薪请求(也看成对象)时,将这个请求沿着这个链传递,部门经理能处理的就处理,不能处理的传给技术总监;技术总监能处理的就处理,不能处理的继续传给总经理。总之,链上的对象(部门经理、技术总监、总经理)都有处理到请求的机会。如果将来增加一个副总经理,同样可以为副总经理创建一个子类,把副总经理链到原来的部门经理、技术总监、总经理链上,当出现一个员工加薪请求时,只要适合副总经理审批的就可以让副总经理审批。
可能会有疑问:如果责任链中副总经理被链到了总经理之后,那么整个责任链就应该是“部门经理→技术总监→总经理→副总经理”,是否会出现应该副总经理审批的加薪请求却被总经理审批了呢?当然不会,只要定好规则,例如总经理审批的薪水必须是18000元之上的,比如要求加薪15000元,不满足大于18000元的条件,那么最终肯定还是会被副总经理审批的。
基于上述的责任链思路,改造一下范例,这里先把加薪请求做成一个类,因为类可以承载更多的信息,也更容易进行请求的扩充,例如,现在是加薪请求,将来可以扩充增加离职、部门调动等请求:
//加薪请求类
class RaiseRequest {
public:
RaiseRequest(const string& name, int salfigure)
: sname(name), m_isalfigure(salfigure) {}
const string& getName() const { return sname; }
int getSalFigure() const { return m_isalfigure; }
private:
string sname;
int m_isalfigure;
};
//薪水审批者父类
class ParSalApprover {
public:
ParSalApprover() : m_nextChain(nullptr) {}
virtual ~ParSalApprover() {} // 虚析构函数
// 设置指向的责任链中的下一个审批者
void setNextChain(shared_ptr<ParSalApprover> next) {
m_nextChain = next;
}
// 处理加薪请求
virtual void processRequest(const RaiseRequest& req) = 0;
protected:
// 找链中的下一个对象并把请求投递给下一个链中的对象
void sendRequestToNextHandler(const RaiseRequest& req) {
// 找链中的下一个对象
if (m_nextChain) {
// 把请求投递给链中的下一个对象
m_nextChain->processRequest(req);
}
else {
// 没找到链中的下一个对象
cout << req.getName() << "的加薪要求为:" << req.getSalFigure() << "元,但无人能够审批!" << endl;
}
}
private:
shared_ptr<ParSalApprover> m_nextChain;
};
// 部门经理子类
class depManager_SA : public ParSalApprover {
public:
void processRequest(const RaiseRequest& req) override {
int salfigure = req.getSalFigure();
if (salfigure <= 1000) {
// 如果自己能处理,则自己处理
cout << req.getName() << "的加薪要求为:" << salfigure << "元,部门经理审批通过!" << endl;
}
else {
// 自己不能处理,尝试找链中的下一个对象来处理
sendRequestToNextHandler(req);
}
}
};
// 技术总监子类
class CTO_SA: public ParSalApprover {
public:
void processRequest(const RaiseRequest& req) override {
int salfigure = req.getSalFigure();
if (salfigure <= 5000) {
// 技术总监可以处理的请求
cout << req.getName() << "的加薪要求为:" << salfigure << "元,技术总监审批通过!" << endl;
}
else {
// 尝试找链中的下一个对象来处理
sendRequestToNextHandler(req);
}
}
};
// 总经理子类
class genManager_SA : public ParSalApprover {
public:
void processRequest(const RaiseRequest& req) override {
int salfigure = req.getSalFigure();
// 总经理可以处理所有请求
cout << req.getName() << "的加薪要求为:" << salfigure << "元,总经理审批通过!" << endl;
}
};
使用如下代码测试:
// 创建审批者链
shared_ptr<ParSalApprover> depManager = make_shared<depManager_SA>();
shared_ptr<ParSalApprover> techDirector = make_shared<CTO_SA>();
shared_ptr<ParSalApprover> generalManager = make_shared<genManager_SA>();
// 设置责任链
depManager->setNextChain(techDirector);
techDirector->setNextChain(generalManager);
// 测试请求
RaiseRequest req1("张三", 800);
RaiseRequest req2("李四", 3000);
RaiseRequest req3("王五", 6000);
depManager->processRequest(req1);
depManager->processRequest(req2);
depManager->processRequest(req3);
/*
张三的加薪要求为:800元,部门经理审批通过!
李四的加薪要求为:3000元,技术总监审批通过!
王五的加薪要求为:6000元,总经理审批通过!
*/
从上述代码可以看到,这3个分别代表部门经理、技术总监、总经理的对象链到一起构成责任链,其实这3个对象谁在链的开头,谁在链的中间或者结尾都行,后续针对张三(包括李四或王五)的加薪请求,从责任链的开始位置遍历责任链中的每个对象,如果该对象能处理该加薪请求就处理,处理不了沿着责任链寻找下一个对象并判断下一个对象能否处理,一直寻找到责任链中最后一个对象。如果责任链中的所有对象全都处理不了这个加薪请求,那么会提示这个加薪无人能够审批。当然这个范例中不存在加薪无人审批的情形,因为要求加薪的数目再大,总经理总是可以审批的。
- 处理者(Handler):定义了用于处理请求的接口,也记录了下一个处理者是谁,此处指
ParSalApprover
类。 - 具体处理者(ConcreteHandler):用于实现针对具体请求的处理,如果自身无法处理请求,则会把请求传递给下一个处理者(后继者)。这里指
depManager_SA
、CTO_SA
、genManager_SA
类。 - 请求者/客户端(Client):用于向责任链上的具体处理者对象提交处理请求。这里指
main
主函数中的代码。其实,在上述范例中,main
主函数中的代码还承担了创建责任链对象并搭建责任链(也可以在Handler
中搭建责任链)的角色。一般来说,责任链模式并不创建责任链,而是由使用责任链的请求者(客户端)来创建。
责任链模式结构
引入责任链的定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链(构成对象链),并沿着这条链传递该请求,直到有一个对象处理它为止。
在上述范例中,“使多个对象都有机会处理请求”中的多个对象,当然是指部门经理、技术总监、总经理(实例化的对象),这些对象也作为请求的接收者被连成了一条链(责任链),而请求指的是请求加薪,这些请求沿着责任链传递,一直到被责任链中的某个对象审批为止。
三、总结
责链模式应用的一个请求可能有多个接收者(扮演处理者角色),但最后只有一个接收者会处理该请求。此时,请求的发送者和接收者之间是解耦的。换句话说,请求的发送者只需要将请求发送到链上,并不关心请求的处理细节以及请求的传递,也并不知道最终会被哪个接收者处理,这种灵活性可以更好地应对变化。
责任链一般是一条直线,责任链上的每个接收者仅需要保存一个指向其后继者的指针(不需要保存其他所有接收者的指针),当然,读者可能也会见到环形或者树形结构的责任链(非直线的责任链建链时要小心,不要造成循环链导致请求传递陷人死循环,从而使整个程序的执行陷入死锁)。同时,可以在程序运行期间动态地添加、修改或删除责任链上的接收者对象,使针对请求的处理更具灵活性,这是责任链模式的重要特色。
而且增加新的处理者不需要修改原有的代码,只需要增加新的具体处理者子类并在客户端重新建链即可,符合开闭原则。如果请求传递到责任链的末尾仍然没有得到处理,则应该有一个合理的默认处理方式(书写一个终极处理器并始终将该处理器放在职责链的末尾)或者给出提示信息,这属于接收者的责任。
- 责任链模式、 命令模式、 中介者模式和观察者模式用于处理请求发送者和接收者之间的不同连接方式:
- 责任链按照顺序将请求动态传递给一系列的潜在接收者, 直至其中一名接收者对请求进行处理。
- 命令在发送者和请求者之间建立单向连接。
- 中介者清除了发送者和请求者之间的直接连接, 强制它们通过一个中介对象进行间接沟通。
- 观察者允许接收者动态地订阅或取消接收请求。
息,这属于接收者的责任。
- 责任链模式、 命令模式、 中介者模式和观察者模式用于处理请求发送者和接收者之间的不同连接方式:
- 责任链按照顺序将请求动态传递给一系列的潜在接收者, 直至其中一名接收者对请求进行处理。
- 命令在发送者和请求者之间建立单向连接。
- 中介者清除了发送者和请求者之间的直接连接, 强制它们通过一个中介对象进行间接沟通。
- 观察者允许接收者动态地订阅或取消接收请求。
责任链通常可以和组合模式结合使用。 在这种情况下, 叶组件接收到请求后, 可以将请求沿包含全体父组件的链一直传递至对象树的底部。责任链和装饰模式的类结构非常相似。 两者都依赖递归组合将需要执行的操作传递给一系列对象。 但是, 两者有几点重要的不同之处。责任链的管理者可以相互独立地执行一切操作, 还可以随时停止传递请求。 另一方面, 各种装饰可以在遵循基本接口的情况下扩展对象的行为。 此外, 装饰无法中断请求的传递。