首先定義一個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
實現類時,我們只需添加新的代碼,而無需修改任何現有的代碼,這樣就完美地實現了“對擴展開放,對修改關閉”的目標。