Java设计模式之职责链模式介绍

目录

一、职责链模式

1.1 职责链模式定义

1.2 职责链模式原理

1.2.1 职责链模式类图

1.2.2 模式角色说明

1.2.3 示例代码

二、责链模式的应用

2.1 需求说明

2.2 流程示意图

2.3 不使用设计模式

2.4 职责链模式重构代码

2.4.1 结构示意图

2.4.2 代码示例

2.4.3 测试结果

三、职责链模式总结

3.1 职责链模式的优点

3.2 职责链模式的缺点

3.3 使用场景分析


一、职责链模式

1.1 职责链模式定义

职责链模式(chain of responsibility pattern) 定义: 避免将一个请求的发送者与接收者耦合在一起,让多个对象都有机会处理请求.将接收请求的对象连接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止。

在职责链模式中,多个处理器(也就是刚刚定义中说的“接收对象”)依次处理同一个请求。一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B处理器处理完后再 传递给 C 处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职 责,所以叫作职责链模式。

1.2 职责链模式原理

1.2.1 职责链模式类图

1.2.2 模式角色说明

职责链模式主要包含以下角色:

  • 抽象处理者(Handler)角色

定义一个处理请求的接口,包含抽象处理方法和一个后继连接(链上的每个处理者都有一个成员变量来保存对于下一处理者的引用,比如上图中的successor) 。

  • 具体处理者(Concrete Handler)角色

实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。

  • 客户类(Client)角色

创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

1.2.3 示例代码

代码实现:

package main.java.cn.test.chain.V1;

/**
 * @author ningzhaosheng
 * @date 2023/6/15 18:17:58
 * @description 请求数据
 */
public class RequestData {
    private String data;

    public RequestData(String data) {
        this.data = data;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }
}
package main.java.cn.test.chain.V1;

/**
 * @author ningzhaosheng
 * @date 2023/6/15 18:18:59
 * @description 抽象处理者
 */
public abstract class Handler {
    protected Handler successor = null;

    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }

    public abstract void handle(RequestData requestData);

}
package main.java.cn.test.chain.V1;

/**
 * @author ningzhaosheng
 * @date 2023/6/15 18:19:46
 * @description 具体处理者A
 */
public class HandlerA extends Handler {
    @Override
    public void handle(RequestData requestData) {
        System.out.println("HandlerA 执行代码逻辑! 处理: " +
                requestData.getData());
        requestData.setData(requestData.getData().replace("A", ""));
        if (successor != null) {
            successor.handle(requestData);
        } else {
            System.out.println("执行中止!");
        }
    }
}
package main.java.cn.test.chain.V1;

/**
 * @author ningzhaosheng
 * @date 2023/6/15 18:20:48
 * @description 具体处理者B
 */
public class HandlerB extends Handler {
    @Override
    public void handle(RequestData requestData) {
        System.out.println("HandlerB 执行代码逻辑! 处理: " +
                requestData.getData());
        requestData.setData(requestData.getData().replace("B", ""));
        if (successor != null) {
            successor.handle(requestData);
        } else {
            System.out.println("执行中止!");
        }

    }
}
package main.java.cn.test.chain.V1;

/**
 * @author ningzhaosheng
 * @date 2023/6/15 18:21:29
 * @description 具体处理者C
 */
public class HandlerC extends Handler {
    @Override
    public void handle(RequestData requestData) {
        System.out.println("HandlerC 执行代码逻辑! 处理: " +
                requestData.getData());
        requestData.setData(requestData.getData());
        if (successor != null) {
            successor.handle(requestData);
        } else {
            System.out.println("执行中止!");
        }
    }
}
package main.java.cn.test.chain.V1;

/**
 * @author ningzhaosheng
 * @date 2023/6/15 18:22:15
 * @description
 */
public class Client {
    public static void main(String[] args) {
        Handler h1 = new HandlerA();
        Handler h2 = new HandlerB();
        Handler h3 = new HandlerC();
        h1.setSuccessor(h2);
        h2.setSuccessor(h3);
        RequestData requestData = new RequestData("请求数据 ABCDE");
                h1.handle(requestData);
    }
}

二、责链模式的应用

2.1 需求说明

我们模拟有一个双11期间,业务系统审批的流程,临近双十一公司会有陆续有一些新的需求上线,为了保证线上系统的稳定,我们对上线的审批流畅做了严格的控制。审批的过程会有不同级别的负责人加入进行审批(平常系统上线只需三级负责人审批即可,双十一前后需要二级或一级审核人参与审批),接下来我们就使用职责链模式来设计一下此功能。

2.2 流程示意图

![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/2895/01686817476000/6b190ff9facd40a7b394ce7104715722.png)

![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/2895/01686817476000/86a2d4e1e1064432877b27c3e1ad53a4.png)

2.3 不使用设计模式

代码示例:

package main.java.cn.test.chain.V2;

/**
 * @author ningzhaosheng
 * @date 2023/6/15 18:34:51
 * @description 审核信息
 */
public class AuthInfo {
    private String code;
    private String info = "";

    public AuthInfo(String code, String... infos) {
        this.code = code;
        for (String str : infos) {
            info = this.info.concat(str + " ");
        }
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }

    @Override
    public String toString() {
        return "AuthInfo{" +
                "code='" + code + '\'' +
                ", info='" + info + '\'' +
                '}';
    }
}
package main.java.cn.test.chain.V2;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ningzhaosheng
 * @date 2023/6/15 18:36:41
 * @description 模拟审核服务
 */
public class AuthService {
    //审批信息 审批人Id+申请单Id
    private static Map<String, Date> authMap = new
            HashMap<String, Date>();

    /**
     * 审核流程
     *
     * @param uId     审核人id
     * @param orderId 审核单id
     */
    public static void auth(String uId, String orderId) {
        System.out.println("进入审批流程,审批人ID: " + uId);
        authMap.put(uId.concat(orderId), new Date());
    }

    //查询审核结果
    public static Date queryAuthInfo(String uId, String
            orderId) {
        return authMap.get(uId.concat(orderId)); //key=审核人id+审核单子id
    }
}
package main.java.cn.test.chain.V2;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author ningzhaosheng
 * @date 2023/6/15 18:38:05
 * @description
 */
public class AuthController {
    //审核接口
    public AuthInfo doAuth(String name, String orderId, Date authDate) throws ParseException {
        //三级审批
        Date date = null;
        //查询是否存在审核信息,查询条件: 审核人ID+订单ID,返回Map集合中的Date
        date = AuthService.queryAuthInfo("1000013", orderId);
        //如果为空,封装AuthInfo信息(待审核)返回
        if (date == null) {
            return new AuthInfo("0001", "单号: " + orderId, "状态: 等待三级审批负责人进行审批");
        }

        //二级审批
        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 时间格式化
        //二级审核人主要审核双十一之前, 11-01 ~ 11-10号的请求,所以要对传入的审核时间进行判断
        //审核时间 大于 2022-11-01 并且 小于 2022-11-10,Date1.after(Date2),当Date1大于Date2时,返回 TRUE,Date1.before(Date2),当Date1小于Date2时,返回TRUE
        if (authDate.after(f.parse("2022-11-01 00:00:00")) &&
                authDate.before(f.parse("2022-11-10 00:00:00"))) {
            //条件成立,查询二级审核的审核信息
            date =
                    AuthService.queryAuthInfo("1000012", orderId);
            //如果为空,还是待二级审核人审核状态
            if (date == null) {
                return new AuthInfo("0001", "单号: " + orderId, "状态: 等待二级审批负责人进行审批");
            }
        }

        //一级审批
        //审核范围是在11-11日 ~ 11-31日
        if (authDate.after(f.parse("2022-11-11 00:00:00")) &&
                authDate.before(f.parse("2022-11-31 00:00:00"))) {
            date = AuthService.queryAuthInfo("1000011", orderId);
            if (date == null) {
                return new AuthInfo("0001", "单号: " + orderId, "状态: 等待一级审批负责人进行审批");
            }
        }
        return new AuthInfo("0001", "单号: " + orderId, "申请人:" + name + ", 状态: 审批完成!");
    }
}
package main.java.cn.test.chain.V2;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author ningzhaosheng
 * @date 2023/6/15 18:43:39
 * @description
 */
public class Client {
    public static void main(String[] args) throws
            ParseException {
        AuthController controller = new AuthController();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = sdf.parse("2022-11-12 00:00:00");
        //设置申请流程
        //三级审核
        //1.调用doAuth方法,模拟发送申请人相关信息
        AuthInfo info1 = controller.doAuth("研发小周", "100001000010000", date);
        System.out.println("当前审核状态: " + info1.getInfo());
        /**
         * 2.模拟进行审核操作, 虚拟审核人ID: 1000013
         * 调用auth() 方法进行审核操作, 就是向Map中添加一个 审核人
         ID和申请单ID
         */
        AuthService.auth("1000013", "100001000010000");
        System.out.println("三级负责人审批完成,审批人: 王工");
        System.out.println("======================================== ===================================");

        //二级审核
        //1.调用doAuth方法,模拟发送申请人相关信息
        AuthInfo info2 = controller.doAuth("研发小周", "100001000010000", date);
        System.out.println("当前审核状态: " + info2.getInfo());
        /**
         * 2.模拟进行审核操作, 虚拟审核人ID: 1000012
         * 调用auth() 方法进行审核操作, 就是向Map中添加一个 审核人
         ID和申请单ID
         */
        AuthService.auth("1000012", "100001000010000");
        System.out.println("二级负责人审批完成,审批人: 张经理");
        System.out.println("======================================== ===================================");

        //一级审核
        //1.调用doAuth方法,模拟发送申请人相关信息
        AuthInfo info3 = controller.doAuth("研发小周", "100001000010000", date);
        System.out.println("当前审核状态: " + info3.getInfo());
        /**
         * 2.模拟进行审核操作, 虚拟审核人ID: 1000012
         * 调用auth() 方法进行审核操作, 就是向Map中添加一个 审核人
         ID和申请单ID
         */
        AuthService.auth("1000011", "100001000010000");
        System.out.println("一级负责人审批完成,审批人: 罗总");
    }
}

2.4 职责链模式重构代码

2.4.1 结构示意图

![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/2895/01686817476000/e8ffe840b96e4e73a8660cb80038daa5.png)

2.4.2 代码示例

package main.java.cn.test.chain.V3;

import main.java.cn.test.chain.V2.AuthInfo;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author ningzhaosheng
 * @date 2023/6/15 18:53:06
 * @description 抽象审核链类
 */
public abstract class AuthLink {

    protected SimpleDateFormat f = new
            SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    protected String levelUserId; //审核人ID
    protected String levelUserName; //审核人姓名
    protected AuthLink next; //持有下一个处理类的引用

    public AuthLink(String levelUserId, String levelUserName) {
        this.levelUserId = levelUserId;
        this.levelUserName = levelUserName;
    }

    //获取下一个处理类
    public AuthLink getNext() {
        return next;
    }

    //责任链中添加处理类
    public AuthLink appendNext(AuthLink next) {
        this.next = next;
        return this;
    }

    //抽象审核方法
    public abstract AuthInfo doAuth(String uId, String orderId, Date authDate);

}
package main.java.cn.test.chain.V3;

import main.java.cn.test.chain.V2.AuthInfo;
import main.java.cn.test.chain.V2.AuthService;

import java.text.ParseException;
import java.util.Date;

/**
 * @author ningzhaosheng
 * @date 2023/6/15 18:55:22
 * @description 一级负责人审批
 */
public class Level1AuthLink extends AuthLink {

    private Date beginDate = f.parse("2020-11-11 00:00:00");
    private Date endDate = f.parse("2020-11-31 23:59:59");

    public Level1AuthLink(String levelUserId, String
            levelUserName) throws ParseException {
        super(levelUserId, levelUserName);
    }


    @Override
    public AuthInfo doAuth(String uId, String orderId, Date authDate) {
        Date date = AuthService.queryAuthInfo(levelUserId,
                orderId);
        if (null == date) {
            return new AuthInfo("0001", "单号:", orderId, " 状态:待一级审批负责人 ", levelUserName);
        }
        AuthLink next = super.getNext();
        if (null == next) {
            return new AuthInfo("0000", "单号:", orderId, " 状态:一级审批完成", " 时间:", f.format(date), " 审批人:",
                    levelUserName);
        }
        if (authDate.before(beginDate) ||
                authDate.after(endDate)) {
            return new AuthInfo("0000", "单号:", orderId, " 状态:一级审批完成", " 时间:", f.format(date), " 审批人:",
                    levelUserName);
        }
        return next.doAuth(uId, orderId, authDate);
    }

}
package main.java.cn.test.chain.V3;

import main.java.cn.test.chain.V2.AuthInfo;
import main.java.cn.test.chain.V2.AuthService;

import java.text.ParseException;
import java.util.Date;

/**
 * @author ningzhaosheng
 * @date 2023/6/15 18:57:38
 * @description 二级负责人审批
 */
public class Level2AuthLink extends AuthLink {
    private Date beginDate = f.parse("2020-11-11 00:00:00");
    private Date endDate = f.parse("2020-11-31 23:59:59");

    public Level2AuthLink(String levelUserId, String
            levelUserName) throws ParseException {
        super(levelUserId, levelUserName);
    }

    @Override
    public AuthInfo doAuth(String uId, String orderId, Date authDate) {
        Date date = AuthService.queryAuthInfo(levelUserId,
                orderId);
        if (null == date) {
            return new AuthInfo("0001", "单号:", orderId, " 状态:待二级审批负责人 ", levelUserName);
        }
        AuthLink next = super.getNext();
        if (null == next) {
            return new AuthInfo("0000", "单号:", orderId, " 状态:二级审批完成", " 时间:", f.format(date), " 审批人:",
                    levelUserName);
        }
        if (authDate.before(beginDate) ||
                authDate.after(endDate)) {
            return new AuthInfo("0000", "单号:", orderId, " 状态:二级审批完成", " 时间:", f.format(date), " 审批人:",
                    levelUserName);
        }
        return next.doAuth(uId, orderId, authDate);
    }
}
package main.java.cn.test.chain.V3;

import main.java.cn.test.chain.V2.AuthInfo;
import main.java.cn.test.chain.V2.AuthService;

import java.util.Date;

/**
 * @author ningzhaosheng
 * @date 2023/6/15 18:59:06
 * @description 三级负责人审批
 */
public class Level3AuthLink extends AuthLink {

    public Level3AuthLink(String levelUserId, String
            levelUserName) {
        super(levelUserId, levelUserName);
    }

    @Override
    public AuthInfo doAuth(String uId, String orderId, Date authDate) {
        Date date = AuthService.queryAuthInfo(levelUserId,
                orderId);
        if (null == date) {
            return new AuthInfo("0001", "单号:", orderId, " 状态:待三级审批负责人 ", levelUserName);
        }
        AuthLink next = super.getNext();
        if (null == next) {
            return new AuthInfo("0000", "单号:", orderId, " 状态:三级审批完成", " 时间:", f.format(date), " 审批人:",
                    levelUserName);
        }
        return next.doAuth(uId, orderId, authDate);
    }
}
package main.java.cn.test.chain.V3;

import main.java.cn.test.chain.V2.AuthService;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author ningzhaosheng
 * @date 2023/6/15 19:00:22
 * @description
 */
public class Client {
    public static void main(String[] args) throws
            ParseException {
        AuthLink authLink = new Level3AuthLink("1000013", "王工")
                .appendNext(new Level2AuthLink("1000012", "张经理")
                        .appendNext(new Level1AuthLink("1000011", "段总")));

        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date currentDate = f.parse("2022-11-18 23:49:46");
        System.out.println("测试结果:" + authLink.doAuth("研发牛马", "1000998004813441", currentDate).toString());

        // 模拟三级负责人审批
        AuthService.auth("1000013", "1000998004813441");
        System.out.println("测试结果:" + "模拟三级负责人审批,王工");
        System.out.println("测试结果:" + authLink.doAuth("研发牛马", "1000998004813441", currentDate).toString());

        // 模拟二级负责人审批
        AuthService.auth("1000012", "1000998004813441");
        System.out.println("测试结果:" + "模拟二级负责人审批,张经理");
        System.out.println("测试结果:" + authLink.doAuth("研发牛马", "1000998004813441", currentDate).toString());

        // 模拟一级负责人审批
        AuthService.auth("1000011", "1000998004813441");
        System.out.println("测试结果:" + "模拟一级负责人审批,段总");
        System.out.println("测试结果:" + authLink.doAuth("研发牛马", "1000998004813441", currentDate).toString());
    }
}

2.4.3 测试结果

测试结果:AuthInfo{code='0001', info='单号: 1000998004813441  状态:待三级审批负责人  王工 '}
进入审批流程,审批人ID: 1000013
测试结果:模拟三级负责人审批,王工
测试结果:AuthInfo{code='0001', info='单号: 1000998004813441  状态:待二级审批负责人  张经理 '}
进入审批流程,审批人ID: 1000012
测试结果:模拟二级负责人审批,张经理
测试结果:AuthInfo{code='0000', info='单号: 1000998004813441  状态:二级审批完成  时间: 2023-06-15 19:07:11  审批人: 张经理 '}
进入审批流程,审批人ID: 1000011
测试结果:模拟一级负责人审批,段总
测试结果:AuthInfo{code='0000', info='单号: 1000998004813441  状态:二级审批完成  时间: 2023-06-15 19:07:11  审批人: 张经理 '}

从上面的代码结果看,我们的责任链已经生效,按照责任链的结构一层一层审批。当工作流程发生变化,可以动态地改变链内的成员或者修改它们的次序,也可动态地新增或者删除责任。并且每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

三、职责链模式总结

3.1 职责链模式的优点

  • 降低了对象之间的耦合度。该模式降低了请求发送者和接收者的耦合度。
  • 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
  • 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者修改它们的次序,也可动态地新增或者删除责任。
  • 责任链简化了对象之间的连接。一个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
  • 责任分担。每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

3.2 职责链模式的缺点

  • 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
  • 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
  • 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

3.3 使用场景分析

责任链模式常见的使用场景有以下几种情况:

  • 在运行时需要动态使用多个关联对象来处理同一次请求时。比如,请假流程、员工入职流程、编译打包发布上线流程等。
  • 不想让使用者知道具体的处理逻辑时。比如,做权限校验的登录拦截器。
  • 需要动态更换处理对象时。比如,工单处理系统、网关 API 过滤规则系统等。
  • 职责链模式常被用在框架开发中,用来实现框架的过滤器、拦截器功能,让框架的使用者在不修改源码的情况下,添加新的过滤拦截功能

好了,本次分享就到这里,欢迎大家继续阅读《设计模式》专栏其他设计模式内容,如果有帮助到大家,欢迎大家点赞+关注+收藏,有疑问也欢迎大家评论留言!

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值