摘要: 原创出处 网络 「网络」欢迎转载,保留摘要,谢谢!
最近,我让团队内一位成员写了一个导入功能。他使用了责任链模式,代码堆的非常多,bug 也多,没有达到我预期的效果。实际上,针对导入功能,我认为模版方法更合适!为此,隔壁团队也拿出我们的案例,进行了集体 code review。
学好设计模式,且不要为了练习,强行使用!让原本100行就能实现的功能,写了 3000 行!
对错暂且不论,我们先一起看看责任链设计模式吧!
什么是责任链
**「责任链模式」**是一种行为设计模式, 允许你将请求沿着处理者链进行发送。收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。
场景
责任链的使用场景还是比较多的
多条件流程判断:权限控制
ERP 系统流程审批:总经理、人事经理、项目经理
Java 过滤器 的底层实现 Filter
如果不使用该设计模式,那么当需求有所改变时,就会使得代码臃肿或者难以维护,例如下面的例子
反例
假设现在有一个闯关游戏,进入下一关的条件是上一关的分数要高于xx
游戏一共 3 个关卡
进入第二关需要第一关的游戏得分大于等于 80
进入第三关需要第二关的游戏得分大于等于 90
那么代码可以这样写
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(); if (firstScore >= 80 ){ int secondScore = secondPassHandler.handler(); if (secondScore >= 90 ){ thirdPassHandler.handler(); } } } }
那么如果这个游戏有100关,我们的代码很可能就会写成这个样子
if (第1 关通过){ if (第2 关通过){ if (第3 关通过){ if (第4 关通过){ if (第5 关通过){ if (第6 关通过){ } } } } } }
这种代码不仅冗余,并且当我们要将某两关进行调整时会对代码非常大的改动,这种操作的风险是很高的,因此,该写法非常糟糕
初步改造
如何解决这个问题,我们可以通过链表将每一关连接起来,形成责任链的方式,第一关通过后是第二关,第二关通过后是第三关 ....,这样客户端就不需要进行多重 if 的判断了
public class FirstPassHandler { private SecondPassHandler secondPassHandler; public void setSecondPassHandler (SecondPassHandler secondPassHandler) { this .secondPassHandler = secondPassHandler; } private int play () { return 80 ; } public int handler () { System.out.println("第一关-->FirstPassHandler" ); if (play() >= 80 ){ if (this .secondPassHandler != null ){ return this .secondPassHandler.handler(); } } return 80 ; } } public class SecondPassHandler { private ThirdPassHandler thirdPassHandler; public void setThirdPassHandler (ThirdPassHandler thirdPassHandler) { this .thirdPassHandler = thirdPassHandler; } private int play () { return 90 ; } public int handler () { System.out.println("第二关-->SecondPassHandler" ); if (play() >= 90 ){ if (this .thirdPassHandler != null ){ return this .thirdPassHandler.handler(); } } return 90 ; } } public class ThirdPassHandler { private int play () { return 95 ; } public int handler () { System.out.println("第三关-->ThirdPassHandler,这是最后一关啦" ); return play(); } } public class HandlerClient { public static void main (String[] args) { FirstPassHandler firstPassHandler = new FirstPassHandler(); SecondPassHandler secondPassHandler = new SecondPassHandler(); ThirdPassHandler thirdPassHandler = new ThirdPassHandler(); firstPassHandler.setSecondPassHandler(secondPassHandler); secondPassHandler.setThirdPassHandler(thirdPassHandler); firstPassHandler.handler(); } }
缺点
现有模式的缺点
每个关卡中都有下一关的成员变量
并且是不一样的,形成链很不方便
代码的扩展性非常不好
责任链改造
既然每个关卡中都有下一关的成员变量
并且是不一样的,那么我们可以在关卡上抽象出一个父类或者接口,然后每个具体的关卡去继承或者实现
有了思路,我们先来简单介绍一下责任链设计模式的**「基本组成」**
抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
public abstract class AbstractHandler { protected AbstractHandler next; public void setNext (AbstractHandler next) { this .next = next; } public abstract int handler () ; } public class FirstPassHandler extends AbstractHandler { private int play () { return 80 ; } @Override public int handler () { System.out.println("第一关-->FirstPassHandler" ); int score = play(); if (score >= 80 ){ if (this .next != null ){ return this .next.handler(); } } return score; } } public class SecondPassHandler extends AbstractHandler { private int play () { return 90 ; } public int handler () { System.out.println("第二关-->SecondPassHandler" ); int score = play(); if (score >= 90 ){ if (this .next != null ){ return this .next.handler(); } } return score; } } public class ThirdPassHandler extends AbstractHandler { private int play () { return 95 ; } public int handler () { System.out.println("第三关-->ThirdPassHandler" ); int score = play(); if (score >= 95 ){ if (this .next != null ){ return this .next.handler(); } } return score; } } public class HandlerClient { public static void main (String[] args) { FirstPassHandler firstPassHandler = new FirstPassHandler(); SecondPassHandler secondPassHandler = new SecondPassHandler(); ThirdPassHandler thirdPassHandler = new ThirdPassHandler(); firstPassHandler.setNext(secondPassHandler); secondPassHandler.setNext(thirdPassHandler); firstPassHandler.handler(); } }
责任链工厂改造
对于上面的请求链,我们也可以把这个关系维护到配置文件中或者一个枚举中。我将使用枚举来教会大家怎么动态的配置请求链并且将每个请求者形成一条调用链。
public enum GatewayEnum { API_HANDLER(new GatewayEntity(1 , "api接口限流" , "cn.dgut.design.chain_of_responsibility.GateWay.impl.ApiLimitGatewayHandler" , null , 2 )), BLACKLIST_HANDLER(new GatewayEntity(2 , "黑名单拦截" , "cn.dgut.design.chain_of_responsibility.GateWay.impl.BlacklistGatewayHandler" , 1 , 3 )), SESSION_HANDLER(new GatewayEntity(3 , "用户会话拦截" , "cn.dgut.design.chain_of_responsibility.GateWay.impl.SessionGatewayHandler" , 2 , null )), ; GatewayEntity gatewayEntity; public GatewayEntity getGatewayEntity () { return gatewayEntity; } GatewayEnum(GatewayEntity gatewayEntity) { this .gatewayEntity = gatewayEntity; } } public class GatewayEntity { private String name; private String conference; private Integer handlerId; private Integer preHandlerId; private Integer nextHandlerId; } public interface GatewayDao { GatewayEntity getGatewayEntity (Integer handlerId) ; GatewayEntity getFirstGatewayEntity () ; } public class GatewayImpl implements GatewayDao { private static Map<Integer, GatewayEntity> gatewayEntityMap = new HashMap<>(); static { GatewayEnum[] values = GatewayEnum.values(); for (GatewayEnum value : values) { GatewayEntity gatewayEntity = value.getGatewayEntity(); gatewayEntityMap.put(gatewayEntity.getHandlerId(), gatewayEntity); } } @Override public GatewayEntity getGatewayEntity (Integer handlerId) { return gatewayEntityMap.get(handlerId); } @Override public GatewayEntity getFirstGatewayEntity () { for (Map.Entry<Integer, GatewayEntity> entry : gatewayEntityMap.entrySet()) { GatewayEntity value = entry.getValue(); if (value.getPreHandlerId() == null ) { return value; } } return null ; } } public class GatewayHandlerEnumFactory { private static GatewayDao gatewayDao = new GatewayImpl(); public static GatewayHandler getFirstGatewayHandler () { GatewayEntity firstGatewayEntity = gatewayDao.getFirstGatewayEntity(); GatewayHandler firstGatewayHandler = newGatewayHandler(firstGatewayEntity); if (firstGatewayHandler == null ) { return null ; } GatewayEntity tempGatewayEntity = firstGatewayEntity; Integer nextHandlerId = null ; GatewayHandler tempGatewayHandler = firstGatewayHandler; while ((nextHandlerId = tempGatewayEntity.getNextHandlerId()) != null ) { GatewayEntity gatewayEntity = gatewayDao.getGatewayEntity(nextHandlerId); GatewayHandler gatewayHandler = newGatewayHandler(gatewayEntity); tempGatewayHandler.setNext(gatewayHandler); tempGatewayHandler = gatewayHandler; tempGatewayEntity = gatewayEntity; } return firstGatewayHandler; } private static GatewayHandler newGatewayHandler (GatewayEntity firstGatewayEntity) { String className = firstGatewayEntity.getConference(); try { Class<?> clazz = Class.forName(className); return (GatewayHandler) clazz.newInstance(); } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { e.printStackTrace(); } return null ; } } public class GetewayClient { public static void main (String[] args) { GetewayHandler firstGetewayHandler = GetewayHandlerEnumFactory.getFirstGetewayHandler(); firstGetewayHandler.service(); } }
聊聊其他
设计模式有很多,责任链只是其中的一种,我觉得很有意思,非常值得一学。
设计模式确实是一门艺术,仍需努力呀!