策略模式獲取算法族(行爲)的方式

今天總結一下策略模式使用過程當中獲取算法族(行爲)的方式,內容純屬我的觀點,歡迎指正。java

1、策略模式的場景及難點

一、策略模式的定義及說明在此就不提了,沒有基礎的推薦你們一本書《Head First 設計模式》,圖文及場景的選擇讓讀者很容易理解。
我的的使用場景:算法

項目中有多個執行器,每一個執行器執行不一樣的業務處理,但願將這些執行器作統一的入口,根據入口的行爲參數決定使用哪一個執行器去執行相應的任務,在策略模式的使用中,我的以爲這也是難點所在,想到的作法有三種,經過Spring getBean、Java的反射以及另外一種在getBean的基礎上增長Spring Annotation,下面逐一介紹三種方式的使用。

2、策略模式獲取處理類的方式

一、Spring getBean

a.執行器的入口

/**
 * @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();
    }
}

b.建立具體的執行策略

一、經過@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));
    }

}

c.執行器接口

/**
 * @author xiaokaige
 */
public interface ExecutorStrategyInterface {
    /**
     * 執行器接口
     */
    void doExecutorAction();

}

d.執行器接口的實現

/**
 * @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.");
    }
}

e.請求接口

請求接口能夠看到控制檯輸出:I entered through Strategy B.服務器

clipboard.png

補充一點,此方式也可以使用@Service("別名"),此時applicationContext.getBeansOfType(ExecutorStrategyInterface.class)拿到的是「別名」app

二、Java反射

這種方式很是簡單,不須要策略工廠類,直接在調用的時候經過全類名及方法名利用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

clipboard.png

三、Spring Annotation

a.定義 @Annotation

首先經過@interface Annotation{ } 定義一個註解 @Annotation設計

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExecutorStrategyAnnotation {
    /**
     * 執行器id,默認值1
     */
    int executorId() default 1;
}

b.爲實現ExecutorStrategyInterface接口的類添加一個Id

/**
 * @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;
}

c.執行器的入口

這裏建立策略的參數有執行器的名稱換爲策略的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();
    }
}

d.策略工廠獲實現

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類無變化

e.運行程序控制臺輸出:

clipboard.png

到此三種方式介紹結束。

3、總結

第一種方式調用方只須要提供執行器的名稱,還能夠經過別名靈活配置,可是要實現策略工廠。第二種經過java反射最爲簡單,不須要策略工廠類,可是參數須要提供類名及方法名,須要設置兩個參數。第三種方式與第一種相似,只是多了Id的映射,適合的場景與第一種方式不一樣。我的建議使用第一種,具體看你們的使用了。

相關文章
相關標籤/搜索