SourceCode Java Code

Java策略模式工具类

Posted on 2021-11-09,8 min read

在业务代码中,经常会写策略调度器,还有自动注入策略的方法。极大的降低了开发的效率。所以用Java的反射功能来实现一个策略模式的工具类。

代码

  • 定义一个策略接口。

    package cn.gloduck.common.strategy;
    
    /**
     * 策略接口,无实际作用。
     * 所有的自定义策略接口必须继承此类
     *
     * @author Gloduck
     * @date 2021/11/09
     */
    public interface Strategy {
    }
    
  • 在定义一个注解,用于表明当前类是一个策略。

    package cn.gloduck.common.strategy;
    
    import java.lang.annotation.*;
    
    /**
     * 策略组件
     *
     * @author Gloduck
     * @date 2021/11/09
     */
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface StrategyComponent {
        /**
         * 策略接口的class
         * @return class
         */
        Class<? extends Strategy> strategyInterface();
    
        /**
         * 策略名称,单个策略接口名称不能有重复的
         * @return 策略名称
         */
        String name();
    }
    
  • 策略的调度器,用户获取具体的策略。这里使用泛型来定义。

    package cn.gloduck.common.strategy;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 存放单个策略的调度器
     *
     * @author Gloduck
     * @date 2021/11/09
     */
    public class StrategyContext<T> {
        private final Map<String, T> strategyMap;
    
        StrategyContext() {
            this.strategyMap = new HashMap<>(16);
        }
    
        StrategyContext(int size) {
            int cap = size / 3 * 4 + 1;
            this.strategyMap = new HashMap<>(cap);
        }
    
        /**
         * 注册策略
         *
         * @return boolean
         */
        void registerStrategy(String name, T strategy) {
            this.strategyMap.put(name, strategy);
        }
    
        /**
         * 获取策略
         *
         * @param name 策略名称
         * @return {@link T}
         */
        public T getStrategy(String name) {
            return strategyMap.get(name);
        }
    
        /**
         * 策略数量
         *
         * @return int
         */
        public int size() {
            return strategyMap.size();
        }
    
    }
    
  • 最后是一个容器,存储了所有的策略。

    package cn.gloduck.common.strategy;
    
    import cn.gloduck.common.exception.strategy.InvalidStrategyException;
    import cn.gloduck.common.exception.strategy.NoStrategyFoundException;
    import cn.hutool.core.util.ClassUtil;
    import cn.hutool.core.util.ReflectUtil;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    
    /**
     * 策略工厂,存放所有的策略
     * 实现一个策略首先先定义一个策略接口[需要继承{@link Strategy}接口],然后写策略实现类。策略实现类必须使用{@link StrategyComponent}注解,并且{@link StrategyComponent#strategyInterface()}需要和定义的策略接口对应
     *
     * @author Gloduck
     * @date 2021/11/09
     */
    public class StrategyFactory {
        private final Map<Class<? extends Strategy>, StrategyContext> strategyContextMap;
        private static final StrategyFactory INSTANCE = new StrategyFactory();
    
        private StrategyFactory() {
            this.strategyContextMap = new HashMap<>();
        }
    
        /**
         * 注册策略
         *
         * @param strategyInterface 策略接口
         * @param strategyName      策略名称
         * @param strategy          策略
         */
        public <K extends Strategy, V extends K> void registerStrategy(Class<K> strategyInterface, String strategyName, V strategy) {
            if (strategyInterface == null || strategy == null) {
                throw new InvalidStrategyException("策略相关的类不能为空");
            }
            if (!strategyInterface.isInterface()) {
                throw new InvalidStrategyException("类【" + strategyInterface.getName() + "】不是接口");
            }
            doRegister(strategyInterface, strategyName, strategy);
        }
    
    
        /**
         * 注册策略
         *
         * @param strategyInterface 策略接口
         * @param strategyName      策略名称
         * @param strategy          策略
         */
        private <T extends Strategy> void registerScannedStrategy(Class<T> strategyInterface, String strategyName, Object strategy) {
            Class<?>[] interfaces = strategy.getClass().getInterfaces();
            boolean hasInterface = false;
            for (Class<?> anInterface : interfaces) {
                // 检查是否实现了StrategyComponent中定义的接口
                if (strategyInterface.equals(anInterface)) {
                    hasInterface = true;
                    break;
                }
            }
            if (!hasInterface) {
                throw new InvalidStrategyException("注册策略失败,请检查类【" + strategy.getClass().getName() + "】是否实现了StrategyComponent注解中定义的【" + strategyInterface.getName() + "】接口");
            }
            doRegister(strategyInterface, strategyName, strategy);
        }
    
        /**
         * 执行注册
         * 调用此方法请优先对strategy进行合法性检验,确保是strategyInterface的实现类
         *
         * @param strategyInterface 策略接口
         * @param strategyName      策略名称
         * @param strategy          策略
         */
        @SuppressWarnings("unchecked")
        private <T extends Strategy> void doRegister(Class<T> strategyInterface, String strategyName, Object strategy) {
            if (!strategyContextMap.containsKey(strategyInterface)) {
                // 不存在则添加
                StrategyContext<T> context = new StrategyContext<>();
                strategyContextMap.put(strategyInterface, context);
            }
            StrategyContext<T> context = strategyContextMap.get(strategyInterface);
            context.registerStrategy(strategyName, (T) strategy);
        }
    
    
        /**
         * 获取策略
         *
         * @param strategyInterface 策略接口
         * @return {@link T}
         */
        @SuppressWarnings("unchecked")
        public <T extends Strategy> StrategyContext<T> getStrategyContext(Class<T> strategyInterface) {
            StrategyContext<T> strategyContext = strategyContextMap.get(strategyInterface);
            if (strategyContext == null || strategyContext.size() == 0) {
                throw new NoStrategyFoundException("无法找到对应的策略【" + strategyInterface.getName() + "】");
            }
            return strategyContext;
        }
    
    
        /**
         * 扫描包并注册策略
         *
         * @param basePackage 基本包
         */
        public void scanPackageAndRegisterStrategy(String basePackage) {
            // 获取所有带StrategyComponent注解的类
            Set<Class<?>> scanPackage = ClassUtil.scanPackage(basePackage, tClass -> {
                boolean accept = false;
                try {
                    accept = tClass.getAnnotation(StrategyComponent.class) != null;
                } catch (Throwable e) {
                }
                return accept;
            });
            scanPackage.forEach(tClass -> {
                // 获取注解
                StrategyComponent strategyComponent = tClass.getAnnotation(StrategyComponent.class);
                Class<? extends Strategy> strategyInterface = strategyComponent.strategyInterface();
                registerScannedStrategy(strategyInterface, strategyComponent.name(), createInstance(tClass));
            });
        }
    
        /**
         * 扫描包并注册策略
         * 直接空扫描会导致性能损耗,建议使用{@link StrategyFactory#scanPackageAndRegisterStrategy(String)}
         */
        @Deprecated
        public void scanPackageAndRegisterStrategy() {
            scanPackageAndRegisterStrategy("");
        }
    
        /**
         * 反色调用无参构造函数创建对象
         *
         * @param tClass 类
         * @return {@link T}
         */
        private <T> T createInstance(Class<T> tClass) {
            return ReflectUtil.newInstance(tClass);
        }
    
        /**
         * 获取实例
         *
         * @return {@link StrategyFactory}
         */
        public static StrategyFactory instance() {
            return INSTANCE;
        }
    }
    

使用

使用流程

  • 定义一个策略的接口,然后这个接口继承Strategy类。
  • 然后写一些策略实现这个策略接口,并且使用StrategyComponent注解。
  • 最后将策略注册到容器中。通过调度器来使用。

测试代码

  • 这里定义了两个完全不同的策略,可以理解为在业务中这两个策略是完全没有关系的。如果不使用工具的话要为这些策略没一个都写一个调度器。使用了工具的话则不需要,添加其他的策略模式扩展会方便很多。
import cn.gloduck.common.strategy.Strategy;
import cn.gloduck.common.strategy.StrategyComponent;
import cn.gloduck.common.strategy.StrategyContext;
import cn.gloduck.common.strategy.StrategyFactory;

public class TestClass {
    public static void main(String[] args) {
        /*
         * 这里定义了两个策略模式,一个ServiceStrategy一个ControllerStrategy。后续也可以引入更多策略。
         */
        // 将所有的策略注册到容器中,这里推荐带参数的扫描方式,防止扫描的类太多了影响性能
        StrategyFactory.instance().scanPackageAndRegisterStrategy();
        // 获取第一个策略模式的调度器
        StrategyContext<ServiceStrategy> serviceStrategyStrategyContext = StrategyFactory.instance().getStrategyContext(ServiceStrategy.class);
        // 获取并调用第一个策略模式中的策略
        ServiceStrategy strategyA = serviceStrategyStrategyContext.getStrategy("strategyA");
        strategyA.doSomething();

        // 获取第二个策略模式的调度器
        StrategyContext<ControllerStrategy> controllerStrategyStrategyContext = StrategyFactory.instance().getStrategyContext(ControllerStrategy.class);
        // 获取并调用第二个策略模式中的策略
        ControllerStrategy strategyB = controllerStrategyStrategyContext.getStrategy("strategyB");
        strategyB.doRoute();
    }

    public interface ServiceStrategy extends Strategy{
        void doSomething();
    }
    @StrategyComponent(strategyInterface = ServiceStrategy.class, name = "strategyA")
    public static class ServiceStrategyA implements ServiceStrategy{

        @Override
        public void doSomething() {
            System.out.println("这是业务策略1");
        }
    }
    @StrategyComponent(strategyInterface = ServiceStrategy.class, name = "strategyB")
    public static class ServiceStrategyB implements ServiceStrategy{

        @Override
        public void doSomething() {
            System.out.println("这是业务策略1");
        }
    }
    public interface ControllerStrategy extends Strategy{
        void doRoute();
    }
    @StrategyComponent(strategyInterface = ControllerStrategy.class, name = "strategyA")
    public static class ControllerStrategyA implements ControllerStrategy{

        @Override
        public void doRoute() {
            System.out.println("这是控制器策略1");
        }
    }
    @StrategyComponent(strategyInterface = ControllerStrategy.class, name = "strategyB")
    public static class ControllerStrategyB implements ControllerStrategy{

        @Override
        public void doRoute() {
            System.out.println("这是控制器策略2");
        }
    }
}
  • 运行结果

    这是业务策略1
    这是控制器策略2
    

下一篇: 记一次Uniaccess卸载→

loading...