策略模式
源碼地址
定義
定義了算法族(一組行為),分別封裝起來(封裝實現(xiàn)),讓他們之間可以相互替換(擴展),此模式讓算法的變化(擴展)獨立與使用算法的客戶(解耦);
場景
- Strategy描述一組概念相同卻行為不同(一個接口卻實現(xiàn)不同)的相關類;
- Strategy的使用客戶不應該知道其具體實現(xiàn)(解耦),避免暴露復雜的、與具體策略相關的數(shù)據(jù)結(jié)構(gòu);
- 一個類定義多種行為,避免這些行為以if-else的形式出現(xiàn)在此類中,減少對實現(xiàn)細節(jié)的依賴;
舉例: 在一些外部網(wǎng)關,如銀行網(wǎng)關設計時,因直連模式時會接入多個銀行,這些銀行的具體報文封裝邏輯、解析邏輯、業(yè)務邏輯不同(實現(xiàn)ConcreteStrategy),但其都可抽象為共用的網(wǎng)關處理邏輯(接口Strategy);為減少調(diào)用方對實現(xiàn)的依賴關系、便于接入其他銀行、共用代碼邏輯的復用,可采用策略模式進行設計;
結(jié)構(gòu)
- Strategy
多個類似行為的抽象,Context所依賴的接口
- ConcreteStrategy
Strategy的具體實現(xiàn),為Context提供具體邏輯實現(xiàn)
- Context
上下文(客戶),一個具有多種行為的類,持有strategy引用
- 流程描述
Context 與Strategy關系為一對多,Strategy與ConcreteStrategy關系為一對多
image
推薦搭配
工廠模式、模板方法
代碼實現(xiàn)
- 簡單策略DEMO
測試代碼
public class TransTest extends NnnToolsApplicationTests {
@Autowired
private Trans trans;
@Test
public void testTrans(){
TransDO transDO = new TransDO();
Invocation invocation = new Invocation();
invocation.setBizType("N00001");
invocation.setParam(transDO);
//調(diào)用,外部對內(nèi)部無感知,無依賴
//Trans內(nèi)部只依賴行為接口,不依賴實現(xiàn),可動態(tài)改變其行為實現(xiàn)
trans.transfer(invocation);
}
}
簡單演示動態(tài)行為切換
/**
* @author hanlujun
*/
@Service
public class TransImpl implements Trans {
@Autowired
private Map<String,Validator> validators;
/**
* 交易操作
* @param var1
* @return
*/
@Override
public Result transfer(Invocation var1) {
//**變化的行為**
//獲取對應業(yè)務類型的校驗(如權(quán)限校驗、非空校驗、賬戶校驗等)
Validator validator = validators.get(var1.getBizType());
if(Objects.nonNull(validator)){
//校驗
validator.validation(var1.getParam());
}
//執(zhí)行對應業(yè)務類型的業(yè)務邏輯
return accountOperation(var1).doTransfer(var1);
}
/**
* 測試
*/
public TransOperation accountOperation(Invocation var1){
// **變化的行為**
return SpringContextUtil.getBean(var1.getBizType(), TransOperation.class);
}
}
- 進階策略DEMO
借鑒之前老師的寫法來演示并做了稍微改動,demo簡單描述了策略模式是如何實現(xiàn)可復用、可擴展、可維護的OO思想;
另外此demo仍有很大的優(yōu)化空間,需要大家發(fā)散思維;
測試代碼
/**
* 測試
*/
public class TransTest extends NnnToolsApplicationTests {
@Autowired
private StrategyFactory strategyFactory;
public void testTrans(){
Context context = strategyFactory.makeDecision("N00001");
context.execute("N00001","{}");
}
}
策略接口
/**
* 多個類似行為的抽象,Context所依賴的接口
*/
public interface IStrategy {
Response execute(String code, String jsonBody);
}
策略接口與具體實現(xiàn)結(jié)合并結(jié)合模板方法
/**
* 多個類似行為的抽象,Context所依賴的接口,具體策略有子類實現(xiàn)
*/
@Slf4j
public abstract class AbstractStrategy implements IStrategy{
@Autowired
private Map<String, Validator> validators;
@Override
public Response execute(String code , String jsonBody) {
//獲取對應業(yè)務類型的校驗(如權(quán)限校驗、非空校驗、賬戶校驗等)
Validator validator = validators.get(code);
if(Objects.nonNull(validator)){
//校驗
validator.validation(jsonBody);
}
//執(zhí)行對應業(yè)務類型的業(yè)務邏輯
return doTransfer(code);
}
/**
* 具體實現(xiàn)由子類決定
* @param code
* @return
*/
protected abstract Response doTransfer(String code);
}
策略模式中的客戶Context
/**
* 上下文(客戶),一個具有多種行為的類,持有strategy引用
*/
public class Context {
private IStrategy strategy;
private Context(IStrategy strategy){
this.strategy = strategy;
}
public static Context getInstance(IStrategy strategy){
return new Context(strategy);
}
/**
* 執(zhí)行策略
* @param code
* @param jsonBody
* @return
*/
public Response execute(String code, String jsonBody) {
return strategy.execute(code,jsonBody);
}
}
/**
* 策略工廠
*/
public interface IStrategyFactory {
Context makeDecision(String code);
}
推薦搭配:工廠類
/**
* 策略工廠
*/
@Component
public class StrategyFactory implements IStrategyFactory {
@Override
public Context makeDecision(String code) {
String serviceName =BizTypeEnums.getServiceByCode(code);
IStrategy strategy = SpringContextUtil.getBean(serviceName);
return Context.getInstance(strategy);
}
}