在策略模式(Strategy Pattern)中筒严,一個類的行為或其算法可以在運行時更改。這種類型的設(shè)計模式屬于行為型模式情萤。
業(yè)務(wù)背景
商場搞活動鸭蛙,根據(jù)客戶購買商品的金額,收費時給與不同的打折筋岛,比如娶视,購買 金額>=2000 的打八折(0.8),金額 500 ~ 1000 的睁宰,打九折(0.9)肪获,購買金額 0 ~ 500 的九五折(0.95),根據(jù)不同的金額走不同計算策略邏輯柒傻。
- 首先定義一個Strategy接口來表示一個策略:
public interface Strategy {
/**
* 采用策略
*/
String strategy();
/**
* 計算方法邏輯
*/
void algorithm();
}
其中strategy方法返回當(dāng)前策略的唯一標(biāo)識孝赫,algorithm則是該策略的具體執(zhí)行的計算邏輯。
下面是Strategy接口的三個實現(xiàn)類:
public class ConcreteStrategyA implements Strategy {
@Override
public String strategy() {
return StrategySelector.strategyA.getStrategy();
}
@Override
public void algorithm() {
System.out.println("process with strategyA...");
}
}
public class ConcreteStrategyB implements Strategy {
@Override
public String strategy() {
return StrategySelector.strategyB.getStrategy();
}
@Override
public void algorithm() {
System.out.println("process with strategyB...");
}
}
public class ConcreteStrategyC implements Strategy {
@Override
public String strategy() {
return StrategySelector.strategyC.getStrategy();
}
@Override
public void algorithm() {
System.out.println("process with strategyC...");
}
}
- 自定義策略選擇枚舉StrategySelector:
@Getter
public enum StrategySelector {
strategyA(1,"strategyA"),
strategyB(2,"strategyB"),
strategyC(3,"strategyC");
private Integer code;
private String strategy;
StrategySelector(Integer code, String strategy) {
this.code = code;
this.strategy = strategy;
}
}
- 然后定義一個StrategyRunner接口用來表示策略的調(diào)度器:
public interface StrategyRunner {
void execute(String strategy);
}
execute方法內(nèi)部通過判斷strategy的值來決定具體執(zhí)行哪一個策略诅愚。
public class StrategyRunnerImpl implements StrategyRunner {
private static final List<Strategy> STRATEGIES = Arrays.asList(new ConcreteStrategyA(), new ConcreteStrategyB(), new ConcreteStrategyC());
private static Map<String, Strategy> STRATEGY_MAP = Maps.newHashMap();
static {
STRATEGY_MAP = STRATEGIES.stream().collect(Collectors.toMap(Strategy::strategy, s -> s));
}
@Override
public void execute(String strategy) {
STRATEGY_MAP.get(strategy).algorithm();
}
}
在StrategyRunnerImpl內(nèi)部寒锚,定義了一個STRATEGIES列表來保存所有Strategy實現(xiàn)類的實例劫映,以及一個叫做STRATEGY_MAP的Map來保存strategy和Strategy實例之間的對應(yīng)關(guān)系违孝,static塊中的代碼用于從STRATEGIES列表構(gòu)造STRATEGY_MAP。
這樣泳赋,在execute方法中就可以很方便地獲取到指定strategy的Strategy實例雌桑。
SpringBoot項目中實現(xiàn)并運用策略模式
@Component
public class ConcreteStrategyA implements Strategy {
@Override
public String strategy() {
return StrategySelector.strategyA.getStrategy();
}
@Override
public void algorithm() {
System.out.println("process with strategyA...");
}
}
@Component
public class ConcreteStrategyB implements Strategy {
@Override
public String strategy() {
return StrategySelector.strategyB.getStrategy();
}
@Override
public void algorithm() {
System.out.println("process with strategyB...");
}
}
@Component
public class ConcreteStrategyC implements Strategy {
@Override
public String strategy() {
return StrategySelector.strategyC.getStrategy();
}
@Override
public void algorithm() {
System.out.println("process with strategyC...");
}
}
然后,定義一個StrategyConfig配置類祖今,用于向容器注入一個StrategyRunner:
@Configuration
public class StrategyConfig {
@Bean
public StrategyRunner runner(List<Strategy> strategies) {
Map<String, Strategy> strategyMap = strategies.stream().collect(Collectors.toMap(Strategy::strategy, s -> s));
return flag -> strategyMap.get(flag).algorithm();
}
}
不難發(fā)現(xiàn)校坑,strategyRunner方法的實現(xiàn),其中的邏輯與之前的StrategyRunnerImpl幾乎完全相同千诬,也是根據(jù)一個List<Strategy>來構(gòu)造一個Map<String, Strategy>耍目。只不過,這里的strategies列表不是我們自己構(gòu)造的徐绑,而是通過方法參數(shù)傳進(jìn)來的邪驮。由于strategyRunner標(biāo)注了Bean注解,因此參數(shù)上的List<Strategy>實際上是在Spring Boot初始化過程中從容器獲取的傲茄,所以我們之前向容器中注冊的那兩個實現(xiàn)類會在這里被注入毅访。
這樣沮榜,我們再也無需操心系統(tǒng)中一共有多少個Strategy實現(xiàn)類,因為Spring Boot的自動配置會幫我們自動發(fā)現(xiàn)所有實現(xiàn)類喻粹。我們只需編寫自己的Strategy實現(xiàn)類蟆融,然后將它注冊進(jìn)容器,并在任何需要的地方注入StrategyRunner:
@Autowired private StrategyRunner strategyRunner;
然后直接使用strategyRunner就行了:
@RestController
@RequestMapping(value = "/designPatterns")
public class DesignPatternController {
@Autowired
private StrategyRunner strategyRunner;
@GetMapping(value = "/algorithm")
public void algorithm(@RequestParam("strategy") String strategy) {
strategyRunner.execute(strategy);
}
}
訪問:
http://localhost:8080/designPatterns/algorithm 控制臺輸出如下:
process with strategyA...
類似的業(yè)務(wù)場景守呜,完全可以結(jié)合業(yè)務(wù)通過方面的代碼來進(jìn)行改造實現(xiàn)型酥,非常實用~