?一、背景
在軟體開發中常常遇到這種情況,實作某一個功能有多種演算法或者策略,我們可以根據環境或者條件的不同選擇不同的演算法或者策略來完成該功能,策略模式(Strategy Pattern)定義了一組策略,分別在不同類中封裝起來,每種策略都可以根據當前場景相互替換,從而使策略的變化可以獨立于操作者,比如我們去某個地方,會根據目標地的距離或者手頭經濟情況,來選擇不同的出行方式(共享單車、公交、滴滴、高鐵、飛機等),這些出行方式即是不同的策略,
二、使用時機
了解完背景我們對策略模式心里有個底了,核心是把不同的策略封裝起來,在不同的場景把該策略拎出來,額,這咋一看不就是if…else…的邏輯么,既然這樣,我那我們就來看下我們最初學代碼的那個場景,
if (conditionA) {
logicA
} else if (conditionB) {
logicB
} else if (conditionC) {
logicC
} else {
logicD
}
當你開始學的時候,問題不大,能跑就行,當你有了一定經驗以后,這段代碼明顯違反了OOP的兩個基本原則:
- 單一職責原則(SPR,Single Responsibility Principle):一個類或者模塊只負責完成一個職責或者功能,
- 開閉原則(OCP,Open Closed Principle):軟體物體(模塊、類、方法等)應該”對擴展開放、對修改關閉“,
因為違反了這兩個原則,當if-else塊中的代碼量比較大時,后續的代碼會變得越來越難以維護,而且不小心就改出問題,到時候等著背鍋吧,當然你會問,老周,那我咋樣才能避免寫這樣的代碼啊!額,根據老周的經驗,當你就兩層左右,可以這樣寫,不要做過度設計;三層以上,但每層代碼行比較少的話,可以使用衛陳述句;當三層以上并且每層代碼量比較多時,則需要使用策略模式了,
三、最佳實踐
1、需求
比如我們模型訓練平臺,之前只是模型輸入的提交,后面業務方還希望支持在線預覽表單提交以及系結dubbo服務提交給其它平臺,
這樣的話咱們的表單提交就要回應業務方支持下面三種提交型別:
- 模型輸入的提交
- 在線預覽表單提交
- 系結dubbo服務提交
鐵子們,現在我們就來最佳實踐一波~
2、定義策略介面
- 獲取策略型別
- 處理策略邏輯
/**
* 表單提交處理器
* @param <R> 業務值
*/
public interface FormSubmitHandler<R extends Serializable> {
/**
* 獲得提交型別
* @return 提交型別
*/
String getSubmitType();
?
/**
* 處理表單提交請求
* @param request 請求
* @return 回應,left:為回傳給前端的提示資訊,right:為業務值
*/
CommonPairResponse<String, R> handleSubmit(FormSubmitRequest request);
}
/**
* 表單提交的請求
*/
@Getter
@Setter
public class FormSubmitRequest {
/**
* 提交型別
*/
private String submitType;
?
/**
* 用戶id
*/
private Long userId;
?
/**
* 表單提交的資料
*/
private Map<String, Object> formInput;
}
其中,FormSubmitHandler 的 getSubmitType 方法用來獲取表單的提交型別(即策略型別),用于根據客戶端傳遞的引數直接獲取到對應的策略實作;客戶端傳遞的相關引數都被封裝為 FormSubmitRequest,傳遞給 handleSubmit 進行處理,
3、相關策略實作
模型輸入的提交:
@Slf4j
@Component
public class ModelSubmitHandler implements FormSubmitHandler<Serializable> {
public String getSubmitType() {
return "model";
}
?
public CommonPairResponse<String, Serializable> handleSubmit(FormSubmitRequest request) {
log.info("模型提交:userId={}, formInput={}", request.getUserId(), request.getFormInput());
// 模型創建成功后獲得模型的 id
Long modelId = createModel(request);
return CommonPairResponse.success("模型提交成功!", modelId);
}
?
private Long createModel(FormSubmitRequest request) { // 創建模型的邏輯
return 123L;
}
}
在線預覽表單提交:
@Slf4j
@Component
public class OnlinePreviewSubmitHandler implements FormSubmitHandler<Serializable> {
public String getSubmitType() {
return "online preview";
}
?
public CommonPairResponse<String, Serializable> handleSubmit(FormSubmitRequest request) {
log.info("在線預覽提交:userId={}, formInput={}", request.getUserId(), request.getFormInput());
return CommonPairResponse.success("在線預覽模式提交資料成功!", null);
}
}
系結dubbo服務提交:
@Slf4j
@Component
public class DubboSubmitHandler implements FormSubmitHandler<Serializable> {
public String getSubmitType() {
return "dubbo";
}
?
public CommonPairResponse<String, Serializable> handleSubmit(FormSubmitRequest request) {
log.info("dubbo模式提交:userId={}, formInput={}", request.getUserId(), request.getFormInput());
// 進行dubbo呼叫,獲得業務方回傳的提示資訊和業務資料
CommonPairResponse<String, Serializable> response = dubboSubmitData(request);
return response;
}
}
4、建立策略的簡單工廠
@Component
public class FormSubmitHandlerFactory implements InitializingBean, ApplicationContextAware {
private static final Map<String, FormSubmitHandler<Serializable>> FORM_SUBMIT_HANDLER_MAP = new HashMap<>();
?
private ApplicationContext applicationContext;
?
/**
* 根據提交型別獲取對應的處理器
* @param submitType 提交型別
* @return 提交型別對應的處理器
*/
public FormSubmitHandler<Serializable> getHandler(String submitType) {
return FORM_SUBMIT_HANDLER_MAP.get(submitType);
}
?
public void afterPropertiesSet() throws Exception {
// 將 Spring 容器中所有的 FormSubmitHandler 注冊到 FORM_SUBMIT_HANDLER_MAP
applicationContext.getBeansOfType(FormSubmitHandler.class).values().forEach(
handler -> FORM_SUBMIT_HANDLER_MAP.put(handler.getSubmitType(), handler)
);
}
?
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
我們讓 FormSubmitHandlerFactory 實作 InitializingBean 介面,在 afterPropertiesSet 方法中,基于 Spring 容器將所有 FormSubmitHandler 自動注冊到 FORM_SUBMIT_HANDLER_MAP,從而 Spring 容器啟動完成后, getHandler 方法可以直接通過 submitType 來獲取對應的表單提交處理器,
Factory 只負責獲取 Handler,Handler 只負責處理具體的提交,Service 只負責邏輯編排, 從而達到功能上的 “低耦合高內聚”,
有木有感覺到,整個流程如絲滑般流暢~
5、假設擴展
如果業務方又需要加入一種提交策略,比如新增鉤子函式提交,這對我們來說就相當easy了,我們只需要添加新的策略實作即可,
@Slf4j
@Component
public class HookSubmitHandler implements FormSubmitHandler<Serializable> {
public String getSubmitType() {
return "hook";
}
?
public CommonPairResponse<String, Serializable> handleSubmit(FormSubmitRequest request) {
log.info("hook鉤子函式提交:userId={}, formInput={}", request.getUserId(), request.getFormInput());
// 進行 hook 函式呼叫,并獲得業務方回傳的提示資訊和業務資料
CommonPairResponse<String, Serializable> response = hookSubmitData(request);
return response;
}
}
此時不需要修改任何代碼,因為Spring容器重啟時會自動將HookSubmitHandler 注冊到 FormSubmitHandlerFactory 中,后續業務方又要加其它策略的話,直接加策略實作,也不需要改動之前的代碼,“低耦合高內聚”簡直不要太香,
歡迎大家關注我的公眾號【老周聊架構】,Java后端主流技術堆疊的原理、原始碼分析、架構以及各種互聯網高并發、高性能、高可用的解決方案,

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/264137.html
標籤:其他
上一篇:理解VUE雙向資料系結原理和實作
