设计模式-责任链

在现代的软件开发中,程序低耦合、高复用、w易拓展、易维护
什么是责任链

责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。
使用场景

  • 多条件流程判断:权限控制
  • 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();  
        //第一关的分数大于等于80则进入第二关  
        if(firstScore >= 80){  
            int secondScore = secondPassHandler.handler();  
            //第二关的分数大于等于90则进入第二关  
            if(secondScore >= 90){  
                thirdPassHandler.handler();  
            }  
        }  
    }  
}

那么如果这个游戏有 100 关,我们的代码很可能就会写成这个样子:

if(第1关通过){  
    // 第2关 游戏  
    if(第2关通过){  
        // 第3关 游戏  
        if(第3关通过){  
           //...  
        }  
    }  
}

这种代码不仅冗余,并且当我们要将某两关进行调整时会对代码非常大的改动,这种操作的风险是很高的。

| 初步改造

如何解决这个问题,我们可以通过链表将每一关连接起来,形成责任链的方式,第一关通过后是第二关,第二关通过后是第三关...

这样客户端就不需要进行多重 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){  
            //分数>=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){  
            //分数>=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();  
  
    }  
}

| 缺点

  • 每个关卡中都有下一关的成员变量并且是不一样的,形成链很不方便
  • 代码的扩展性非常不好

|优化

既然每个关卡中都有下一关的成员变量并且是不一样的,那么我们可以在关卡上抽象出一个父类或者接口,然后每个具体的关卡去继承或者实现。示列:

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){  
            //分数>=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){  
            //分数>=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){  
            //分数>=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();//第三关  
  
        // 和上面没有更改的客户端代码相比,只有这里的set方法发生变化,其他都是一样的  
        firstPassHandler.setNext(secondPassHandler);//第一关的下一关是第二关  
        secondPassHandler.setNext(thirdPassHandler);//第二关的下一关是第三关  
  
        //说明:因为第三关是最后一关,因此没有下一关  
  
        //从第一个关卡开始  
        firstPassHandler.handler();  
  
    }  
}

对于上面的请求链,我们也可以把这个关系维护到配置文件中或者一个枚举中。示列

public enum GatewayEnum {  
    // handlerId, 拦截者名称,全限定类名,preHandlerId,nextHandlerId  
    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 {  
  
    /**  
     * 根据 handlerId 获取配置项  
     * @param handlerId  
     * @return  
     */  
    GatewayEntity getGatewayEntity(Integer handlerId);  
  
    /**  
     * 获取第一个处理者  
     * @return  
     */  
    GatewayEntity getFirstGatewayEntity();  
}  
  
public class GatewayImpl implements GatewayDao {  
  
    /**  
     * 初始化,将枚举中配置的handler初始化到map中,方便获取  
     */  
    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();  
            //  没有上一个handler的就是第一个  
            if (value.getPreHandlerId() == null) {  
                return value;  
            }  
        }  
        return null;  
    }  
}  
  
public class GatewayHandlerEnumFactory {  
  
    private static GatewayDao gatewayDao = new GatewayImpl();  
  
    // 提供静态方法,获取第一个handler  
    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;  
        // 迭代遍历所有handler,以及将它们链接起来  
        while ((nextHandlerId = tempGatewayEntity.getNextHandlerId()) != null) {  
            GatewayEntity gatewayEntity = gatewayDao.getGatewayEntity(nextHandlerId);  
            GatewayHandler gatewayHandler = newGatewayHandler(gatewayEntity);  
            tempGatewayHandler.setNext(gatewayHandler);  
            tempGatewayHandler = gatewayHandler;  
            tempGatewayEntity = gatewayEntity;  
        }  
    // 返回第一个handler  
        return firstGatewayHandler;  
    }  
  
    /**  
     * 反射实体化具体的处理者  
     * @param firstGatewayEntity  
     * @return  
     */  
    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();  
    }  
}
相关推荐
蜡笔小新..12 小时前
【设计模式】软件设计原则——开闭原则&里氏替换&单一职责
java·设计模式·开闭原则·单一职责原则
性感博主在线瞎搞13 小时前
【面向对象】设计模式概念和分类
设计模式·面向对象·中级软件设计师·设计方法
lucifer31113 小时前
JavaScript 中的组合模式(十)
javascript·设计模式
lucifer31113 小时前
JavaScript 中的装饰器模式(十一)
javascript·设计模式
蜡笔小新..14 小时前
【设计模式】软件设计原则——依赖倒置&合成复用
设计模式·依赖倒置原则·合成复用原则
刷帅耍帅14 小时前
设计模式-代理模式
设计模式·代理模式
神的孩子都在歌唱21 小时前
行为设计模式 -观察者模式- JAVA
java·观察者模式·设计模式
刷帅耍帅1 天前
设计模式-解释器模式
设计模式·解释器模式
刷帅耍帅1 天前
设计模式-备忘录模式
设计模式·备忘录模式