责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它用于将请求的发送者和接收者解耦,使多个对象都有机会处理请求。这种模式建立在一个处理对象的链上,每个处理对象都可以选择处理请求或者将请求传递给链上的下一个处理对象。
在 Java 中,责任链模式的实现通常包括以下几个要素:
总之,责任链模式在任何需要将处理逻辑拆分成独立步骤,并且这些步骤可以灵活组合的情况下都是有用的。它帮助减少耦合,使代码更加可扩展和可维护。
假设现在有一个闯关游戏,进入下一关的条件是上一关的分数要高于 xx:
游戏一共 3 个关卡
进入第二关需要第一关的游戏得分大于等于 90
进入第三关需要第二关的游戏得分大于等于 80
//第一关
public class FirstPassHandler {
public int handler(){
System.out.println("第一关-->FirstPassHandler");
return 80;
}
}
//第二关
public class SecondPassHandler {
public int handler(){
System.out.println("第二关-->SecondPassHandler");
return 90;
}
}
//第三关
public class ThirdPassHandler {
public int handler(){
System.out.println("第三关-->ThirdPassHandler,这是最后一关啦");
return 95;
}
}
//客户端
public class HandlerClient {
public static void main(String[] args) {
FirstPassHandler firstPassHandler = new FirstPassHandler();//第一关
SecondPassHandler secondPassHandler = new SecondPassHandler();//第二关
ThirdPassHandler thirdPassHandler = new ThirdPassHandler();//第三关
int firstScore = firstPassHandler.handler();
//第一关的分数大于等于90则进入第二关
if(firstScore >= 90){
int secondScore = secondPassHandler.handler();
//第二关的分数大于等于80则进入第二关
if(secondScore >= 80){
thirdPassHandler.handler();
}
}
}
}
那么如果这个游戏有 99 关,我们的代码很可能就会写成这个样子:
if(第1关通过){
// 第2关 游戏
if(第2关通过){
// 第3关 游戏
if(第3关通过){
// 第4关 游戏
if(第4关通过){
// 第5关 游戏
if(第5关通过){
// 第6关 游戏
if(第6关通过){
//...
}
}
}
}
}
}
这种代码不仅冗余,并且当我们要将某两关进行调整时会对代码非常大的改动,这种操作的风险是很高的,因此,该写法非常糟糕。
如何解决这个问题,我们可以通过链表将每一关连接起来,形成责任链的方式,第一关通过后是第二关,第二关通过后是第三关…
这样客户端就不需要进行多重 if 的判断了:
public abstract class Handler {
/**
* 下一关用当前抽象接口来接收
*/
protected Handler next;
public void setNext(Handler next) {
this.next = next;
}
public abstract int handler();
}
public class FirstPassHandler extends Handler{
final int firstPassScore = 90;
private int play(){
return firstPassScore;
}
@Override
public int handler(){
System.out.println("第一关-->FirstPassHandler");
int score = play();
if(score >= firstPassScore){
//分数>=firstPassScore 并且存在下一关才进入下一关
if(this.next != null){
return this.next.handler();
}
}
return score;
}
}
package com.lf.java.designpattern.chain;
public class SecondPassHandler extends Handler{
final int SecondPassScore = 80;
private int play(){
return SecondPassScore;
}
public int handler(){
System.out.println("第二关-->SecondPassHandler");
int score = play();
if(score >= SecondPassScore){
//分数>=SecondPassScore 并且存在下一关才进入下一关
if(this.next != null){
return this.next.handler();
}
}
return score;
}
}
package com.lf.java.designpattern.chain;
public class ThirdPassHandler extends Handler{
final int SecondPassScore = 70;
private int play(){
return SecondPassScore;
}
public int handler(){
System.out.println("第三关-->ThirdPassHandler");
int score = play();
if(score >= SecondPassScore){
//分数>=SecondPassScore 并且存在下一关才进入下一关
if(this.next != null){
return this.next.handler();
}
}
return score;
}
}
package com.lf.java.designpattern.chain;
public class HandlerClient {
public static void main(String[] args) {
FirstPassHandler firstPassHandler = new FirstPassHandler();//第一关
SecondPassHandler secondPassHandler = new SecondPassHandler();//第二关
ThirdPassHandler thirdPassHandler = new ThirdPassHandler();//第三关
// 和上面没有更改的客户端代码相比,只有这里的set方法发生变化,其他都是一样的
firstPassHandler.setNext(secondPassHandler);//第一关的下一关是第二关
secondPassHandler.setNext(thirdPassHandler);//第二关的下一关是第三关
//说明:因为第三关是最后一关,因此没有下一关
//从第一个关卡开始
firstPassHandler.handler();
}
}
改造完成的代码请求会从链的开头传递到每个处理器,根据请求的内容,每个处理器都可以选择处理请求或者将请求传递给下一个处理器。这样的设计使得责任链可以根据需要动态地调整和扩展。
但是还不能自动化的添加对应的链之间的关系。
对于上面的请求链,我们也可以把这个关系维护到配置文件中或者一个枚举中。将使用枚举来动态的配置请求链并且将每个请求者形成一条调用链。
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
@ToString
public class PassEntity {
/** 处理器顺序id */
private Integer handlerId;
/** 业务处理器名称*/
private String name;
/** 全限定类名 */
private String conference;
/** 前置处理器 */
private Integer preHandlerId;
/** 前置处理器 */
private Integer nextHandlerId;
}
public enum PassEnum {
// handlerId, 拦截者名称,全限定类名,preHandlerId,nextHandlerId
API_HANDLER(new PassEntity(1, "第一关", "com.lf.java.design.pattern.chain.FirstPassHandler", null, 2)),
BLACKLIST_HANDLER(new PassEntity(2, "第二关", "com.lf.java.design.pattern.chain.SecondPassHandler", 1, 3)),
SESSION_HANDLER(new PassEntity(3, "第三关", "com.lf.java.design.pattern.chain.ThirdPassHandler", 2, null)),
;
PassEntity passEntity;
public PassEntity getPassEntity() {
return passEntity;
}
PassEnum(PassEntity passEntity) {
this.passEntity = passEntity;
}
}
public interface IPassService {
/**
* 根据 handlerId 获取配置项
* @param handlerId
* @return
*/
PassEntity getPassEntity(Integer handlerId);
/**
* 获取第一个处理者
* @return
*/
PassEntity getFirstPassEntity();
}
package com.lf.java.designpattern.chain;
import java.util.HashMap;
import java.util.Map;
public class PassServiceImpl implements IPassService {
/**
* 初始化,将枚举中配置的handler初始化到map中,方便获取
*/
private static Map<Integer, PassEntity> passEntityMap = new HashMap<>();
static {
PassEnum[] values = PassEnum.values();
for (PassEnum value : values) {
PassEntity passEntity = value.getPassEntity();
passEntityMap.put(passEntity.getHandlerId(), passEntity);
}
}
@Override
public PassEntity getPassEntity(Integer handlerId) {
return passEntityMap.get(handlerId);
}
@Override
public PassEntity getFirstPassEntity() {
for (Map.Entry<Integer, PassEntity> entry : passEntityMap.entrySet()) {
PassEntity value = entry.getValue();
// 没有上一个handler的就是第一个
if (value.getPreHandlerId() == null) {
return value;
}
}
return null;
}
}
package com.lf.java.designpattern.chain;
public class PassHandlerEnumFactory {
private static IPassService passService = new PassServiceImpl();
// 提供静态方法,获取第一个handler
public static Handler getFirstPassHandler() {
PassEntity firstPassEntity = passService.getFirstPassEntity();
Handler firstPassHandler = newPassHandler(firstPassEntity);
if (firstPassHandler == null) {
return null;
}
PassEntity tempPassEntity = firstPassEntity;
Integer nextHandlerId = null;
Handler tempPassHandler = firstPassHandler;
// 迭代遍历所有handler,以及将它们链接起来
while ((nextHandlerId = tempPassEntity.getNextHandlerId()) != null) {
PassEntity PassEntity = passService.getPassEntity(nextHandlerId);
Handler PassHandler = newPassHandler(PassEntity);
tempPassHandler.setNext(PassHandler);
tempPassHandler = PassHandler;
tempPassEntity = PassEntity;
}
// 返回第一个handler
return firstPassHandler;
}
/**
* 反射实体化具体的处理者
* @param firstPassEntity
* @return
*/
private static Handler newPassHandler(PassEntity firstPassEntity) {
// 获取全限定类名
String className = firstPassEntity.getConference();
try {
// 根据全限定类名,加载并初始化该类,即会初始化该类的静态段
Class<?> clazz = Class.forName(className);
return (Handler) clazz.newInstance();
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
return null;
}
}
测试业务类
public class PassHandlerClient {
public static void main(String[] args) {
Handler firstPassHandler = PassHandlerEnumFactory.getFirstPassHandler();
firstPassHandler.handler();
}
}
运行结果:
第一关-->FirstPassHandler
第二关-->SecondPassHandler
第三关-->ThirdPassHandler
这样通过动态的配置请求链就可以自动将每个请求者形成一条调用链。
还有更为复杂的链的形成,比如业务的链里面有复合链,而复合链又是普通的功能链组成的。这种责任链工厂方式更能充分体现出代码设计优势。