今天總結一下策略模式使用過程當中獲取算法族(行爲)的方式,內容純屬我的觀點,歡迎指正。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的映射,適合的場景與第一種方式不一樣。我的建議使用第一種,具體看你們的使用了。