解锁Spring Boot中的设计模式—03.委派模式:探索【委派模式】的奥秘与应用实践!

委派模式

文章目录

  • 委派模式
    • 1.简述
      • **应用场景**
      • **优缺点**
      • **业务场景示例**
    • 2.类图
    • 3.具体实现
      • 3.1.自定义注解
      • 3.2.定义抽象委派接口
      • 3.3.定义具体执行者
      • 3.4.定义委派者(统一管理委派任务)
      • 3.5.定义委派者管理类
    • 4.测试
      • 4.1.controller层
      • 4.2.测试不同场景
        • 4.2.1.测试生产部门计算费用
        • 4.2.2.测试财务部门计算费用
        • 4.2.3.测试各种类型传值

1.简述

委派模式是一种负责任务的调度和分配模式,类似于代理模式但更注重结果而非过程。它可以被视为一种特殊情况下的静态代理的全权代理,但并不属于GOF 23种设计模式之一,而是归类为行为型模式。

委派模式在Spring框架中广泛应用,其中最常见的例子是DispatcherServlet,它充分利用了委派模式的特性。

在Spring框架中,DispatcherServlet是一个核心组件,负责接收HTTP请求并将其分发给相应的处理器(Controller)进行处理。DispatcherServlet利用了委派模式的特性来实现请求的转发和处理。

具体来说,DispatcherServlet接收到HTTP请求后,根据请求的信息(如URL、请求方法等),会将请求委派给相应的处理器进行处理。这个委派的过程就是典型的委派模式应用。

在Spring中,DispatcherServlet并不直接处理请求,而是通过HandlerMapping确定请求对应的处理器(Controller),然后委派给对应的Controller进行处理。处理完成后,再由DispatcherServlet负责返回响应给客户端。

这种设计使得Spring的Web应用具有良好的灵活性和可扩展性。开发者可以根据自己的需求定制不同的HandlerMapping和Controller,而DispatcherServlet作为统一的入口,负责请求的委派和响应的处理,使得整个Web应用的架构更加清晰和易于维护。

应用场景

委派模式适用于以下场景:

  • 当需要实现表现层与业务层之间的松耦合时,委派模式能够提供一个良好的解决方案。
  • 在需要编排多个服务之间的调用时,特别是像责任链模式这样的情况下,委派模式尤为适用。
  • 当需要再封装一层服务查找和调用时,委派模式也是一种常见的选择。

优缺点

优点

  • 隐藏实现:对内部实现进行了封装,使得调用方不必关心具体的实现细节,增强了系统的安全性和稳定性。
  • 易于扩展:通过委派模式,可以方便地添加新的服务或功能,而不会影响到已有的代码。
  • 简化调用:调用方只需要知道委派对象,而不需要知道具体的执行细节,降低了系统的复杂度和维护成本。

缺点

  • 膨胀和管理难度:与静态代理相似,随着具体执行类和委派类的扩展,代码容易膨胀,难以管理,需要谨慎设计和维护。

业务场景示例

在关务系统中,费用计算是一个关键功能。不同部门可能采用不同的计算方式。为了应对这种多样性,系统需要根据不同部门的要求,动态选择合适的费用计算算法,以确保费用的准确性和合规性。

在这样的场景下,可以借助委派模式来实现动态选择合适的计算算法。下面,我们以Spring Boot版本的委派模式为例来展示其具体实现。

2.类图

在这里插入图片描述

3.具体实现

3.1.自定义注解

import java.lang.annotation.*;@Documented
@Inherited
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Delegate {/*** 执行任务类型*/String type () default "";
}

3.2.定义抽象委派接口

/*** 委派任务通用处理接口*/
public interface DelegateHandler<R,T> {/*** 抽象处理方法* @param t 处理任务参数*/R process(T t);
}

3.3.定义具体执行者

/*** 财务部门委派类* @author 13723* @version 1.0* 2024/1/7 14:52*/
@Delegate(type = "finance")
@Component
public class FinanceDepartmentDelegate implements DelegateHandler<ResultObject, BigDecimal>{private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());/*** 财务部门处理(将数据加上税费)* @param s 处理任务参数* @return*/@Overridepublic ResultObject process(BigDecimal s) {ResultObject instance = ResultObject.createInstance(true);BigDecimal res = BigDecimal.ZERO;if (s != null) {res = s.add(new BigDecimal("300"));instance.setData(res);}instance.setMessage("财务部门处理数据结束!");logger.error("财务部门处理数据结束!处理前:{},处理后:{}",s,res);return instance;}
}/*** 生产部门委派类* @author 13723* @version 1.0* 2024/1/7 14:44*/
@Delegate(type = "product")
@Component
public class ProductionDepartmentDelegate implements DelegateHandler<ResultObject, BigDecimal> {private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());/*** 生产部门处理(将数据重新计算)* @param s 处理任务参数* @return*/@Overridepublic ResultObject process(BigDecimal s) {ResultObject instance = ResultObject.createInstance(true);BigDecimal res = BigDecimal.ZERO;if (s != null) {res = s.multiply(new BigDecimal("1.6"));instance.setData(res);}instance.setMessage("生产部门处理数据结束!");logger.error("生产部门处理数据结束!处理前:{},处理后:{}",s,res);return instance;}
}

3.4.定义委派者(统一管理委派任务)

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.CollectionUtils;import java.lang.invoke.MethodHandles;
import java.util.*;/***  委派角色管理类* @author 13723* @version 1.0* 2024/1/7 14:58*/
@Delegate
public class DelegateManager extends DelegatePatternConfiguration {private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());/*** 存放具体委派角色类型* k-具体委派类型* v-具体任务角色*/private Map<String, List<DelegateHandler>> handlerMap;/*** 存放委派模式中的各种真正执行任务的角色* 分组放* @param iHandlerList*/public void setHandlerMap(List<DelegateHandler> iHandlerList) {handlerMap = scanHandlers(iHandlerList);}/*** 将具体的委派角色 获取进行统一管理* @param handlerList 具体实现委派的类集合* @return 存放委派*/private Map<String, List<DelegateHandler>> scanHandlers(List<DelegateHandler> handlerList) {ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);scanner.addIncludeFilter(new AnnotationTypeFilter(Delegate.class));Map<String, List<DelegateHandler>> handlerMap = new HashMap<>();for (DelegateHandler handler : handlerList) {Class<?> targetClass = handler.getClass();if (hasDutyAnnotation(targetClass)) {String type = getTypeAnnotationValue(targetClass);handlerMap.computeIfAbsent(type, key -> new ArrayList<>()).add(handler);}}return handlerMap;}/*** 判断该类是否是 使用使用了Delegate注解* @param clazz 类名称* @return true使用了*/private boolean hasDutyAnnotation(Class<?> clazz ) {return AnnotationUtils.findAnnotation(clazz, Delegate.class) != null;}/*** 判断使用了的注解@Delegate 是否填写了类型* @param clazz* @return*/private String getTypeAnnotationValue(Class<?> clazz) {Delegate dutyAnnotation = AnnotationUtils.findAnnotation(clazz, Delegate.class);if (dutyAnnotation == null) {throw new IllegalStateException("Delegate annotation not found for class: " + clazz);}return dutyAnnotation.type();}/*** 根据类型 委派具体执行任务的角色* @param type 任务类型* @param t 传递参数* @return 执行后 最终返回结果*/public <R, T> R process(String type, T t) {List<DelegateHandler> handlers = handlerMap.get(type);R result = null;if (!CollectionUtils.isEmpty(handlers)) {// 注意 这里需要考虑多个执行角色问题,遇到多个相同类型执行角色,考虑使用责任链,别再此模式上死磕。for (DelegateHandler<R, T> handler : handlers) {result = handler.process(t);}}else {throw new RuntimeException("no match delegate class !");}return result;}
}

3.5.定义委派者管理类

/*** 将 委派模式管理类 注入到Spring中* @author 13723* @version 1.0* 2024/1/7 15:16*/
@Configuration
public class DelegatePatternConfiguration {private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());@Beanpublic DelegateManager delegateManager(List<DelegateHandler> handlers) {// 创建对象 会获取全部使用注解的类,将其注入到对应map集合中DelegateManager delegateManager = new DelegateManager();delegateManager.setHandlerMap(handlers);return delegateManager;}
}

4.测试

4.1.controller层

import com.hrfan.java_se_base.config.ResultObject;
import com.hrfan.java_se_base.pattern.decorator_pattern.config.DecorateManager;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.util.Objects;/*** 委派模式测试类* @author 13723* @version 1.0* 2024/1/7 15:20*/
@RestController
@RequestMapping("/v1/delegate")
public class DelegateTestController {@Resourceprivate DelegateService service;private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());@ResourceDelegateManager manager;@PostMapping("/test")public ResultObject test(@RequestBody DelegateMoneyParams params){ResultObject instance = ResultObject.createInstance(false);instance.setMessage("计算失败!");Objects.requireNonNull(params);if (StringUtils.isNotBlank(params.getType())){switch (params.getType()){case "a":instance =  manager.process("product", params.getTotal());return instance;case "b":instance =  manager.process("finance", params.getTotal());return instance;default:return instance;}}return instance;}
}

4.2.测试不同场景

4.2.1.测试生产部门计算费用

在这里插入图片描述

4.2.2.测试财务部门计算费用

在这里插入图片描述

4.2.3.测试各种类型传值

在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/474454.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MCU看门狗

目录 一、独立看门狗(IWDG) 1、IWDG 主要作用 2、IWDG 主要特性 3、编程控制 4、注意地方 二、窗口看门狗(WWDG) 1、窗口看门狗作用&#xff1a; 2、窗口看门狗产生复位信号有两个条件&#xff1a; 3、WWDG 框图 4、WWDG 将要复位的时间 5、编程控制 一、独立看门…

跟着pink老师前端入门教程(JavaScript)-day02

三、变量 &#xff08;一&#xff09;变量概述 1、什么是变量 白话&#xff1a;变量就是一个装东西的盒子 通俗&#xff1a;变量是用于存放数据的容器&#xff0c;通过变量名获取数据&#xff0c;甚至数据可以修改 2、变量在内存中的存储 本质&#xff1a;变量是程序在内存…

LeetCode---384周赛

题目列表 3033. 修改矩阵 3034. 匹配模式数组的子数组数目 I 3035. 回文字符串的最大数量 3036. 匹配模式数组的子数组数目 II 一、修改矩阵 简单模拟即可&#xff0c;代码如下 class Solution { public:vector<vector<int>> modifiedMatrix(vector<vecto…

SpringCloud-Nacos集群搭建

本文详细介绍了如何在SpringCloud环境中搭建Nacos集群&#xff0c;为读者提供了一份清晰而详尽的指南。通过逐步演示每个关键步骤&#xff0c;包括安装、配置以及Nginx的负载均衡设置&#xff0c;读者能够轻松理解并操作整个搭建过程。 一、Nacos集群示意图 Nacos&#xff0…

VS中设置#define _CRT_SECURE_NO_WARNINGS的原因和设置方式

原因&#xff1a; 在编译老的用C语言的开源项目的时候&#xff0c;可能因为一些老的.c文件使用了strcpy,scanf等不安全的函数&#xff0c;而报警告和错误&#xff0c;而导致无法编译通过。 解决方案&#xff1a; 我们有两种解决方案&#xff1a; 1、在指定的源文件的开头定…

20-k8s中pod的调度-nodeSelector节点选择器

一、概念 我们先创建一个普通的deploy资源&#xff0c;设置为10个副本 [rootk8s231 dns]# cat deploy.yaml apiVersion: apps/v1 kind: Deployment metadata: name: dm01 spec: replicas: 10 selector: matchLabels: k8s: k8s template: metadata: …

C 练习实例23-打印菱形

题目&#xff1a;打印出如下图案&#xff08;菱形&#xff09;。 * *** ***** ******* ***** *** * 题目分析&#xff1a; 先打印前4行&#xff0c;因为是递增关系。 第0行&#xff1a;打印3个空格&#xff0c;1个* 第1行&#xff1a;打印2个空格&#xff0c;3个*…

(每日持续更新)jdk api之ObjectInputStream.GetField基础、应用、实战

博主18年的互联网软件开发经验&#xff0c;从一名程序员小白逐步成为了一名架构师&#xff0c;我想通过平台将经验分享给大家&#xff0c;因此博主每天会在各个大牛网站点赞量超高的博客等寻找该技术栈的资料结合自己的经验&#xff0c;晚上进行用心精简、整理、总结、定稿&…

云计算基础-华为存储实验

存储配置流程 创建硬盘域&#xff08;CK&#xff0c;包括热备CK&#xff09;创建存储池&#xff08;CKG、Extent&#xff09;创建LUN、LUN组、将LUN加入LUN组创建主机、主机组、将主机加入主机组创建映射视图&#xff08;LUN组和主机组的映射&#xff09; 1. 创建CK 创建硬盘…

yolov8源码解读Detect层

yolov8源码解读Detect层 Detect层解读网络各层解读及detect层后的处理 关于网络的backbone,head&#xff0c;以及detect层后处理&#xff0c;可以参考文章结尾博主的文章。 Detect层解读 先贴一下全部代码,下面一一解读。 class Detect(nn.Module):"""YOLOv8 …

“挖矿”系列:细说Python、conda 和 pip 之间的关系

继续挖矿&#xff0c;挖“金矿”&#xff01; 1. Python、conda 和 pip&#xff08;挖“金矿”工具&#xff09; Python、conda 和 pip 是在现代数据科学和软件开发中常用的工具&#xff0c;它们各自有不同的作用&#xff0c;但相互之间存在密切的关系&#xff1a; Python&…

数据库MySQL中出现乱码和表格不对齐怎么解决

MySQL中出现乱码问题及解决办法&#xff1a; 情况类似&#xff1a; 首先进入到数据库中&#xff0c;命令&#xff1a;mysql -h localhost -uroot -p或者mysql -uroot -p;进入数据库后选择一个你的数据库查看表中的中文是否乱码 以上是数据库中表格出现乱码情况&#xff0c;原…