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

委派模式

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
	 */
	@Override
	public 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
	 */
	@Override
	public 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());

	@Bean
	public 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 {
   
	@Resource
	private DelegateService service;
	private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

	@Resource
	DelegateManager 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.测试各种类型传值

在这里插入图片描述

在这里插入图片描述

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-02-18 17:08:02       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-02-18 17:08:02       106 阅读
  3. 在Django里面运行非项目文件

    2024-02-18 17:08:02       87 阅读
  4. Python语言-面向对象

    2024-02-18 17:08:02       96 阅读

热门阅读

  1. C++知识点总结(14):桶的排序、冒泡排序

    2024-02-18 17:08:02       48 阅读
  2. WordPress绑定,实现任意域名访问

    2024-02-18 17:08:02       50 阅读
  3. 百万级并发分布式锁

    2024-02-18 17:08:02       52 阅读
  4. 从零学算法33

    2024-02-18 17:08:02       42 阅读
  5. Docker 数据卷

    2024-02-18 17:08:02       41 阅读
  6. 大数据之数据上云的主要优点

    2024-02-18 17:08:02       53 阅读
  7. 寒假作业2月6号

    2024-02-18 17:08:02       53 阅读
  8. LeetCode171. Excel Sheet Column Number

    2024-02-18 17:08:02       50 阅读
  9. 使用 C++23 从零实现 RISC-V 模拟器(5):CSR

    2024-02-18 17:08:02       46 阅读