今天总结一下策略模式使用过程当中获取算法族(行为)的方式,内容纯属我的观点,欢迎指正。java
一、策略模式的定义及说明在此就不提了,没有基础的推荐你们一本书《Head First 设计模式》,图文及场景的选择让读者很容易理解。
我的的使用场景:算法
项目中有多个执行器,每一个执行器执行不一样的业务处理,但愿将这些执行器作统一的入口,根据入口的行为参数决定使用哪一个执行器去执行相应的任务,在策略模式的使用中,我的以为这也是难点所在,想到的作法有三种,经过Spring getBean、Java的反射以及另外一种在getBean的基础上增长Spring Annotation,下面逐一介绍三种方式的使用。
/** * @author xiaokaige */ @RestController @RequestMapping(value = "/test") public class TestController { @Autowired private ExecutorStrategyFactory strategyFactory; @GetMapping("strategy") public void strategy(){ //建立具体的执行策略,并执行为 ExecutorStrategyInterface strategy = strategyFactory.createStrategy("executorB"); strategy.doExecutorAction(); } }
一、经过@PostConstruct修饰init(),在服务器加载Servle的时候运行,而且只会被服务器执行一次。
二、经过applicationContext.getBeansOfType(ExecutorStrategyInterface.class)获取全部实现ExecutorStrategyInterface的Bean,将其放入内存EXECUTOR_BEANS。
三、遍历实现ExecutorStrategyInterface的Bean并返回与参数对应的实例。设计模式
/** * @author xiaokaige */ @Component public class ExecutorStrategyFactory { private static final Map<String,ExecutorStrategyInterface> EXECUTOR_BEANS = Maps.newConcurrentMap(); @Autowired private ApplicationContext applicationContext; /** * 根据不一样的执行器建立不一样的策略 * @return */ public ExecutorStrategyInterface createStrategy(String executor) { Optional<ExecutorStrategyInterface> strategyOptional = EXECUTOR_BEANS .entrySet() .stream() .map(e -> { if (Objects.equals(e.getKey(),executor)) { return e.getValue(); } return null; }).filter(Objects::nonNull).findAny(); if(strategyOptional.isPresent()){ System.out.println(strategyOptional.get()); return strategyOptional.get(); } throw new RuntimeException("策略得到失败"); } /** * 初始化策略列表 */ @PostConstruct private void init() { EXECUTOR_BEANS.putAll(applicationContext.getBeansOfType(ExecutorStrategyInterface.class)); } }
/** * @author xiaokaige */ public interface ExecutorStrategyInterface { /** * 执行器接口 */ void doExecutorAction(); }
/** * @author xiaokaige */ @Slf4j @Service public class ExecutorA implements ExecutorStrategyInterface { @Override public void doExecutorAction() { log.info("I entered through Strategy A."); } }
/** * @author xiaokaige */ @Slf4j @Service public class ExecutorB implements ExecutorStrategyInterface { @Override public void doExecutorAction() { log.info("I entered through Strategy B."); } }
请求接口能够看到控制台输出:I entered through Strategy B.服务器
补充一点,此方式也可以使用@Service("别名"),此时applicationContext.getBeansOfType(ExecutorStrategyInterface.class)拿到的是“别名”app
这种方式很是简单,不须要策略工厂类,直接在调用的时候经过全类名及方法名利用java反射调用方法就行了。ide
/** * @author xiaokaige */ @RestController @RequestMapping(value = "/test") public class TestController { @GetMapping("strategy") public void strategy(){ try { Class c = Class.forName("com.xiaokaige.demo.strategy.ExecutorB"); Method m = c.getMethod("doExecutorAction"); m.invoke(c.newInstance()); } catch (Exception e) { e.printStackTrace(); } } }
控制台输出结果以下:spa
首先经过@interface Annotation{ } 定义一个注解 @Annotation设计
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface ExecutorStrategyAnnotation { /** * 执行器id,默认值1 */ int executorId() default 1; }
/** * @author xiaokaige */ @Slf4j @Service @ExecutorStrategyAnnotation(executorId = ExecutorStrategyConstants.A) public class ExecutorA implements ExecutorStrategyInterface { @Override public void doExecutorAction() { log.info("I entered through Strategy A."); } }
/** * @author xiaokaige */ @Slf4j @Service @ExecutorStrategyAnnotation(executorId = ExecutorStrategyConstants.B) public class ExecutorB implements ExecutorStrategyInterface { @Override public void doExecutorAction() { log.info("I entered through Strategy B."); } }
/** * @author xiaokaige */ public class ExecutorStrategyConstants { public static final int B = 2; public static final int A = 3; public static final int C =4; }
这里建立策略的参数有执行器的名称换为策略的IDcode
/** * @author xiaokaige */ @RestController @RequestMapping(value = "/test") public class TestController { @Autowired private ExecutorStrategyFactory strategyFactory; @GetMapping("strategy") public void strategy(){ //建立具体的执行策略,并执行为 ExecutorStrategyInterface strategy = strategyFactory.createStrategy(3); strategy.doExecutorAction(); } }
ExecutorStrategyInterface的每一个Bean的Id,返回与传参对应的策略blog
/** * @author xiaokaige */ @Component public class ExecutorStrategyFactory { private static final Map<String,ExecutorStrategyInterface> EXECUTOR_BEANS = Maps.newConcurrentMap(); @Autowired private ApplicationContext applicationContext; /** * 根据不一样的执行器建立不一样的策略 * @return */ public ExecutorStrategyInterface createStrategy(int executor) { Optional<ExecutorStrategyInterface> strategyOptional = EXECUTOR_BEANS .entrySet() .stream() .map(e -> { ExecutorStrategyAnnotation validExecutor = e.getValue().getClass().getDeclaredAnnotation(ExecutorStrategyAnnotation.class); if (Objects.equals(validExecutor.executorId(),executor)) { return e.getValue(); } return null; }).filter(Objects::nonNull).findFirst(); if(strategyOptional.isPresent()){ System.out.println(strategyOptional.get()); return strategyOptional.get(); } throw new RuntimeException("策略得到失败"); } /** * 初始化策略列表 */ @PostConstruct private void init() { EXECUTOR_BEANS.putAll(applicationContext.getBeansOfType(ExecutorStrategyInterface.class)); } }
ExecutorStrategyInterface类无变化
到此三种方式介绍结束。
第一种方式调用方只须要提供执行器的名称,还能够经过别名灵活配置,可是要实现策略工厂。第二种经过java反射最为简单,不须要策略工厂类,可是参数须要提供类名及方法名,须要设置两个参数。第三种方式与第一种相似,只是多了Id的映射,适合的场景与第一种方式不一样。我的建议使用第一种,具体看你们的使用了。