目录
一.专栏简介
本专栏是我学习《head first》设计模式的笔记。这本书中是用Java语言为基础的,我将用C++语言重写一遍,并且详细讲述其中的设计模式,涉及是什么,为什么,怎么做,自己的心得等等。希望阅读者在读完我的这个专题后,也能在开发中灵活且正确的使用,或者在面对面试官时,能够自信地说自己熟悉常用设计模式。
本章将开始**责任链模式(Chain of Responsibility Pattern)**的学习。
二.责任链模式概念
通过责任链模式,你可以创建一个对象链来检查请求。每个对象依次检查请求,处理它或者将它传给链中的下一个对象。
当你要把处理请求的机会给予多于一个的对象时,使用责任链模式。
模式结构:
-
Handler(抽象处理者):定义处理请求的接口,包含指向后继处理者的引用。
-
ConcreteHandler(具体处理者):实现处理接口,判断自身能否处理请求,能则处理,不能则转发给后继者。
-
Client(客户端):创建责任链,并发起请求。
下面通过在公司中请假审批的流程为例子来说明。
三.请假审批案例和代码
以 "请假审批流程" 为例(符合真实业务场景,更易理解):
-
请假 1 天及以内:小组长审批
-
请假 2-3 天:部门经理审批
-
请假 4 天及以上:总经理审批
我们首先用LeaveRequest类封装请假请求 ,其中包含请假人和请假天数以及相应的get方法;然后是一个抽象类Approver表示审批者的基类 ,包含审批者和指向后继审批者的指针,也就是审批者们是一个单链表的数据结构,并提供设置下一个审批者和审批请假的函数,后者是纯虚函数。然后是具体处理者们 ,分别是小组长,部门经理和总经理,它们继承抽象类Approver,然后重写实现approve()方法,这个方法中判断这个请假条是否自己处理,如果不是则传给单链表的下一个审批者,如果走到单链表的末尾则表示请假失败。
代码如下:
chainOfResponsibilityPattern.h:
cpp
#pragma once
#include <iostream>
#include <string>
using namespace std;
// 请假请求(封装请求数据)
class LeaveRequest
{
public:
LeaveRequest(string name, int days) : m_name(name), m_days(days)
{}
string getName() const { return m_name; }
int getDays() const { return m_days; }
private:
string m_name; // 请假人
int m_days; // 请假天数
};
// 1. 抽象处理者:审批者
class Approver
{
public:
Approver(string name) : m_name(name)
{}
// 设置后继审批者(构建责任链)
void setNextApprover(Approver* next) { m_nextApprover = next; }
// 抽象审批方法(核心)
virtual void approve(LeaveRequest* request) = 0;
virtual ~Approver() = default;
protected:
string m_name; // 审批者名称
Approver* m_nextApprover = nullptr; // 后继审批者
};
// 2. 具体处理者1:小组长(处理1天及以内请假)
class GroupLeader : public Approver
{
public:
GroupLeader(string name) : Approver(name)
{}
void approve(LeaveRequest* request) override
{
if (request->getDays() <= 1)
{
// 自身能处理,直接审批
cout << "小组长" << m_name << "审批:"
<< request->getName() << "请假" << request->getDays() << "天,批准!" << endl;
}
else if (m_nextApprover != nullptr)
{
// 自身不能处理,转发给后继者
cout << "小组长" << m_name << ":请假" << request->getDays()
<< "天,超出权限,转交给部门经理审批..." << endl;
m_nextApprover->approve(request);
}
else
{
// 无后继者,审批失败
cout << "小组长" << m_name << ":无更高审批者,请假失败!" << endl;
}
}
};
// 2. 具体处理者2:部门经理(处理2-3天请假)
class DepartmentManager : public Approver
{
public:
DepartmentManager(string name) : Approver(name)
{}
void approve(LeaveRequest* request) override
{
if (request->getDays() >= 2 && request->getDays() <= 3)
{
cout << "部门经理" << m_name << "审批:"
<< request->getName() << "请假" << request->getDays() << "天,批准!" << endl;
}
else if (m_nextApprover != nullptr)
{
cout << "部门经理" << m_name << ":请假" << request->getDays()
<< "天,超出权限,转交给总经理审批..." << endl;
m_nextApprover->approve(request);
}
else
{
cout << "部门经理" << m_name << ":无更高审批者,请假失败!" << endl;
}
}
};
// 2. 具体处理者3:总经理(处理4天及以上请假)
class GeneralManager : public Approver
{
public:
GeneralManager(string name) : Approver(name)
{}
void approve(LeaveRequest* request) override
{
if (request->getDays() >= 4)
{
cout << "总经理" << m_name << "审批:"
<< request->getName() << "请假" << request->getDays() << "天,批准!" << endl;
}
else
{
// 总经理是最终审批者,无后继者
cout << "总经理" << m_name << ":请假天数不符合权限范围,审批失败!" << endl;
}
}
};
UML类图:

main.cpp:(客户端代码)
cpp
#include "chainOfResponsibilityPattern.h"
// 客户端代码
int main()
{
// 1. 创建各审批者
Approver* groupLeader = new GroupLeader("张三");
Approver* deptManager = new DepartmentManager("李四");
Approver* genManager = new GeneralManager("王五");
// 2. 构建责任链:小组长 → 部门经理 → 总经理
groupLeader->setNextApprover(deptManager);
deptManager->setNextApprover(genManager);
// 3. 发起不同的请假请求
LeaveRequest* req1 = new LeaveRequest("小明", 1); // 小组长处理
LeaveRequest* req2 = new LeaveRequest("小红", 3); // 部门经理处理
LeaveRequest* req3 = new LeaveRequest("小刚", 5); // 总经理处理
// 4. 从链头(小组长)提交请求
groupLeader->approve(req1);
cout << "------------------------" << endl;
groupLeader->approve(req2);
cout << "------------------------" << endl;
groupLeader->approve(req3);
// 释放资源
delete req1;
delete req2;
delete req3;
delete groupLeader;
delete deptManager;
delete genManager;
return 0;
}
运行结果:

可以看到,请假条被正确的审批处理。
四.责任链的优点
-
解耦请求的发送者和接收者。
-
允许你通过改变链的成员或次序,动态地添加或移除责任。
-
单一职责原则:每个审批者只处理自己权限内的逻辑,无需关心其他流程,同时避免多层
if-else,代码可读性和可维护性提升。; -
开闭原则:新增审批级别(比如总监审批)时,只需新增一个具体处理者类,无需修改原有代码。
五.责任链的用途和缺点
-
经常用在窗体系统中,处理鼠标点击和键盘事件。Qt底层的鼠标事件和键盘事件也是这样的。
-
并不保证请求一定会被执行,如果没有对象处理它,可能会掉到链的末尾(这可以是优点或缺点)。
-
运行时可能会难以观察和调试。
-
性能开销:如果责任链过长,请求需要遍历整个链路,会有一定的性能损耗;
-
循环引用风险:如果责任链构建错误出现环(比如总经理的后继又指回了小组长),会导致请求死循环。