前言
策略模式是開發中常用的一種設計模式,主要解決在有多種算法相似的情況下,使用 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;來決定調用哪個實現類,這種適用於某一種場景的調用,無需聚合所有調用場景,如果想動態實現,推薦上面三種。策略模式的枚舉類也可以用註解的方式實現,這裏有興趣的同學可以自己嘗試一下。另外有其他更好的實現方式也可以在下方留言,大家一起學習。