责任链(Chain Of Responsibility)

Intent

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链发送该请求,直到有一个对象处理它为止。

Class Diagram

  • Handler:定义处理请求的接口,并且实现后继链(successor)

Implementation

public abstract class Handler {
​
    protected Handler successor;
​
​
    public Handler(Handler successor) {
        this.successor = successor;
    }
​
​
    protected abstract void handleRequest(Request request);
}
public class ConcreteHandler1 extends Handler {
​
    public ConcreteHandler1(Handler successor) {
        super(successor);
    }
​
​
    @Override
    protected void handleRequest(Request request) {
        if (request.getType() == RequestType.TYPE1) {
            System.out.println(request.getName() + " is handle by ConcreteHandler1");
            return;
        }
        if (successor != null) {
            successor.handleRequest(request);
        }
    }
}
public class ConcreteHandler2 extends Handler {
​
    public ConcreteHandler2(Handler successor) {
        super(successor);
    }
​
​
    @Override
    protected void handleRequest(Request request) {
        if (request.getType() == RequestType.TYPE2) {
            System.out.println(request.getName() + " is handle by ConcreteHandler2");
            return;
        }
        if (successor != null) {
            successor.handleRequest(request);
        }
    }
}
public class Request {
​
    private RequestType type;
    private String name;
​
​
    public Request(RequestType type, String name) {
        this.type = type;
        this.name = name;
    }
​
​
    public RequestType getType() {
        return type;
    }
​
​
    public String getName() {
        return name;
    }
}
​
public enum RequestType {
    TYPE1, TYPE2
}
public class Client {
​
    public static void main(String[] args) {
​
        Handler handler1 = new ConcreteHandler1(null);
        Handler handler2 = new ConcreteHandler2(handler1);
​
        Request request1 = new Request(RequestType.TYPE1, "request1");
        handler2.handleRequest(request1);
​
        Request request2 = new Request(RequestType.TYPE2, "request2");
        handler2.handleRequest(request2);
    }
}

Business Scenario

模拟上线审批

模拟在618期间系统上线的审批流程,随着时间的邻近,上线的审批流程会越来越复杂

模拟审核服务

/**
 * 模拟审核服务
 * 1. auth          审核流程
 * 2. queryAuthInfo 查询审核信息(时间)
 */
public class AuthService {
​
    private static Map<String, Date> authMap = new ConcurrentHashMap<String, Date>();
​
    public static Date queryAuthInfo(String uId, String orderId) {
        return authMap.get(uId.concat(orderId));
    }
​
    public static void auth(String uId, String orderId) {
        authMap.put(uId.concat(orderId), new Date());
    }
​
}

责任链模式实现

审核返回的结果

public class AuthInfo {
​
    private String code;
    private String info = "";
​
    public AuthInfo(String code, String ...infos) {
        this.code = code;
        for (String str:infos){
            this.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;
    }
}

责任链抽象

/**
 * 审核规定;
 * 1. 601-610 三级审批 + 二级审批
 * 2. 611-620 三级审批 + 二级审批 + 一级审批
 * 3. 其他时间 三级审批
 */
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;                         // 级别人员姓名
    private AuthLink next;                                  // 责任链
​
    public AuthLink(String levelUserId, String levelUserName) {
        this.levelUserId = levelUserId;
        this.levelUserName = levelUserName;
    }
​
    public AuthLink next() {
        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 {
​
    public Level1AuthLink(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.next();
        if (null == next) {
            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("2022-06-11 00:00:00");
    private Date endDate = f.parse("2022-06-20 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.next();
        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);
        }
​
        logger.info("code = {} 单号:{} 状态:二级审批完成负责人 时间:{} 审批人:{}", "0000", orderId, f.format(date), levelUserName);
        return next.doAuth(uId, orderId, authDate);
    }
​
}
​
/**
 * 三级负责人
 */
public class Level3AuthLink extends AuthLink {
​
    private Date beginDate = f.parse("2022-06-01 00:00:00");
    private Date endDate = f.parse("2022-06-25 23:59:59");
​
    public Level3AuthLink(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.next();
        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);
        }
​
        logger.info("code = {} 单号:{} 状态:三级审批完成负责人 时间:{} 审批人:{}", "0000", orderId, f.format(date), levelUserName);
        return next.doAuth(uId, orderId, authDate);
    }
​
}

测试

public class ApiTest {
​
    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 format = new SimpleDateFormat("yyyy-MM-dd");
        // 模拟三级负责⼈人审批
        AuthService.auth("1000013", "1000998004813441");
        logger.info("测试结果:{}", "模拟三级负责⼈人审批");
        logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("⼩小z",
                "1000998004813441", new Date())));
​
        logger.info("===============");
        // 模拟⼆二级负责⼈人审批
        AuthService.auth("1000012", "1000998004813441");
        logger.info("测试结果:{}", "模拟三、二级负责⼈人审批");
        logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("⼩小z",
                "1000998004813441", format.parse("2022-06-01"))));
​
        logger.info("===============");
        // 模拟⼀一级负责⼈人审批
        AuthService.auth("1000011", "1000998004813441");
        logger.info("测试结果:{}", "模拟三、二、一级负责⼈人审批");
        logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("小z",
                "1000998004813441", format.parse("2022-06-11"))));
    }
​
}

Summary

责任链模式通过一条链关联了所有的处理过程,当来请求后,可以只是链路上的某个处理类处理,也可以是链路上的多个处理类处理,这样让逻辑更加的明确,也有了处理的先后顺序,如果在结合工厂模式,还方便拓展。