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

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

相关推荐
rannn_1111 小时前
【Javaweb学习|黑马笔记|Day1】初识,入门网页,HTML-CSS|常见的标签和样式|标题排版和样式、正文排版和样式
css·后端·学习·html·javaweb
柏油1 小时前
Spring @Cacheable 解读
redis·后端·spring
柏油2 小时前
Spring @TransactionalEventListener 解读
spring boot·后端·spring
两码事3 小时前
告别繁琐的飞书表格API调用,让飞书表格操作像操作Java对象一样简单!
java·后端
shark_chili4 小时前
面试官再问synchronized底层原理,这样回答让他眼前一亮!
后端
灵魂猎手4 小时前
2. MyBatis 参数处理机制:从 execute 方法到参数流转全解析
java·后端·源码
易元4 小时前
模式组合应用-桥接模式(一)
后端·设计模式
柑木4 小时前
隐私计算-SecretFlow/SCQL-SCQL的两种部署模式
后端·安全·数据分析
灵魂猎手4 小时前
1. Mybatis Mapper动态代理创建&实现
java·后端·源码
泉城老铁4 小时前
在秒杀场景中,如何通过动态调整线程池参数来应对流量突增
后端·架构