if-else 嵌套地狱”:责任链设计模式如何帮你理清复杂流程?

责任链模式

我们在工作中,经常会遇到这种情况。对于不同的身份的人会有不同的处理。比如请假,学生请假向班主任提出申请,老师请假需要向年级组长申请,教导主任请假需要向校长申请。不同的身份,不同的处置流程。其实代码倒是没什么,用简单的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);
            }
        }
    }
}

结果如下图:

其实这个也实现了上述的要求,耦合度也降低了。当前实现的主要缺点:

  1. 违反单一职责原则

客户端代码(HospitalSystem)承担了过多的职责,包括:

  • 患者分类判断
  • 诊疗部门分配
  • 流程控制

问题本质

  • 决策逻辑泄露(Feature Envy):本应由各科室决定的接诊标准,被提取到Client类中

  • 违反信息专家模式:科室自身最清楚能处理什么病情的患者,但判断逻辑却被放在外部

    1. 违反开闭原则

    当需要新增诊疗部门时:

    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方法

耦合带来的维护成本

  • 变更影响分析困难

  • 回归测试范围扩大

  • 团队协作容易冲突

    ini 复制代码
    IMedicalHandler generalClinic = new GeneralClinic();
    IMedicalHandler specialistClinic = new SpecialistClinic();
    // ...其他部门
    1. 流程僵化

    无法动态调整诊疗流程,例如疫情期间需要:

    1. 所有患者先经过发热筛查

    2. 夜间模式跳过普通门诊

    3. 违反迪米特法则

    客户端需要了解所有诊疗部门的细节和判断逻辑。上述的都不太重要,主要是这个。

使用责任链模式

设计流程

病人

病人的模型还不变。

处理流程

我们希望的是可以设计,一个每个责任方可以承担自己的处理逻辑,不用管其他部分。急诊只管理急诊的处理流程,专科门诊只管理专科门诊的事情。因此处理流程得变。让每个处理的部分只处理自己的部分。同时每个模块之间的内部的处理逻辑相互不知道,不对外暴露。减少耦合性。同时还可以随意的动态的调整责任链流程。

每个部分只处理相关的部分,并且可以动态调整流程。

因此,需要抽象出来,每个部门的门诊,根据病人的级别,自己判断是否能够处理,让其子类实现。同时还要实现,不能处理传递到下一个节点。这里类似链表,可以采用链表的结构。

抽象诊处理器

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);
        }
    }
}

这里你也会说,创建责任链也耦合了,这个后续其实可以进行设置成配置类。

相关推荐
IguoChan1 小时前
9. Redis Operator (2) —— Sentinel部署
后端
ansurfen2 小时前
耗时一周,我的编程语言 Hulo 新增 Bash 转译和包管理工具
后端·编程语言
库森学长2 小时前
索引失效的场景有哪些?
后端·mysql·面试
半夏知半秋2 小时前
CentOS7下的ElasticSearch部署
大数据·服务器·后端·学习·elasticsearch·搜索引擎·全文检索
种子q_q3 小时前
面试官:什么是Spring的三级缓存机制
后端·面试
朱雨鹏3 小时前
同步队列阻塞器AQS的执行流程,案例图
java·后端
用户1512905452203 小时前
Python——Html(表格, , ,、表单 、自定义标签 和)
后端
ezreal_pan3 小时前
巧用 Golang 函数特性实现单元测试中的数据库操作 Mock
开发语言·后端·golang·单元测试·函数
超浪的晨3 小时前
Java Map 集合详解:从基础语法到实战应用,彻底掌握键值对数据结构
java·开发语言·后端·学习·个人开发
Jooolin3 小时前
【C++】deque的设计思想
c++·后端·ai编程