在SpringBoot中實現策略模式
首先定義一個Strategy
介面來表示一個策略:
public interface Strategy {
String flag();
void process();
}
其中flag
方法返回當前策略的唯一標識,process
則是該策略的具體執行邏輯。
下面是Strategy
介面的兩個實現類:
public class StrategyImpl1 implements Strategy { @Override public String flag() { return "s1"; } @Override public void process() { System.out.println("strategy 1"); } } public class StrategyImpl2 implements Strategy { @Override public String flag() { return "s2"; } @Override public void process() { System.out.println("strategy 2"); } }
然後定義一個StrategyRunner
介面用來表示策略的排程器:
public interface StrategyRunner {
void run(String flag);
}
run
方法內部通過判斷flag
的值來決定具體執行哪一個策略。
下面是一個簡單的StrategyRunner
:
public class StrategyRunnerImpl implements StrategyRunner { private static final List<Strategy> STRATEGIES = Arrays.asList(new StrategyImpl1(), new StrategyImpl2()); private static final Map<String, Strategy> STRATEGY_MAP; static { STRATEGY_MAP = STRATEGIES.stream() .collect(Collectors.toMap(Strategy::flag, s -> s)); } @Override public void run(String flag) { STRATEGY_MAP.get(flag).process(); } }
在StrategyRunnerImpl
內部,定義了一個STRATEGIES
列表來儲存所有Strategy
實現類的例項,以及一個叫做STRATEGY_MAP
的Map
來儲存flag
和Strategy
例項之間的對應關係,static
塊中的程式碼用於從STRATEGIES
列表構造STRATEGY_MAP
。這樣,在run
方法中就可以很方便地獲取到指定flag
的Strategy
例項。
這個實現雖然簡單,但是它有個很大的缺點,想象一下,如果我們想新增新的Strategy
實現類,我們不僅需要新增新的實現類,還要修改STRATEGIES
列表的定義。這樣就違反了“對擴充套件開放,對修改關閉”的原則。
藉助於Spring的IOC容器和SpringBoot的自動配置,我們可以以一種更加優雅的方式實現上述策略模式。
首先,我們繼續使用StrategyImpl1
和StrategyImpl2
這兩個實現類。不過,為了將它們註冊進Spring的IOC容器,需要給他們標註上Component
註解:
@Component
public class StrategyImpl1 implements Strategy {
...
}
@Component
public class StrategyImpl2 implements Strategy {
...
}
然後,寫一個StrategyConfig
配置類,用於向容器中註冊一個StrategyRunner
:
@Configuration
public class StrategyConfig {
@Bean
public StrategyRunner strategyRunner(List<Strategy> strategies) {
Map<String, Strategy> strategyMap = strategies.stream()
.collect(Collectors.toMap(Strategy::flag, s -> s));
return flag -> strategyMap.get(flag).process();
}
}
仔細看strategyRunner
方法的實現,不難發現,其中的邏輯與之前的StrategyRunnerImpl
幾乎完全相同,也是根據一個List<Strategy>
來構造一個Map<String, Strategy>
。只不過,這裡的strategies
列表不是我們自己構造的,而是通過方法引數傳進來的。由於strategyRunner
標註了Bean
註解,因此引數上的List<Strategy>
實際上是在SpringBoot初始化過程中從容器獲取的(還記得之前我們把兩個Strategy
的實現類註冊進了容器嗎)。
這樣,我們再也無需操心繫統中一共有多少個Strategy
實現類,因為SpringBoot的自動配置會幫我們把它們全部收集起來。我們只需編寫自己的Strategy
實現類,然後將它註冊進容器,並在任何需要的地方注入StrategyRunner
:
@Autowired
private StrategyRunner strategyRunner;
然後直接使用strategyRunner
就行了:
strategyRunner.run("s1");
strategyRunner.run("s2");
控制檯輸出如下:
strategy 1
strategy 2
也就是說,當我們想新增新的Strategy
實現類時,我們只需新增新的程式碼,而無需修改任何現有的程式碼,這樣就完美地實現了“對擴充套件開放,對修改關閉”的目標。