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

发布于:2025-08-15 ⋅ 阅读:(15) ⋅ 点赞:(0)

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
     */
    @Override
    public void updateFence(IUpdateFenceVO fence) {
    	sout("执行了围栏服务具体实现类1");
    }
}

/**
 * 围栏服务具体实现类2
 */
@Slf4j
@Service
@RequiredArgsConstructor
//这个没有使用自定义注解,@SysPlatform 用于无法识别是,走这个通用实现
@Primary
public class FenceServiceImpl  implements IFenceService {
	/**
     * 更新围栏
     *
     * @param fence
     */
    @Override
    public void updateFence(IUpdateFenceVO fence) {
    	sout("执行了围栏服务具体实现类2");
    }
}
/**
 * 围栏服务具体实现类3
 */
@Slf4j
@Service
//这个是自定义注解,标记这个实现类是这个平台的标识,靠这个注解来识别执行具体的实现类
@SysPlatform(from = SysPlatformConstant.SysPlatformEnum.ELDER_PLATFORM)
public class ElderFenceServiceImpl implements IFenceService {
	/**
     * 更新围栏
     *
     * @param fence
     */
    @Override
    public 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
     */
    @PostConstruct
    public 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 {

	 @Override
    public void pushData(List<HealthPushVO> oCardCallBackVO, Long tenantId) {
    
    }
}

/**
 * 实现父类的具体方法
 **/
@Slf4j
//指定实现类的名称,这个名称和枚举上的名称要一致,不然无法识别
@Component("ZF161Push")
@RequiredArgsConstructor
public class ZF161Producer implements BaseMqProducer {

	 @Override
    public void pushData(List<HealthPushVO> oCardCallBackVO, Long tenantId) {
    
    }
}

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到