乐趣区

关于java:设计模式15从审批流中学习责任链模式

曾经来到了责任链模式,各位客官听我瞎扯 ……

责任链模式是什么

责任链模式是一种设计模式。在责任链模式里,很多对象由每一个对象对其下家的援用而连接起来造成一条链。申请在这个链上传递,直到链上的某一个对象决定解决此申请。收回这个申请的客户端并不知道链上的哪一个对象最终解决这个申请,这使得零碎能够在不影响客户端的状况下动静地从新组织和调配责任。(百度百科)

责任链模式是一种行为型设计模式,也就是重点是解决数据,假如咱们有一份数据,须要通过很多个节点解决,那么就会是以下这个样子:

一个节点解决完之后,交给下一个节点,不晓得大家有没有应用过审批流,当咱们提完一个审批单后,你的 leader 审批,leader 审批通过之后就是总监批,总监前面可能是高级总监,或者 cto,或者hr。他们在同一个链条上,假使你的leader 没有审批完,前面的节点是不可能收到信息的。如果你的 leader 回绝了你的申请,那数据也不会达到前面的审批节点。

如果你接触过前端,JS 中点击某个 div 的时候会产生冒泡事件,也就是点击上面的 A, AB 外面,BC 外面,A-> B -> C 会顺次收到点击事件:

再举个例子,在 SpringMVC中,咱们有时候会定义一些拦截器,对申请进行预处理,也就是申请过去的时候,会顺次经验拦截器,通过拦截器之后才会进入咱们的解决业务逻辑代码。

之前,在做人员治理的时候,有波及到人员到职状况的解决流程,要交接工作,解除权限,禁用账号等等,这整个解决流程就很适宜应用责任链来解决。当然,主动解决流程是会出错的,保留每一个阶段的状态,针对出错的场景,能够手动去从断开责任链的中央接着执行。这整个流程的框架就是利用了责任链,然而依据理论场景也增加了不少其余的货色。

两点疑难

  1. 责任链的每一个节点是不是肯定蕴含下一个节点的援用?

答:不肯定,要么把所有责任节点放在一个 list 外面,顺次解决;要么每个节点蕴含下一个责任节点的援用,

  1. 责任链到底是不容许中断还是不容许中断?

答:两种都能够,不拘泥于细节,能够依据本人的场景应用。

责任链模式中的角色

责任链个别有以下的角色:

  • Client(客户端):调用责任链处理器的解决办法,或者在第一个链对象中调用 handle 办法。
  • Handler(处理器):抽象类,提供给理论处理器继承而后实现 handle 办法,解决申请
  • ConcreteHandler(具体处理器):实现 handler 的类,同时实现 handle 办法,负责解决业务逻辑类,不同业务模块有不同的ConcreteHandler
  • HandlerChain:负责组合责任链的所有节点以及流程(如果节点蕴含下一个节点的援用,那么 HandlerChain 能够不存在)

审批链的实现

上面咱们别离来实现不同的写法,假如当初有一个场景,秦怀入职了一家公司,哼哧哼哧干了一年,然而始终没调薪,又过了一年,总得加薪了吧,不加就要提桶跑路了,于是秦怀大胆去外部零碎提了一个申请单:【加薪申请】

不中断模式

先演示不中断模式,得先弄个申请单的实体,外面蕴含了申请单的名字和申请人:

public class Requisition {
    // 名称
    public String name;


    // 申请人
    public String applicant;


    public Requisition(String name, String applicant) {
        this.name = name;
        this.applicant = applicant;
    }
}

责任链中的每个责任节点,也就是处理器,能够形象成为一个接口:

public interface Handler {
    // 解决申请单
    void process(Requisition requisition);
}

咱们顺次实现了三个不同的责任节点,别离代表 leader,总监,hr 审批:

public class ManagerHandler implements Handler {
    @Override
    public void process(Requisition requisition) {System.out.println(String.format("Manager 审批来自 [%s] 的申请单[%s]...", requisition.applicant, requisition.name));
    }
}
public class DirectorHandler implements Handler{
    @Override
    public void process(Requisition requisition) {System.out.println(String.format("Director 审批来自 [%s] 的申请单[%s]...", requisition.applicant, requisition.name));
    }
}
public class HrHandler implements Handler{
    @Override
    public void process(Requisition requisition) {System.out.println(String.format("Hr 审批来自 [%s] 的申请单[%s]...", requisition.applicant, requisition.name));
    }
}

责任节点都有了,咱们须要用一个责任链把它们组合起来:

public class HandlerChain {List<Handler> handlers = new ArrayList<>();


    public void addHandler(Handler handler){handlers.add(handler);
    }


    public void handle(Requisition requisition){for(Handler handler:handlers){handler.process(requisition);
        }
        System.out.println(String.format("来自 [%s] 的申请单 [%s] 审批实现", requisition.applicant, requisition.name));
    }
}

客户端测试类:

public class ClientTest {public static void main(String[] args) {HandlerChain handlerChain = new HandlerChain();
        handlerChain.addHandler(new ManagerHandler());
        handlerChain.addHandler(new DirectorHandler());
        handlerChain.addHandler(new HrHandler());
        handlerChain.handle(new Requisition("加薪申请","秦怀"));
    }
}

运行后果:

Manager 审批来自 [秦怀] 的申请单[加薪申请]...
Director 审批来自 [秦怀] 的申请单[加薪申请]...
Hr 审批来自 [秦怀] 的申请单[加薪申请]...
来自 [秦怀] 的申请单 [加薪申请] 审批实现

从后果上来看,申请单的确经验过了每一个节点,造成了一条链条,这就是责任链的核心思想。每个节点拿到的都是同一个数据,同一个申请单。

中断模式

秦怀加薪的想法很美妙,然而事实很骨感,下面的审批流程一路畅通,然而万一 Hr 想回绝掉这个申请单了,下面的代码并没有赋予她这种能力,因而,代码得改!(Hr 心田:我就要这个性能,今天上线)。

既然是反对中断,也就是反对任何一个节点审批不通过就间接返回,不会再走到下一个节点,先给形象的解决节点办法加上返回值:

public interface Handler {
    // 解决申请单
    boolean process(Requisition requisition);
}

三个解决节点也同步批改:

public class ManagerHandler implements Handler {
    @Override
    public boolean process(Requisition requisition) {System.out.println(String.format("Manager 审批通过来自 [%s] 的申请单[%s]...", requisition.applicant, requisition.name));
        return true;
    }
}
public class DirectorHandler implements Handler{
    @Override
    public boolean process(Requisition requisition) {System.out.println(String.format("Director 审批通过来自 [%s] 的申请单[%s]...", requisition.applicant, requisition.name));
        return true;
    }
}
public class HrHandler implements Handler{
    @Override
    public boolean process(Requisition requisition) {System.out.println(String.format("Hr 审批不通过来自 [%s] 的申请单[%s]...", requisition.applicant, requisition.name));
        return false;
    }
}

解决链调整:

public class HandlerChain {List<Handler> handlers = new ArrayList<>();


    public void addHandler(Handler handler) {handlers.add(handler);
    }


    public void handle(Requisition requisition) {for (Handler handler : handlers) {if (!handler.process(requisition)) {System.out.println(String.format("来自 [%s] 的申请单 [%s] 审批不通过", requisition.applicant, requisition.name));
                return;
            }
        }
        System.out.println(String.format("来自 [%s] 的申请单 [%s] 审批实现", requisition.applicant, requisition.name));
    }
}

批改实现之后的后果:

Manager 审批通过来自 [秦怀] 的申请单[加薪申请]...
Director 审批通过来自 [秦怀] 的申请单[加薪申请]...
Hr 审批不通过来自 [秦怀] 的申请单[加薪申请]...
来自 [秦怀] 的申请单 [加薪申请] 审批不通过

秦怀哭了,加薪的审批被 hr 回绝了。尽管被回绝了,然而秦怀也感触到了能够中断的责任链模式,这种写法在解决申请的时候也比拟常见,因为咱们不心愿不非法的申请到失常的解决逻辑中。

蕴含下一个节点的援用

后面说过,在责任链模式里,很多对象由每一个对象对其下家的援用而连接起来造成一条链。下面的写法都是不蕴含下一个节点援用的写法。上面咱们实际一下,如何应用援用写法实现责任链。

革新 Handler 接口为抽象类:

public abstract class Handler {

    private Handler nextHandler;

    public void setNextHandler(Handler handler) {this.nextHandler = handler;}

    // 解决申请单
    protected abstract boolean process(Requisition requisition);

    // 裸露办法
    public boolean handle(Requisition requisition) {boolean result = process(requisition);
        if (result) {if (nextHandler != null) {return nextHandler.handle(requisition);
            } else {return true;}
        }
        return false;
    }
}

三个实现类不变:

public class ManagerHandler extends Handler{
    @Override
    boolean process(Requisition requisition) {
        System.out.println(String.format("Manager 审批通过来自 [%s] 的申请单[%s]...", requisition.applicant, requisition.name));
        return true;
    }
}
public class DirectorHandler extends Handler {
    @Override
    public boolean process(Requisition requisition) {
        System.out.println(String.format("Director 审批通过来自 [%s] 的申请单[%s]...", requisition.applicant, requisition.name));
        return true;
    }
}

public class HrHandler extends Handler{
    @Override
    public boolean process(Requisition requisition) {System.out.println(String.format("Hr 审批不通过来自 [%s] 的申请单[%s]...",
                requisition.applicant, requisition.name));
        return false;
    }
}

测试方法,结构嵌套援用:

public class ClientTest {public static void main(String[] args) {HrHandler hrHandler = new HrHandler();
        DirectorHandler directorHandler = new DirectorHandler();
        directorHandler.setNextHandler(hrHandler);
        ManagerHandler managerHandler = new ManagerHandler();
        managerHandler.setNextHandler(directorHandler);

        managerHandler.handle(new Requisition("加薪申请","秦怀"));
    }
}

能够看到运行后果也是一样:

Manager 审批通过来自 [秦怀] 的申请单[加薪申请]...
Director 审批通过来自 [秦怀] 的申请单[加薪申请]...
Hr 审批不通过来自 [秦怀] 的申请单[加薪申请]...

拓展一下

其实责任链配合上 Spring 更加好用,次要有两点:

1、能够应用注入,自动识别该接口的所有实现类。

@Autowire
public List<Handler> handlers;

2、能够应用 @Order 注解,让接口实现类依照程序执行。

@Order(1)
public class HrHandler extends Handler{...}

源码中的利用

  • Mybatis 中的 Plugin 机制应用了责任链模式,配置各种官网或者自定义的 Plugin,与 Filter 相似,能够在执行 Sql 语句的时候执行一些操作。
  • Spring中应用责任链模式来治理Adviser

比方 Mybatis 中能够增加若干的插件,比方PageHelper,多个插件对对象的包装采纳的动静代理来实现,多层代理。

// 责任链插件
public class InterceptorChain {private final List<Interceptor> interceptors = new ArrayList<>();
  // 生成代理对象
  public Object pluginAll(Object target) {for (Interceptor interceptor : interceptors) {target = interceptor.plugin(target);
    }
    return target;
  }
  // 一层一层的拦截器
  public void addInterceptor(Interceptor interceptor) {interceptors.add(interceptor);
  }

  public List<Interceptor> getInterceptors() {return Collections.unmodifiableList(interceptors);
  }

}

总结

责任链模式的长处:

  • 升高对象间接的耦合度,对象会主动传递到下一个责任节点,不论是援用形式,还是非援用形式。
  • 加强拓展性,如果须要增加新的责任节点,也比拟不便,实现特定的接口即可。
  • 责任节点的程序可控,能够指定一个程序属性,排序即可。
  • 每个责任节点职责专一,只解决本人的工作,合乎类的繁多职责准则。

责任链的毛病:

  • 如果责任链比拟长,性能会受影响。
  • 责任链可能会中途断掉,申请不肯定会被接管。

责任链个别是在流程化的解决中,多个节点解决同一份数据,顺次传递,可能有程序要求,也可能没有,处理器的能力形象成接口,不便拓展。

设计模式系列:

  • 设计模式【1】– 单例模式到底几种写法?
  • 设计模式【1.1】– 你想如何毁坏单例模式?
  • 设计模式【1.2】– 枚举式单例有那么好用么?
  • 设计模式【1.3】– 为什么饿汉式单例是线程平安的?
  • 设计模式【2】– 简略工厂模式理解一下?
  • 设计模式【2.1】– 简略工厂模式怎么演变成工厂办法模式?
  • 设计模式【2.2】– 工厂模式怎么演变成形象工厂模式?
  • 设计模式【3.1】– 浅谈代理模式之动态、动静、cglib 代理
  • 设计模式【3.2】– JDK 动静代理源码剖析有多香?
  • 设计模式【3.3】– CGLIB 动静代理源码解读
  • 设计模式【4】– 建造者模式详解
  • 设计模式【5】– 原型模式
  • 设计模式【6.1】– 初探适配器模式
  • 设计模式【6.2】– 再聊聊适配器模式
  • 设计模式【7】– 摸索一下桥接模式
  • 设计模式【8】– 手工耿教我写装璜器模式
  • 设计模式【9】– 外观模式?没那么高大上
  • 设计模式【10】– 顺便看看享元模式
  • 设计模式【11】– 搞定组合模式
  • 设计模式【12】– 搞定最近大火的策略模式
  • 设计模式【13】– 模板模式怎么弄?
  • 设计模式【14】– 从智能音箱中学习命令模式

【作者简介】
秦怀,公众号【秦怀杂货店】作者,集体网站:http://aphysia.cn,技术之路不在一时,山高水长,纵使迟缓,驰而不息。

剑指 Offer 全副题解 PDF

开源编程笔记

退出移动版