IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 设计模式之责任链模式 -> 正文阅读

[Java知识库]设计模式之责任链模式

????????“当你想要让一个以上的对象有机会能够处理某个请求的时候,就使用责任链模式。”


1 简介

????????通过责任链模式,你可以为某个请求创建一个对象链。每个对象依序检查此请求,并对其进行处理,或者将它传给链中的下一个对象。

????????链中的每个对象扮演处理器,并且有一个后继对象(successor)。如果它可以处理请求,就进行处理;否则把请求转发给后继者。

????????通过责任链模式,我们可以将一个复杂的功能拆分成多个子模块(前提是这些子模块的逻辑高度统一),每个子模块只需要专注于自己的业务逻辑,不需要关心其他的业务。而且对于不同的业务来说可以组装不同的子模块,更加灵活。


2 示例

????????Handler:

public abstract class Handler {

    protected Handler successor;

    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }

    protected abstract void handleRequest();
}

????????各个子实现:

public class SpamHandler extends Handler {

    @Override
    protected void handleRequest() {
        System.out.println("垃圾邮件");
        if (successor != null) {
            successor.handleRequest();
        }
    }
}

public class FanHandler extends Handler {

    @Override
    protected void handleRequest() {
        System.out.println("粉丝");
        if (successor != null) {
            successor.handleRequest();
        }
    }
}

public class ComplaintHandler extends Handler {

    @Override
    protected void handleRequest() {
        System.out.println("投诉");
        if (successor != null) {
            successor.handleRequest();
        }
    }
}

public class NewLocHandler extends Handler {

    @Override
    protected void handleRequest() {
        System.out.println("新位置");
        if (successor != null) {
            successor.handleRequest();
        }
    }
}

????????使用:

public static void main(String[] args) {
    SpamHandler spamHandler = new SpamHandler();
    FanHandler fanHandler = new FanHandler();
    ComplaintHandler complaintHandler = new ComplaintHandler();
    NewLocHandler newLocHandler = new NewLocHandler();

    spamHandler.setSuccessor(fanHandler);
    fanHandler.setSuccessor(complaintHandler);
    complaintHandler.setSuccessor(newLocHandler);

    spamHandler.handleRequest();
}

????????运行结果:

垃圾邮件
粉丝
投诉
新位置

? ? ? ? 上面的示例演示了在请求下个handler之前打印了一句话,代表当前handler的业务处理。也可以在请求下个handler之后打印一句话,甚至说可以改变调用下个handler本身的逻辑。

? ? ? ? 而在实际的应用中也有很多的框架使用到了责任链模式,比方说MyBatis的二级缓存和插件(我之前写过《较真儿学源码系列-MyBatis核心流程源码分析》,感兴趣的话可以查看):

?????????Spring AOP的通知机制?(我之前写过《较真儿学源码系列-Spring AOP核心流程源码分析》,感兴趣的话可以查看):

?????????Netty的channelHandler?(我之前写过《较真儿学源码系列-Netty核心流程源码分析》,感兴趣的话可以查看):


3 变种

? ? ? ? 上面演示的示例中,如果我们想要新增一个handler的话,不仅需要新创建一个类、写这个handler本身的逻辑。还需要在main方法中对这个handler进行包装,指定下一个handler。这样就很麻烦。如果来了新的需求的话,只创建新的handler类,不动其他逻辑的话,也是可以实现的。如下所示:

????????Handler:

public abstract class NewHandler {

    protected abstract void handleRequest();
}

????????各个子实现:

@Order(1)
public class SpamNewHandler extends NewHandler {

    @Override
    protected void handleRequest() {
        System.out.println("垃圾邮件");
    }
}

@Order(2)
public class FanNewHandler extends NewHandler {

    @Override
    protected void handleRequest() {
        System.out.println("粉丝");
    }
}

@Order(3)
public class ComplaintNewHandler extends NewHandler {

    @Override
    protected void handleRequest() {
        System.out.println("投诉");
    }
}

@Order(4)
public class NewLocNewHandler extends NewHandler {

    @Override
    protected void handleRequest() {
        System.out.println("新位置");
    }
}

????????使用:

public static void main(String[] args) throws Exception {
    //获取所有handler实现类
    Set<HandlerClassDTO> handlerClasses = getHandlerClasses();
    for (HandlerClassDTO handlerClass : handlerClasses) {
        Class<? extends NewHandler> clazz = handlerClass.getClazz();
        Constructor<? extends NewHandler> constructor = clazz.getConstructor();
        NewHandler newHandler = constructor.newInstance();
        //处理请求
        newHandler.handleRequest();
    }
}

private static final Set<HandlerClassDTO> HANDLER_CLASSES = Sets.newLinkedHashSetWithExpectedSize(4);

private static Set<HandlerClassDTO> getExceptionContentClasses() {
    if (CollectionUtils.isNotEmpty(HANDLER_CLASSES)) {
        return HANDLER_CLASSES;
    }
    synchronized (HANDLER_CLASSES) {
        //DCL
        if (CollectionUtils.isNotEmpty(HANDLER_CLASSES)) {
            return HANDLER_CLASSES;
        }
        String packageName = NewHandler.class.getPackage().getName();
        Reflections reflections = new Reflections(new ConfigurationBuilder()
                .forPackages(packageName));
        Set<Class<? extends NewHandler>> classes = reflections.getSubTypesOf(NewHandler.class);
        Set<HandlerClassDTO> handlerClassDTOSet = classes.stream().map(item -> new HandlerClassDTO(item, getOrder(item))).collect(Collectors.toSet());
        //根据@Order排序
        handlerClassDTOSet = handlerClassDTOSet.stream().sorted(Comparator.comparingInt(HandlerClassDTO::getOrder)).collect(Collectors.toCollection(LinkedHashSet::new));
        HANDLER_CLASSES.addAll(handlerClassDTOSet);
        return handlerClassDTOSet;
    }
}

private static int getOrder(Class<?> clazz) {
    Order annotation = clazz.getAnnotation(Order.class);
    if (annotation == null) {
        return Integer.MAX_VALUE;
    }
    return annotation.value();
}

????????运行结果:

垃圾邮件
粉丝
投诉
新位置

? ? ? ? 这里使用了Reflections来反射获取NewHandler的所有实现类,并缓存了起来(我之前写过对Reflections的使用《Java反射工具:Reflections》,感兴趣的话可以查看)。各个Handler的执行顺序依赖于@Order注解(这里只是提供了一种实现思路,也可以有别的实现。例如,加入别的注解来对handler是否加入责任链进行区分)。

? ? ? ? 展示这个例子并不是在说这种实现方案就比第2小节的实现要更好。第2小节的实现更灵活,也更简单,这是它的优势;而上面的例子虽然能完全写活,但更复杂。也丢掉了在下一个handler执行之前和之后处理的功能,只能遍历地去一个个去处理当前handler的内容(可以去尝试解决,但肯定会更加复杂)。具体采用哪种方案需要视情况而定。

????????另外,我在这里想说的是,学习设计模式并应用的话,不要去形成一种固定思维。不要说这种设计模式规定地怎么去写,我就一定要按照这种方式去写。就拿责任链模式来说,我设计handler的时候就一定要加入一个successor的后继节点吗?不一定。其实我只要能把职责分开,再通过组合的方式,一个个去遍历执行handler的话,就可以认为是使用到了责任链模式(就如同我上面使用的那样)。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-05-12 16:20:26  更:2022-05-12 16:20:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 22:55:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码