设计模式-责任链模式

原理和实现

将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收 对象能够处理它为止,实时上,在常见的使用场景中,我们的责任链并不是和概念中的完全一样

● 原始概念中,是直到链上的某个接收对象能够处理它为止

● 实际使用中,链上的所有对象都可以对请求进行特殊处理

实现方式

使用链表实现

  1. 定义虚拟类Handler
java 复制代码
package com.hillky.desgin_learn.chainResponsibility;

public abstract class Handler {
    protected Handler successor = null;
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }
    public abstract void handle();
}
  1. 定义具体处理器
java 复制代码
package com.hillky.desgin_learn.chainResponsibility;

public class HandlerA extends Handler{
    @Override
    public void handle() {
        boolean handled = false;
//...
        if (!handled && successor != null) {
            successor.handle();
        }
    }
}


package com.hillky.desgin_learn.chainResponsibility;

public class HandlerB extends Handler{
    @Override
    public void handle() {
        boolean handled = false;
//...
        if (!handled && successor != null) {
            successor.handle();
        }
    }
}
  1. 创建HandlerChain
java 复制代码
package com.hillky.desgin_learn.chainResponsibility;

public class HandlerChain {
    private Handler head = null;
    private Handler tail = null;

    public void addHandler(Handler handler) {
        handler.setSuccessor(null);
        if (head == null) {
            head = handler;
            tail = handler;
            return;
        }
        tail.setSuccessor(handler);
        tail = handler;
    }
    public void handle() {
        if (head != null) {
            head.handle();
        }
    }
}
  1. 使用测试
java 复制代码
package com.hillky.desgin_learn.chainResponsibility;

public class Client {
    public static void main(String[] args) {
        HandlerChain chain = new HandlerChain();
        chain.addHandler(new HandlerA());
        chain.addHandler(new HandlerB());
        chain.handle();
    }
}

处理器类的 handle() 函数,不仅包含自己的业 务逻辑,还包含对下一个处理器的调用,也就是代码中的 successor.handle()。一个不熟悉这种代码结构的程序员,在添加新的处理器类的时候,很有可能忘记在handle() 函数中调用 successor.handle(),这就会导致代码出现 bug。

针对这个问题,我们对代码进行重构,利用模板模式,将调用 successor.handle() 的逻辑从具体的处理器类中剥离出来,放到抽象父类中。

java 复制代码
package com.hillky.desgin_learn.chainResponsibility;

public abstract class Handler {

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

    public abstract boolean doHandle();

    public final void handle() {
        boolean handled = doHandle();
        if (successor != null && !handled) {
            successor.handle();
        }
    }
}

package com.hillky.desgin_learn.chainResponsibility;

public class HandlerA extends Handler{

    @Override
    public boolean doHandle() {
        boolean handled = false;
        System.out.println("Handler A在处理");
//...
        return handled;
    }
}

使用数组实现

这种实现方式更加简单。 HandlerChain 类用数组而非链表来保存所有的处理器,并且需要在 HandlerChain 的 handle() 函数中,依次调用每个处理器的 handle() 函数。

  1. Handler接口
java 复制代码
package com.hillky.desgin_learn.chainResponsibility.array;

public interface IHandler {
    boolean handle();
}
  1. Handler具体实现
java 复制代码
package com.hillky.desgin_learn.chainResponsibility.array;

import javax.xml.ws.handler.Handler;

public class HandlerA implements IHandler {
    @Override
    public boolean handle() {
        boolean handled = false;
        System.out.println("HandlerA 处理");
        return handled;
    }
}


package com.hillky.desgin_learn.chainResponsibility.array;

public class HandlerB implements IHandler {
    @Override
    public boolean handle() {
        boolean handled = false;
        System.out.println("HandlerB 处理");
        return handled;
    }
}
  1. HandlerChain
java 复制代码
package com.hillky.desgin_learn.chainResponsibility.array;

import java.util.ArrayList;
import java.util.List;

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

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

    public void handle() {
        for (IHandler handler : handlers) {
            boolean handled = handler.handle();
            if (handled) {
                break;
            }
        }
    }
}
  1. 测试
java 复制代码
package com.hillky.desgin_learn.chainResponsibility.array;
public class Client {
    public static void main(String[] args) {
        HandlerChain chain = new HandlerChain();
        chain.addHandler(new HandlerA());
        chain.addHandler(new HandlerB());
        chain.handle();
    }
}

源码实现

Servlet Filter

Servlet Filter 是 Java Servlet 规范中定义的组件 ,翻译成中文就是过滤器,它可以实 现对 HTTP 请求的过滤功能,比如鉴权、限流、记录日志、验证参数等等。因为它是 Servlet 规范的一部分,所以,只要是支持 Servlet 的 Web 容器(比如,Tomcat、 Jetty 等),都支持过滤器功能。

可以自定义实现过滤器配置到web中(chatgpt搜索实现)

简单的示例

  1. 创建一个名为 RequestTimingFilter 的 Java 类,实现 javax.servlet.Filter 接口
java 复制代码
package com.hillky.desgin_learn.chainResponsibility.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class RequestTimingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 在请求到达时记录开始时间
        long startTime = System.currentTimeMillis();
        // 将请求传递给责任链上的下一个 Filter 或目标 Servlet
        chain.doFilter(request, response);
        // 在请求处理完成后记录结束时间,并计算耗时
        long endTime = System.currentTimeMillis();
        long timeElapsed = endTime - startTime;

        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String requestURI = httpServletRequest.getRequestURI();
        System.out.println("请求 " + requestURI + " 的访问耗时:" + timeElapsed + " 毫秒");

    }
}
  1. 注册到我们的web工程中(添加注解)
java 复制代码
package com.hillky.desgin_learn;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication
@EnableAspectJAutoProxy
@ServletComponentScan // 扫描Servlet组件
public class DesginLearnApplication {

    public static void main(String[] args) {
        SpringApplication.run(DesginLearnApplication.class, args);
    }

}
package com.hillky.desgin_learn.chainResponsibility.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@WebFilter(urlPatterns = "/*")
public class RequestTimingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 在请求到达时记录开始时间
        long startTime = System.currentTimeMillis();
        // 将请求传递给责任链上的下一个 Filter 或目标 Servlet
        chain.doFilter(request, response);
        // 在请求处理完成后记录结束时间,并计算耗时
        long endTime = System.currentTimeMillis();
        long timeElapsed = endTime - startTime;

        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String requestURI = httpServletRequest.getRequestURI();
        System.out.println("请求 " + requestURI + " 的访问耗时:" + timeElapsed + " 毫秒");

    }
}

Spring Interceptor

刚刚讲了 Servlet Filter,现在我们来讲一个功能上跟它非常类似的东西,Spring Interceptor,翻译成中文就是拦截器。尽管英文单词和中文翻译都不同,但这两者 基本上可以看作一个概念,都用来实现对 HTTP 请求进行拦截处理。

它们不同之处在于,Servlet Filter 是 Servlet 规范的一部分,实现依赖于 Web 容 器。Spring Interceptor 是 Spring MVC 框架的一部分,由 Spring MVC 框架来提 供实现。客户端发送的请求,会先经过 Servlet Filter,然后再经过 Spring Interceptor,最后到达具体的业务代码中。

简单的示例

  1. 定义Interceptor
java 复制代码
public class RequestTimingInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 在请求到达时记录开始时间
        long startTime = System.currentTimeMillis();
        request.setAttribute("startTime", startTime);

        // 返回 true 以将请求传递给责任链上的下一个 Interceptor 或目标 Controller
        return true;
//        return HandlerInterceptor.super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 在请求处理完成后记录结束时间,并计算耗时
        long endTime = System.currentTimeMillis();
        long startTime = (Long) request.getAttribute("startTime");
        long timeElapsed = endTime - startTime;
        String requestURI = request.getRequestURI();
        System.out.println("请求 " + requestURI + " 的访问耗时:" + timeElapsed + "毫秒");
    }
}
  1. 在你的 Spring 配置类中,注册并配置这个 Interceptor
java 复制代码
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new RequestTimingInterceptor());
    }
}
  1. 当你的 Spring 应用收到 HTTP 请求时, RequestTimingInterceptor 将会拦截这个请求,并在请求处理前后记录时间。

应用场景

  1. 日志记录器:在应用程序中,我们可能需要将日志记录到不同的位置,如控制 台、文件、数据库等。我们可以创建一个日志记录器链,每个记录器处理特定级别的日志,然后将请求传递给下一个记录器。这样,可以根据日志级别灵活地记录日志信息。
  2. Web 应用中的过滤器和拦截器:在 Web 应用程序中,我们经常需要对请求进行 预处理和后处理,如身份验证、授权、编码转换、请求日志记录等。过滤器和拦截器就是典型的使用责任链模式的场景,请求和响应在过滤器或拦截器链中依次传递,每个过滤器或拦截器执行特定的任务。
  3. 工作流引擎:在一个工作流引擎中,一个请求可能需要经过多个处理步骤,这些步骤可以看作是一个责任链。每个处理器处理请求的一个部分,然后将请求传递给下一个处理器,直到请求被完全处理。

实践

对于支持 UGC(User Generated Content,用户生成内容)的应用(比如论坛)来 说,用户生成的内容(比如,在论坛中发表的帖子)可能会包含一些敏感词(比如涉 黄、广告、反动等词汇)。针对这个应用场景,我们就可以利用职责链模式来过滤这 些敏感词。 过滤敏感词

在这个应用场景中,我们可以创建一个过滤器链来过滤用户生成的内容。每个过滤器 负责处理一种类型的敏感词,然后将内容传递给下一个过滤器。以下是一个简单的实现示例:

  1. 定义一个过滤器接口
java 复制代码
public interface ContentFilter {
    String filter(String content);
}
  1. 实现不同类型的过滤器,例如涉黄过滤器、广告过滤器和反动过滤器
java 复制代码
package com.hillky.desgin_learn.chainResponsibility.ugc;

public class PornographyFilter implements ContentFilter{

    @Override
    public String filter(String content) {
        // 这里用简单的字符串替换来表示过滤操作,实际应用中需要更复杂的过滤逻辑
        return content.replaceAll("涉黄词汇", "***");
    }

}

package com.hillky.desgin_learn.chainResponsibility.ugc;

// 广告过滤器
public class AdvertisementFilter implements ContentFilter {
    @Override
    public String filter(String content) {
        return content.replaceAll("广告词汇", "***");
    }
}

package com.hillky.desgin_learn.chainResponsibility.ugc;

// 反动过滤器
public class ReactionaryFilter implements ContentFilter {
    @Override
    public String filter(String content) {
        return content.replaceAll("反动词汇", "***");
    }
}
  1. 创建一个过滤器链
java 复制代码
package com.hillky.desgin_learn.chainResponsibility.ugc;

import java.util.ArrayList;
import java.util.List;

public class FilterChain {
    private List<ContentFilter> filters=new ArrayList<>();

    public FilterChain addFilter(ContentFilter filter) {
        filters.add(filter);
        return this;
    }

    public String doFilter(String content) {
        for (ContentFilter filter : filters) {
            content = filter.filter(content);
        }
        return content;
    }
}
  1. 测试
java 复制代码
package com.hillky.desgin_learn.chainResponsibility.ugc;

public class Client {
    public static void main(String[] args) {
        // 创建一个过滤器链
        FilterChain filterChain = new FilterChain();
        filterChain.addFilter(new PornographyFilter())
        .addFilter(new AdvertisementFilter())
        .addFilter(new ReactionaryFilter());
        // 用户生成的内容
        String userContent = "这里有一些涉黄词汇,这里有一些广告词汇,这里有一些反动词汇。";
        // 使用过滤器链处理内容
        String filteredContent = filterChain.doFilter(userContent);
        System.out.println(filteredContent);
    }
}
相关推荐
是小崔啊16 分钟前
开源轮子 - EasyExcel01(核心api)
java·开发语言·开源·excel·阿里巴巴
黄公子学安全25 分钟前
Java的基础概念(一)
java·开发语言·python
liwulin050626 分钟前
【JAVA】Tesseract-OCR截图屏幕指定区域识别0.4.2
java·开发语言·ocr
jackiendsc31 分钟前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
Yuan_o_31 分钟前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端
Oneforlove_twoforjob35 分钟前
【Java基础面试题027】Java的StringBuilder是怎么实现的?
java·开发语言
数据小小爬虫1 小时前
利用Java爬虫获取苏宁易购商品详情
java·开发语言·爬虫
小汤猿人类1 小时前
nacos-服务发现注册
java·开发语言·服务发现
码农爱java1 小时前
设计模式--抽象工厂模式【创建型模式】
java·设计模式·面试·抽象工厂模式·原理·23种设计模式·java 设计模式
2401_854391081 小时前
SSM 架构中 JAVA 网络直播带货查询系统设计与 JSP 有效实现方法
java·开发语言·架构