java設計模式之策略模式的優雅實現

前言

策略模式是開發中常用的一種設計模式,主要解決在有多種算法相似的情況下,使用 if...else 所帶來的複雜和難以維護的問題。看了網上很多關於策略模式的上下文切換類實現都不甚優雅,故而想總結分享一篇自我感覺比較優雅的處理方式,方便大家一起學習。

方式一:使用@PostConstruct初始化到map中

該方式是比較常用的,相對比較優雅,能滿足動態獲取不同實現類的功能。廢話不多說直接上代碼。

策略類:

public interface Strategy {
    /**
     * 策略方法
     */
    void method();
}

 

策略實現類:

@Service(value = "strategyA")
public class StrategyA implements Strategy {
    /**
     * 策略方法
     */
    @Override
    public void method() {
        System.out.println("我是A策略實現方案!");
    }
}


@Service(value = "strategyB")
public class StrategyB implements Strategy {
    /**
     * 策略方法
     */
    @Override
    public void method() {
        System.out.println("我是B策略實現方案!");
    }

@Service(value = "strategyC")
public class StrategyC implements Strategy {
    /**
     * 策略方法
     */
    @Override
    public void method() {
        System.out.println("我是C策略實現方案!");
    }
}

枚舉類:

public enum StrategyEnum {
    /**
     * 策略枚舉類
     */
    STRATEGY_A(1, "strategyA"),
    STRATEGY_B(2, "strategyB"),
    STRATEGY_C(3, "strategyC");

    private Integer code;

    private String name;

    StrategyEnum(Integer code, String name) {
        this.code = code;
        this.name = name;
    }

    public static StrategyEnum getByCode(Integer code) {
        StrategyEnum[] values = StrategyEnum.values();
        for (StrategyEnum strategyEnum : values) {
            if (strategyEnum.getCode().equals(code)) {
                return strategyEnum;
            }
        }
        return null;
    }

    /**
     * Getter method for property <tt>code</tt>.
     *
     * @return property value of code
     */
    public Integer getCode() {
        return code;
    }

    /**
     * Getter method for property <tt>name</tt>.
     *
     * @return property value of name
     */
    public String getName() {
        return name;
    }
}

上下文切換類:

@Component
public class StrategyContext {
    private final Map<String, Strategy> strategyMap = new ConcurrentHashMap<>();
    @Autowired
    private ApplicationContext applicationContext;

    @PostConstruct
    private void init() {
        strategyMap.putAll(applicationContext.getBeansOfType(Strategy.class));
    }

    
    public Strategy getInstance(Integer code) {
        String beanName = StrategyEnum.getByCode(code).getName();
        return this.getInstanceByBeanName(beanName);
    }

    private Strategy getInstanceByBeanName(String beanName) {
        if (!StringUtils.isEmpty(beanName)) {
            return strategyMap.get(beanName);
        }
        return null;
    }

}

測試:

@RestController
public class StrategyController {

    @Autowired
    private StrategyContext context;

    @RequestMapping("/method")
    public String method() {
        return context.getInstance(code).method();
    }

測試結果:

 

方式二:使用@Autowired初始化到map中

原因是:@Autowired 註釋中提到In case of a java.util.Collection or java.util.Map dependency type, the container will autowire all beans matching the declared value type. In case of a Map, the keys must be declared as type String and will be resolved to the corresponding bean names.


意思是:以java.util.Collection 或java.util.Map 爲例。映射依賴項類型,容器將自動連接所有與聲明值類型匹配的bean。對於映射,鍵必須聲明爲類型String,並將解析爲相應的bean名稱。

具體實現如下:

@Component
public class StrategyContext {
    private final Map<String, Strategy> strategyMap = new ConcurrentHashMap<>();
    

    @Autowired
    public StrategyContext(Map<String, Strategy> strategyMap) {
        this.strategyMap.clear();
        strategyMap.forEach((k, v) -> this.strategyMap.put(k, v));
    }

    public Strategy getInstance(Integer code) {
        String beanName = StrategyEnum.getByCode(code).getName();
        return this.getInstanceByBeanName(beanName);
    }

    private Strategy getInstanceByBeanName(String beanName) {
        if (!StringUtils.isEmpty(beanName)) {
            return strategyMap.get(beanName);
        }
        return null;
    }
}

測試結果同方式一,這裏就不演示了。

方式三:使用ApplicationContext

這是交給spring上下文容器去管理,我們自己不需要再做實現了。實現方式如下:
@Component
public class StrategyContext {
    
    @Autowired
    private ApplicationContext applicationContext;

    public Strategy getInstance(Integer code) {
        String beanName = StrategyEnum.getByCode(code).getName();
        return this.getInstanceByBeanName(beanName);
    }

    

    private Strategy getInstanceByBeanName(String beanName) {
        if (!StringUtils.isEmpty(beanName)) {
            return (Strategy) applicationContext.getBean(beanName);
        }
        return null;
    }


}

其實這種方式和第二種差不多,第二種也是藉助spring,但是不同點是未進行緩存,從beanFactory中獲取。測試結果同方式一,這裏不再演示了。

總結:

這三種動態獲取策略實現類的方法,個人覺得大同小異,都是通過實現類beanName實現動態的效果,其實還有一種比較簡單的方式就是通過註解@Qualifier(value = "strategyA")+@Autowired private Strategy strategy;來決定調用哪個實現類,這種適用於某一種場景的調用,無需聚合所有調用場景,如果想動態實現,推薦上面三種。策略模式的枚舉類也可以用註解的方式實現,這裏有興趣的同學可以自己嘗試一下。另外有其他更好的實現方式也可以在下方留言,大家一起學習。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章