责任链模式
我们在工作中,经常会遇到这种情况。对于不同的身份的人会有不同的处理。比如请假,学生请假向班主任提出申请,老师请假需要向年级组长申请,教导主任请假需要向校长申请。不同的身份,不同的处置流程。其实代码倒是没什么,用简单的if-else就能实现。但是今天并不是要强调这个,而是用一种新的设计模式,责任链。好,我们首先用从最简单的if-else开始,一开始不要求多好。能实现就行。我们这次用的案例是医院的案例。医院根据患者的病情进行分级诊断。
不使用责任链模型

设计流程
这样我们怎么进行实现呢?为了进行防着耦合性过高,先设置一个病人的抽象类,抽象类有对应的病情等级,和病情描述。让子类病人进行实现。增加扩展性。再到主类进行判断病情进行调用。因为诊疗也是需要根据病人的情况,需要设计出对应的诊疗方案的。因此,先设计一个抽象的诊疗类,不同的诊疗方案具体实现接口。
病人抽象接口
java
public interface IPatient {
// 获取病情严重程度级别 (1-普通 2-中等 3-严重 4-危急)
int getSeverityLevel();
// 获取患者症状描述
String getSymptoms();
}
病人实体类
java
public class Patient implements IPatient {
/**
* 病情严重程度级别
*/
private int severityLevel;
/**
* 症状描述
*/
private String symptoms;
public Patient(int level, String symptoms) {
this.severityLevel = level;
this.symptoms = symptoms;
}
// 获取病情严重程度级别 (1-普通 2-中等 3-严重 4-危急)
@Override
public int getSeverityLevel() {
return this.severityLevel;
}
//获取患者症状描述
@Override
public String getSymptoms() {
return this.symptoms;
}
}
诊疗方案接口
java
public interface IMedicalHandler {
void handleRequest(IPatient patient);
}
普通门诊实现
java
public class GeneralClinic implements IMedicalHandler {
@Override
public void handleRequest(IPatient patient) {
System.out.println("普通门诊处理患者: " + patient.getSymptoms());
System.out.println("处理结果: 开常规药物,建议休息");
}
}
专科门诊实现
java
public class SpecialistClinic implements IMedicalHandler {
@Override
public void handleRequest(IPatient patient) {
System.out.println("专科门诊处理患者: " + patient.getSymptoms());
System.out.println("处理结果: 进行专科检查,开专科药物");
}
}
急诊实现
java
public class EmergencyRoom implements IMedicalHandler {
@Override
public void handleRequest(IPatient patient) {
System.out.println("急诊科处理患者: " + patient.getSymptoms());
System.out.println("处理结果: 立即进行急救处理");
}
}
ICU实现
java
public class ICU implements IMedicalHandler {
@Override
public void handleRequest(IPatient patient) {
System.out.println("ICU处理患者: " + patient.getSymptoms());
System.out.println("处理结果: 转入重症监护,进行高级生命支持");
}
}
客户端实现
java
public class HospitalSystem {
public static void main(String[] args) {
// 创建不同严重程度的患者
IPatient[] patients = {
new Patient(1, "轻微感冒症状"),
new Patient(2, "持续头痛一周"),
new Patient(3, "严重胸痛"),
new Patient(4, "心脏骤停"),
new Patient(2, "高烧不退")
};
// 创建各诊疗部门
IMedicalHandler generalClinic = new GeneralClinic();
IMedicalHandler specialistClinic = new SpecialistClinic();
IMedicalHandler emergencyRoom = new EmergencyRoom();
IMedicalHandler icu = new ICU();
// 处理每个患者
for (IPatient patient : patients) {
System.out.println("\n===== 新患者就诊 =====");
System.out.println("症状: " + patient.getSymptoms());
System.out.println("严重程度: " + patient.getSeverityLevel());
if (patient.getSeverityLevel() == 1) {
System.out.println("分配到: 普通门诊");
generalClinic.handleRequest(patient);
} else if (patient.getSeverityLevel() == 2) {
System.out.println("分配到: 专科门诊");
specialistClinic.handleRequest(patient);
} else if (patient.getSeverityLevel() == 3) {
System.out.println("分配到: 急诊科");
emergencyRoom.handleRequest(patient);
} else if (patient.getSeverityLevel() == 4) {
System.out.println("分配到: ICU");
icu.handleRequest(patient);
} else {
System.out.println("无法识别的病情级别,转到急诊科");
emergencyRoom.handleRequest(patient);
}
}
}
}
结果如下图:

其实这个也实现了上述的要求,耦合度也降低了。当前实现的主要缺点:
- 违反单一职责原则
客户端代码(HospitalSystem
)承担了过多的职责,包括:
- 患者分类判断
- 诊疗部门分配
- 流程控制
问题本质:
-
决策逻辑泄露(Feature Envy):本应由各科室决定的接诊标准,被提取到Client类中
-
违反信息专家模式:科室自身最清楚能处理什么病情的患者,但判断逻辑却被放在外部
- 违反开闭原则
当需要新增诊疗部门时:
csharp// 必须修改客户端代码 else if (patient.getSeverityLevel() == 5) { // 新增条件 System.out.println("分配到: 发热门诊"); feverClinic.handleRequest(patient); }
这一条我感觉还行,主要是违反了上一条,这条如果是比较复杂的逻辑,会影响到。
markdown
3. **高耦合**
耦合过重的具体危害
扩展场景示例: 当需要新增"亚急诊科"(处理level=2.5的患者)时:
操作步骤 | 传统实现需要修改的地方 | 责任链模式需要修改的地方 |
---|---|---|
1. 新增处理器类 | 修改HospitalSystem的条件分支 | 仅新增SubEmergencyHandler 类 |
2. 调整处理顺序 | 重构所有if-else逻辑 | 调整setNext调用顺序 |
3. 修改级别判断标准 | 需要同步修改Client和具体科室类 | 仅修改对应科室的canHandle方法 |
耦合带来的维护成本:
-
变更影响分析困难
-
回归测试范围扩大
-
团队协作容易冲突
iniIMedicalHandler generalClinic = new GeneralClinic(); IMedicalHandler specialistClinic = new SpecialistClinic(); // ...其他部门
- 流程僵化
无法动态调整诊疗流程,例如疫情期间需要:
-
所有患者先经过发热筛查
-
夜间模式跳过普通门诊
-
违反迪米特法则
客户端需要了解所有诊疗部门的细节和判断逻辑。上述的都不太重要,主要是这个。
使用责任链模式
设计流程
病人
病人的模型还不变。
处理流程
我们希望的是可以设计,一个每个责任方可以承担自己的处理逻辑,不用管其他部分。急诊只管理急诊的处理流程,专科门诊只管理专科门诊的事情。因此处理流程得变。让每个处理的部分只处理自己的部分。同时每个模块之间的内部的处理逻辑相互不知道,不对外暴露。减少耦合性。同时还可以随意的动态的调整责任链流程。
每个部分只处理相关的部分,并且可以动态调整流程。

因此,需要抽象出来,每个部门的门诊,根据病人的级别,自己判断是否能够处理,让其子类实现。同时还要实现,不能处理传递到下一个节点。这里类似链表,可以采用链表的结构。
抽象诊处理器
java
public abstract class MedicalHandler {
// 能处理的级别
private int level;
// 责任链中的下一个处理器
private MedicalHandler nextHandler;
public MedicalHandler(int level) {
this.level = level;
}
// 设置下一个处理器
public void setNext(MedicalHandler handler) {
this.nextHandler = handler;
}
// 处理请求的模板方法
public final void handleRequest(IPatient patient) {
if (patient.getSeverityLevel() == this.level) {
this.response(patient);
} else {
if (this.nextHandler != null) {
this.nextHandler.handleRequest(patient);
} else {
System.out.println("没有匹配的诊疗部门可以处理该患者!");
System.out.println("患者症状: " + patient.getSymptoms());
System.out.println("严重程度: " + patient.getSeverityLevel());
}
}
}
// 抽象响应方法,由子类实现
protected abstract void response(IPatient patient);
}
一开始我是难以理解这个是怎么设计责任链。
typescript
// 设置下一个处理器
public void setNext(MedicalHandler handler) {
this.nextHandler = handler;
}
下一个节点.setNext(上一个节点);
这里的
遍历责任链是this.nextHandler.handleRequest,是递归到下一个节点。直到为空,最后一个节点的next为空。
具体实现
java
//普通门诊
public class GeneralClinic extends MedicalHandler {
public GeneralClinic() {
super(1); // 处理级别1的患者
}
@Override
protected void response(IPatient patient) {
System.out.println("普通门诊处理患者: " + patient.getSymptoms());
System.out.println("处理结果: 开常规药物,建议休息\n");
}
}
//专科
public class SpecialistClinic extends MedicalHandler {
public SpecialistClinic() {
super(2); // 处理级别2的患者
}
@Override
protected void response(IPatient patient) {
System.out.println("专科门诊处理患者: " + patient.getSymptoms());
System.out.println("处理结果: 进行专科检查,开专科药物\n");
}
}
//急诊
public class EmergencyRoom extends MedicalHandler {
public EmergencyRoom() {
super(3); // 处理级别3的患者
}
@Override
protected void response(IPatient patient) {
System.out.println("急诊科处理患者: " + patient.getSymptoms());
System.out.println("处理结果: 立即进行急救处理\n");
}
}
//ICU
public class SpecialistClinic extends MedicalHandler {
public SpecialistClinic() {
super(2); // 处理级别2的患者
}
@Override
protected void response(IPatient patient) {
System.out.println("专科门诊处理患者: " + patient.getSymptoms());
System.out.println("处理结果: 进行专科检查,开专科药物\n");
}
}
一开始我还是很困惑,为啥要用super,这里很巧妙,这样初始化,就完全依靠抽象的父类。不在依靠具体的实现类,实现了解耦。
责任链
java
public class HospitalSystem {
public static void main(String[] args) {
// 创建不同严重程度的患者
IPatient[] patients = {
new Patient(1, "轻微感冒症状"),
new Patient(2, "持续头痛一周"),
new Patient(3, "严重胸痛"),
new Patient(4, "心脏骤停"),
new Patient(2, "高烧不退"),
new Patient(5, "未知症状") // 测试异常情况
};
// 创建责任链
MedicalHandler generalClinic = new GeneralClinic();
MedicalHandler specialistClinic = new SpecialistClinic();
MedicalHandler emergencyRoom = new EmergencyRoom();
MedicalHandler icu = new ICU();
// 设置责任链顺序
generalClinic.setNext(specialistClinic);
specialistClinic.setNext(emergencyRoom);
emergencyRoom.setNext(icu);
// 处理每个患者
for (IPatient patient : patients) {
System.out.println("===== 处理新患者 =====");
System.out.println("症状: " + patient.getSymptoms());
System.out.println("严重程度: " + patient.getSeverityLevel());
// 从责任链的第一个处理器开始处理
generalClinic.handleRequest(patient);
}
}
}
这里你也会说,创建责任链也耦合了,这个后续其实可以进行设置成配置类。