if-else 嵌套地獄”:責任鏈設計模式如何幫你理清復雜流程?
責任鏈模式
我們在工作中,經常會遇到這種情況。對于不同的身份的人會有不同的處理。比如請假,學生請假向班主任提出申請,老師請假需要向年級組長申請,教導主任請假需要向校長申請。不同的身份,不同的處置流程。其實代碼倒是沒什么,用簡單的if-else就能實現。但是今天并不是要強調這個,而是用一種新的設計模式,責任鏈。好,我們首先用從最簡單的if-else開始,一開始不要求多好。能實現就行。我們這次用的案例是醫院的案例。醫院根據患者的病情進行分級診斷。
不使用責任鏈模型

設計流程
這樣我們怎么進行實現呢?為了進行防著耦合性過高,先設置一個病人的抽象類,抽象類有對應的病情等級,和病情描述。讓子類病人進行實現。增加擴展性。再到主類進行判斷病情進行調用。因為診療也是需要根據病人的情況,需要設計出對應的診療方案的。因此,先設計一個抽象的診療類,不同的診療方案具體實現接口。
病人抽象接口
public interface IPatient {
// 獲取病情嚴重程度級別 (1-普通 2-中等 3-嚴重 4-危急)
int getSeverityLevel();
// 獲取患者癥狀描述
String getSymptoms();
}
病人實體類
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;
}
}
診療方案接口
public interface IMedicalHandler {
void handleRequest(IPatient patient);
}
普通門診實現
public class GeneralClinic implements IMedicalHandler {
@Override
public void handleRequest(IPatient patient) {
System.out.println("普通門診處理患者: " + patient.getSymptoms());
System.out.println("處理結果: 開常規藥物,建議休息");
}
}
專科門診實現
public class SpecialistClinic implements IMedicalHandler {
@Override
public void handleRequest(IPatient patient) {
System.out.println("??崎T診處理患者: " + patient.getSymptoms());
System.out.println("處理結果: 進行專科檢查,開??扑幬?);
}
}
急診實現
public class EmergencyRoom implements IMedicalHandler {
@Override
public void handleRequest(IPatient patient) {
System.out.println("急診科處理患者: " + patient.getSymptoms());
System.out.println("處理結果: 立即進行急救處理");
}
}
ICU實現
public class ICU implements IMedicalHandler {
@Override
public void handleRequest(IPatient patient) {
System.out.println("ICU處理患者: " + patient.getSymptoms());
System.out.println("處理結果: 轉入重癥監護,進行高級生命支持");
}
}
客戶端實現
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("分配到: ??崎T診");
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類中
-
違反信息專家模式:科室自身最清楚能處理什么病情的患者,但判斷邏輯卻被放在外部
- 違反開閉原則
當需要新增診療部門時:
// 必須修改客戶端代碼 else if (patient.getSeverityLevel() == 5) { // 新增條件 System.out.println("分配到: 發熱門診"); feverClinic.handleRequest(patient); }
這一條我感覺還行,主要是違反了上一條,這條如果是比較復雜的邏輯,會影響到。
3. **高耦合**
耦合過重的具體危害
擴展場景示例: 當需要新增"亞急診科"(處理level=2.5的患者)時:
| 操作步驟 | 傳統實現需要修改的地方 | 責任鏈模式需要修改的地方 |
|---|---|---|
| 1. 新增處理器類 | 修改HospitalSystem的條件分支 | 僅新增SubEmergencyHandler類 |
| 2. 調整處理順序 | 重構所有if-else邏輯 | 調整setNext調用順序 |
| 3. 修改級別判斷標準 | 需要同步修改Client和具體科室類 | 僅修改對應科室的canHandle方法 |
耦合帶來的維護成本:
-
變更影響分析困難
-
回歸測試范圍擴大
-
團隊協作容易沖突
IMedicalHandler generalClinic = new GeneralClinic(); IMedicalHandler specialistClinic = new SpecialistClinic(); // ...其他部門- 流程僵化
無法動態調整診療流程,例如疫情期間需要:
-
所有患者先經過發熱篩查
-
夜間模式跳過普通門診
-
違反迪米特法則
客戶端需要了解所有診療部門的細節和判斷邏輯。上述的都不太重要,主要是這個。
使用責任鏈模式
設計流程
病人
病人的模型還不變。
處理流程
我們希望的是可以設計,一個每個責任方可以承擔自己的處理邏輯,不用管其他部分。急診只管理急診的處理流程,專科門診只管理??崎T診的事情。因此處理流程得變。讓每個處理的部分只處理自己的部分。同時每個模塊之間的內部的處理邏輯相互不知道,不對外暴露。減少耦合性。同時還可以隨意的動態的調整責任鏈流程。
每個部分只處理相關的部分,并且可以動態調整流程。

因此,需要抽象出來,每個部門的門診,根據病人的級別,自己判斷是否能夠處理,讓其子類實現。同時還要實現,不能處理傳遞到下一個節點。這里類似鏈表,可以采用鏈表的結構。
抽象診處理器
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);
}
一開始我是難以理解這個是怎么設計責任鏈。
// 設置下一個處理器
public void setNext(MedicalHandler handler) {
this.nextHandler = handler;
}
下一個節點.setNext(上一個節點);
這里的
遍歷責任鏈是this.nextHandler.handleRequest,是遞歸到下一個節點。直到為空,最后一個節點的next為空。
具體實現
//普通門診
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("??崎T診處理患者: " + patient.getSymptoms());
System.out.println("處理結果: 進行??茩z查,開??扑幬颸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("??崎T診處理患者: " + patient.getSymptoms());
System.out.println("處理結果: 進行專科檢查,開專科藥物\n");
}
}
一開始我還是很困惑,為啥要用super,這里很巧妙,這樣初始化,就完全依靠抽象的父類。不在依靠具體的實現類,實現了解耦。
責任鏈
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);
}
}
}
這里你也會說,創建責任鏈也耦合了,這個后續其實可以進行設置成配置類。


浙公網安備 33010602011771號