委派模式
文章目录
- 委派模式
- 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
*/
@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;
}
}