第六章 6.4 职责链模式

分类:01_设计模式

标签:

6.4.1 职责链模式介绍

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

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

113.jpg

6.4.2 职责链模式原理

职责链模式结构

114.jpg

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

6.4.3 职责链模式实现

责任链模式的实现非常简单,每一个具体的处理类都会保存在它之后的下一个处理类。当处理完成后,就会调用设置好的下一个处理类,直到最后一个处理类不再设置下一个处理类,这时处理链条全部完成。


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


/**
* 抽象处理者类
* @author spikeCong
* @date 2022/10/14
**/
public abstract class Handler {

   protected Handler successor = null;

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

   public abstract void handle(RequestData requestData);
}

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("执行中止!");
       }
   }
}

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("执行中止!");
       }
   }
}

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("执行中止!");
       }
   }
}

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

}

6.4.4 职责链模式应用实例

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

116.jpg

1) 不使用设计模式


/**
* 审核信息
* @author spikeCong
* @date 2022/10/14
**/
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 + '\'' +
               '}';
   }
}

/**
* 模拟审核服务
* @author spikeCong
* @date 2022/10/14
**/
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
   }
}

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 +", 状态: 审批完成!");
   }
}

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 ) 职责链模式重构代码

下图是为当前业务设计的责任链结构,统一抽象类AuthLink 下 有三个子类,将三个子类的执行通过编排,模拟出一条链路,这个链路就是业务中的责任链.

115.jpg


/**
* 抽象审核链类
*/
public abstract class AuthLink {

   protected Logger logger = LoggerFactory.getLogger(AuthLink.class);

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

/*
* 一级负责人
*/
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);
   }
}

/**
* 二级负责人
*/
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);
   }

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

}

/**
* 三级负责人
*/
public class Level3AuthLink extends AuthLink {

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

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

}

测试


public class Client {

   private Logger logger = LoggerFactory.getLogger(ApiTest.class);

   @Test
   public void test_AuthLink() 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("2020-11-18 23:49:46");

       logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));

       // 模拟三级负责人审批
       AuthService.auth("1000013", "1000998004813441");
       logger.info("测试结果:{}", "模拟三级负责人审批,王工");
       logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));

       // 模拟二级负责人审批
       AuthService.auth("1000012", "1000998004813441");
       logger.info("测试结果:{}", "模拟二级负责人审批,张经理");
       logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));

       // 模拟一级负责人审批
       AuthService.auth("1000011", "1000998004813441");
       logger.info("测试结果:{}", "模拟一级负责人审批,段总");
       logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));

   }
}

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

6.4.5 职责链模式总结

1) 职责链模式的优点:

2) 职责链模式的缺点:

3) 使用场景分析

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


修改内容