Java设计模式【责任链模式】

news/2024/5/19 14:04:18 标签: java, 设计模式, 责任链模式

一、前言

1.1 背景

  • 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定
  • 不明确指定接收者的情况下,向多个对象中的一个提交一个请求
  • 可动态指定一组对象处理请求

1.2 简介

职责链模式是一种行为型设计模式,它通过将请求的发送者和接收者解耦来实现请求的处理。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

优点

  • 降低系统的耦合度
  • 提高代码的可扩展性和可维护性
  • 具备链式传递处理请求功能,请求发送者无需知晓链路结构,只需等待请求处理结果
  • 链路结构灵活,可以通过改变链路结构动态地新增或删减责任
  • 易于扩展新的请求处理类(节点),符合开闭原则

缺点

  • 责任链太长或者处理时间过长,会影响整体性能
  • 如果节点对象存在循环引用时,会造成死循环,导致系统崩溃

组成部分

  • 抽象处理者(Handler):定义一个处理请求的接口,包含抽象处理方法和一个后继连接
  • 具体处理者(Concrete Handler):实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者
  • 客户类(Client):创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程

二、案例代码

java">package com.qiangesoft.design.behavioral.responsibilitychain;

import java.util.HashMap;
import java.util.Map;

/**
 * 客户类
 * 
 * ps:责任链模式
 */
public class Chain {

    public static void main(String[] args) {
        // 模拟请求
        Request request = new Request();
//        request.setPath("/api/news");
        request.setPath("/user/1");
        Map<String, String> header = new HashMap<>();
        header.put("token", "111111");
        request.setHeader(header);

        // 组装责任链
        Handler handler = getHandlerChain();

        // 提交请求
        handler.doHandler(request);

        System.out.println("***执行了接口方法***");
    }

    /**
     * 组装责任链
     *
     * @return
     */
    private static Handler getHandlerChain() {
        Handler tokenHandler = new TokenHandler();
        Handler authHandler = new AuthHandler();
        Handler logHandler = new LogHandler();
        tokenHandler.setNext(authHandler)
                .setNext(logHandler);
        return tokenHandler;
    }
}

/**
 * 抽象处理者
 */
abstract class Handler {

    protected Handler next;

    public Handler setNext(Handler next) {
        this.next = next;
        return next;
    }

    protected abstract void doHandler(Request request);
}

/**
 * 具体处理者:token校验
 */
class TokenHandler extends Handler {
    @Override
    public void doHandler(Request request) {
        // 有些接口无需登录
        String path = request.getPath();
        if (path.startsWith("/api")) {
            return;
        }

        try {
            // token校验(模拟)
            String token = request.getHeader().get("token");
            if (token == null || "".equals(token)) {
                System.out.println("用户未登录!");
                return;
            }
            if (!"111111".equals(token)) {
                System.out.println("登录已失效!");
                return;
            }
            String username = "admin";
            AuthenticationContextHolder.set(username);
            System.out.println("token校验成功!");

            if (next != null) {
                next.doHandler(request);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            AuthenticationContextHolder.remove();
        }
    }
}

/**
 * 具体处理者:权限校验
 */
class AuthHandler extends Handler {
    @Override
    public void doHandler(Request request) {
        String username = AuthenticationContextHolder.get();
        if (!"admin".equals(username)) {
            System.out.println("当前用户无权限!");
            return;
        }

        System.out.println("权限校验成功!");

        if (next != null) {
            next.doHandler(request);
        }
    }
}

/**
 * 具体处理者:日志记录
 */
class LogHandler extends Handler {
    @Override
    public void doHandler(Request request) {
        String username = AuthenticationContextHolder.get();
        System.out.println("记录日志:请求【xxx接口】 操作人【" + username + "】!");

        if (next != null) {
            next.doHandler(request);
        }
    }
}

/**
 * 模拟request对象
 */
class Request {

    private String path;

    private Map<String, String> header;

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public Map<String, String> getHeader() {
        return header;
    }

    public void setHeader(Map<String, String> header) {
        this.header = header;
    }
}

/**
 * 线程本地变量:用户信息
 */
class AuthenticationContextHolder {
    private static final ThreadLocal<String> CURRENT_USER = new ThreadLocal<>();

    public static String get() {
        return CURRENT_USER.get();
    }

    public static void set(String user) {
        CURRENT_USER.set(user);
    }

    public static void remove() {
        CURRENT_USER.remove();
    }
}

三、总结

应用场景

  • 过滤器(Filter):在Servlet中,过滤器就是使用责任链模式实现的。每个过滤器都可以决定是否处理请求,或者将其转发给下一个过滤器进行处理。
  • 拦截器(Interceptor):在Spring框架中,拦截器就是使用责任链模式实现的。拦截器可以对请求进行预处理或后处理,也可以将请求转发给下一个拦截器进行处理。
  • 异常处理(Exception Handling):在Java中,可以使用责任链模式来处理异常。首先,程序先尝试使用自定义的异常处理器来处理异常,如果该处理器无法处理异常,则将其转发给下一个处理器进行处理。

http://www.niftyadmin.cn/n/5390497.html

相关文章

第十一章 REST 客户端方法 - 插入和更新文档

文章目录 第十一章 REST 客户端方法 - 插入和更新文档插入和更新文档删除文档检索文档 第十一章 REST 客户端方法 - 插入和更新文档 插入和更新文档 SaveDocument&#xff1a;将新文档插入到指定数据库中。 curl -i -X POST -H "Content-Type: application/json" …

力扣代码学习日记五

Problem: 283. 移动零 文章目录 思路解题方法复杂度代码 思路 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,…

C++的string容器->基本概念、构造函数、赋值操作、字符串拼接、查找和替换、字符串比较、字符存取、插入和删除、子串

#include<iostream> using namespace std; #include <string> //string的构造函数 /* -string(); //创建一个空的字符串 例如: string str; -string(const char* s); //使用字符串s初始化 -string(const string& str); //使…

Nest.js权限管理系统开发(二)连接MySQL、Redis

安装MySQL及相关依赖 下载dmg文件安装 前往MySQL :: Download MySQL Community Server下载最新版本的MySQL。 打开系统设置&#xff0c;拉到最下方可以看到MySQL&#xff0c;打开看到两个绿点表示安装成功&#xff0c;也可以在这里修改MySQL密码。 配置环境变量 打开终端配…

【Spring面试题】

目录 前言 1.Spring框架中的单例bean是线程安全的吗? 2.什么是AOP? 3.你们项目中有没有使用到AOP&#xff1f; 4.Spring中的事务是如何实现的&#xff1f; 5.Spring中事务失效的场景有哪些&#xff1f; 6.Spring的bean的生命周期。 7.Spring中的循环引用 8.构造方法…

js图片url反转file文件 vue

场景 由于项目需求&#xff0c;需要将图片 url 转成文件格式上传到服务器 参考博客&#xff1a;图片url转file 封装js transferFile.js function setInitImg(url,callback){let img url;//这里是淘宝上随便找的一张图片let _ thislet imgResgetBase64(img, (dataURL) > {i…

nios ii开发随笔

错误一&#xff1a; d:/intelfpga/17.1/nios2eds/bin/gnu/h-x86_64-mingw32/bin/../lib/gcc/nios2-elf/5.3.0/../../../../../H-x86_64-mingw32/nios2-elf/bin/ld.exe: test.elf section .text will not fit in region ram_oc_xzs d:/intelfpga/17.1/nios2eds/bin/gnu/h-x86_6…

基于微信小程序的垃圾分类系统,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…