当前位置: 首页 > news >正文

【java实现一个接口多个实现类通用策略模式】

java实现同个接口多个实现类通用策略模式

项目业务中,有多个平台,多个平台直接有相同的业务,只有一个接口入口,但是
不同的平台入口,虽然接口相同,参数相同,但是各自的具体实现不同,唯一能区分来源的
是请求头中有标识平台的字段:from
最简单的方法是对from进行判断,
if(from==1){//执行平台1的方法
}
if(from==2){//执行平台2的方法
}
......
虽然这样也能实现,但是明显多余,且不好扩展,我们只需要关注与具体业务实现即可
通过from标识,来自动判断执行具体的实现方法
思路:
1、定义个通用的策略实现方法,spingboot注入的时候扫描到哪些类是需要多实现的
2、需要多实现的每个实现类标记下平台的自定义注解,用于识别from具体执行的类

策略父类

定义父类,用于多实现的类能被扫描到
/*** 基础策略,需要进行策略分发的接口类,就要继承这个接口* 方便统一注入管理*/
public interface BaseStrategy {}/*** 所属平台注解* 用于标识类所属平台*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface SysPlatform {/*** 所属平台,自定义的平台枚举,根据自己业务来,可以是 123 数字等,不一定是要枚举* 枚举好管理*/SysPlatformConstant.SysPlatformEnum from() default SysPlatformConstant.SysPlatformEnum.ALL;}/*** 围栏服务接口,提供围栏相关的操作功能* 继承基础策略**/
public interface IFenceService  extends BaseStrategy {/*** 更新围栏信息** @param fence 围栏更新信息*/void updateFence(IUpdateFenceVO fence);
}/*** 围栏服务具体实现类1* 实现IFenceService**/
@Slf4j
@Service
@RequiredArgsConstructor
//这个是自定义注解,标记这个实现类是这个平台的标识,靠这个注解来识别执行具体的实现类
@SysPlatform(from = SysPlatformConstant.SysPlatformEnum.AGRICULTURE_PLATFORM)
public class AgrFenceServiceImpl implements IFenceService {/*** 更新围栏** @param fence*/@Overridepublic void updateFence(IUpdateFenceVO fence) {sout("执行了围栏服务具体实现类1");}
}/*** 围栏服务具体实现类2*/
@Slf4j
@Service
@RequiredArgsConstructor
//这个没有使用自定义注解,@SysPlatform 用于无法识别是,走这个通用实现
@Primary
public class FenceServiceImpl  implements IFenceService {/*** 更新围栏** @param fence*/@Overridepublic void updateFence(IUpdateFenceVO fence) {sout("执行了围栏服务具体实现类2");}
}
/*** 围栏服务具体实现类3*/
@Slf4j
@Service
//这个是自定义注解,标记这个实现类是这个平台的标识,靠这个注解来识别执行具体的实现类
@SysPlatform(from = SysPlatformConstant.SysPlatformEnum.ELDER_PLATFORM)
public class ElderFenceServiceImpl implements IFenceService {/*** 更新围栏** @param fence*/@Overridepublic void updateFence(IUpdateFenceVO fence) {sout("执行了围栏服务具体实现类3");}
}

策略实现具体服务上下文代码

/*** 基本策略上下文,用于具体分发接口到哪个实现类,执行多实现的具体方法*/
@Component
@RequiredArgsConstructor
@Slf4j
public class BaseStrategyContext {/*** 所有产品策略集合,spring自动注入*/@Autowired(required = false)private List<BaseStrategy> productStrategyList;private static BaseStrategyContext baseStrategyContext;/*** 初始化时已全部注入,赋值给静态变量baseStrategyContext*/@PostConstructpublic void init() {baseStrategyContext = this;baseStrategyContext.productStrategyList = this.productStrategyList;}/*** 选择产品策略** @param parent 来源* @return 对应服务层策略*/public static <T> T build(Class<T> parent) {// 接收到请求,记录请求内容HttpServletRequest request = WebUtil.getRequest();String fromType = request.getHeader(SecurityConstant.OLD_FROM);return getRunServiceClass(parent, fromType);}/*** 选择产品策略** @param parent   接口类,由该类来获取实现该接口的全部子类* @param fromType 来源* @return 对应服务层策略*/public <T> T create(Class<T> parent, String fromType) {return getRunServiceClass(parent, fromType);}private static <T> T getRunServiceClass(Class<T> parent, String fromType) {SysPlatformConstant.UserFromEnum byFromType = SysPlatformConstant.UserFromEnum.getByFromType(fromType);List<Class<?>> subClasses = findSubClasses(parent);T productStrategy = null;for (Class<?> subClass : subClasses) {//获取类上所属平台的注解,有指定平台就按指定实现走,没有就是通用if (subClass.getAnnotation(SysPlatform.class) != null) {SysPlatform annotation = subClass.getAnnotation(SysPlatform.class);SysPlatformConstant.SysPlatformEnum from = annotation.from();if (from.getPlatformId().equals(byFromType.getPlatformEnum().getPlatformId())) {productStrategy = (T) SpringUtil.getBean(subClass);break;}} else {productStrategy = (T) SpringUtil.getBean(subClass);}}AssertUtil.notNull(productStrategy, CustomReturnEnum.NOT_ALLOW_OPERATION);return productStrategy;}/*** 根据传进来的父类,获取所有子类** @param parent* @return*/private static List<Class<?>> findSubClasses(Class<?> parent) {List<Class<?>> subclasses = new ArrayList<>();// 假设我们知道所有类的名称for (BaseStrategy t : baseStrategyContext.productStrategyList) {try {Class<?> clazz = t.getClass();if (parent.isAssignableFrom(clazz) && !clazz.equals(parent)) {subclasses.add(clazz);}} catch (Exception e) {log.error("基础策略上下文错误:{}", ExceptionUtil.getStackStr(e));}}return subclasses;}}

具体的实现方法示例

方法1,用BaseStrategyContext 的create方法//controller层 注入 基本策略上下文private final BaseStrategyContext baseStrategyContext;/*** 查询步数** @param iStepStatisticVO* @return*/@GetMapping("/listStepNum")public OStepMiniappStaticsVO listStepNum(IStepStatisticVO iStepStatisticVO) {//具体当前的来源String from = UserUtil.getFrom();//StepService.class 这个就是要继承BaseStrategy的接口类return baseStrategyContext.create(StepService.class, from).listStepNum(iStepStatisticVO);}----------------------------------------------------------------------------------方法2,用BaseStrategyContext 的build 静态方法,
使用静态方法,controller层不用注入基本策略上下文,可以直接使用/*** 修改围栏信息** @param fence*/@PostMapping("/update")public void updateById(@RequestBody @Valid IUpdateFenceVO fence) {//IFenceService.class 这个就是要继承BaseStrategy的接口类//updateFence 是IFenceService 这个接口里面需要定义的实现方法BaseStrategyContext.build(IFenceService.class).updateFence(fence);}

在这里插入图片描述

通过枚举的方式也能多实现不同的方式

业务背景:相同的数据,但是不同的客户,具体的实现是不一样的,通过客户代码去区分,走不同的具体实现
    /*** MQ监听到的批量推送数据** @param resultList*/private void pushData(List<HealthPushVO> resultList) {//根据租户进行分组Map<Long, List<HealthPushVO>> pushMap = resultList.stream().collect(Collectors.groupingBy(HealthPushVO::getTenantId));pushMap.forEach((tenantId, value) -> {//根据租户获取对应的具体实现类ListenerTableEnum customPush = ListenerTableEnum.getByValue(tenantId.toString());BaseMqProducer producer = SpringUtil.getBean(customPush.getServiceName());producer.healthPush(value, tenantId);});}
/*** 客户配置枚举类**/
@Getter
@AllArgsConstructor
public enum ListenerTableEnum {ZF160("ZF160", "ZF160Push", "ZF160"),ZF161("ZF161", "ZF161Push", "ZF161"),...................public static ListenerTableEnum getByValue(String type) {if (type == null) {return ZF160;}for (ListenerTableEnum val : values()) {if (val.customCode.equals(type)) {return val;}}return ZF160;}/*** 客户代码*/private final String customCode;/*** 服务映射名称*/private final String serviceName;/*** 租户名称*/private final String customName;
}/*** 父类**/
public interface BaseMqProducer {/*** 推送数据** @param oCardCallBackVOS* @param tenantId* @return*/void pushData(List<HealthPushVO> oCardCallBackVOS, Long tenantId);
}/*** 实现父类的具体方法**/
@Slf4j
//指定实现类的名称,这个名称和枚举上的名称要一致,不然无法识别
@Component("ZF160Push")
@RequiredArgsConstructor
public class ZF160Producer implements BaseMqProducer {@Overridepublic void pushData(List<HealthPushVO> oCardCallBackVO, Long tenantId) {}
}/*** 实现父类的具体方法**/
@Slf4j
//指定实现类的名称,这个名称和枚举上的名称要一致,不然无法识别
@Component("ZF161Push")
@RequiredArgsConstructor
public class ZF161Producer implements BaseMqProducer {@Overridepublic void pushData(List<HealthPushVO> oCardCallBackVO, Long tenantId) {}
}

在这里插入图片描述

http://www.dtcms.com/a/330482.html

相关文章:

  • [Oracle数据库] ORACLE基本DML操作
  • 【软件测试】自动化测试 — selenium快速上手
  • Java设计模式之《策略模式》
  • STM32L051C8与STM32L151C8的主要区别
  • visual studio调试cmake项目记录
  • 用飞算JavaAI一键生成电商平台项目:从需求到落地的高效实践
  • 远程影音访问:通过 cpolar 内网穿透服务使用 LibreTV
  • Mybatis学习笔记(九)
  • Spring Boot + Redis + 布隆过滤器防止缓存穿透
  • [已解决]当启动 Spring Boot 应用时出现 Using generated security password xxx提示
  • OpenCV 视频处理全解析
  • EI学术会议 | 可再生能源、智能电网、电力系统优化、能源存储技术
  • Linux系统Namespace隔离实战:dd/mkfs/mount/unshare命令组合应用
  • 缓存元数据损坏操作步骤(lvmcache修复)
  • 微软推出AI恶意软件检测智能体 Project Ire
  • 截断重要性采样(TIS)在医疗AI大模型训练中的优化路径
  • 嵌入式领域,ROM和RAM的区别
  • pytorch学习笔记-Loss的使用、在神经网络中加入Loss、优化器(optimizer)的使用
  • 基于SpringBoot+Vue的轻手工创意分享平台(WebSocket即时通讯、协同过滤算法、Echarts图形化分析)
  • 依托AR远程协助,沟通协作,高效流畅
  • 七、SpringBoot工程日志设置
  • [前端算法]动态规划
  • 【保姆级教程】CentOS 7 部署 FastDFS 全流程(避坑指南)
  • 【Docker】安装kafka案例
  • 深入解析 Spring IOC 容器在 Web 环境中的启动机制
  • ActiveReports 19.1 Crack
  • 新手向:Python条件语句(if-elif-else)使用指南
  • 初识HTML
  • 云原生俱乐部-k8s知识点归纳(1)
  • AI 编程实践:用 Trae 快速开发 HTML 贪吃蛇游戏