文章首發在公眾號(龍臺的技術筆記),之後同步到部落格園和個人網站:xiaomage.info
今天和大家聊一聊,如何合理的將多種設計模式放到同一個業務場景中
業務背景
最近接到一個認證的需求,C 端使用者在購買公司保險時,需要先進行 實名認證確認身份
為了保證業務複用,單獨將認證的邏輯拆分為微服務模組
C 端使用者下單購買保險的邏輯大致如下
先說下關於認證相關的一些基本知識。簡單來說,你如何證明你是你自己
一些雲服務廠商都會有關於驗證身份的付費介面,接下來我們就以騰訊雲姓名、身份證二要素認證為參考進行舉例
說完認證知識,我們再來拆解下使用者購買保險的步驟
- 使用者在前端發起認證行為
- 請求經過閘道器呼叫保險服務,保險服務呼叫認證服務
- 認證服務呼叫騰訊雲認證付費 API,返回認證結果資訊
認證流程
在整個塊認證流程中,我們會講解三種設計模式,按照順序分別是策略、責任鏈、模板模式
策略模式
定義一組演算法類,將每個演算法分別封裝起來,讓它們可以互相替換。策略模式使這些演算法在客戶端呼叫它們的時候能夠互不影響地變化,客戶端代指使用演算法的程式碼
我們拿認證來說,定義一個認證介面,然後實現二、三、四要素以及人臉識別實現;將這些實現類放到一個 Map 容器中,並和業務規定好對應的標識 Key,通過標識 Key 獲取對應的認證策略實現
如果真的像上面這麼簡單,if-else 判斷加上拆解幾個認證函式就可以搞得定,還真的不一定需要策略模式
我們再延伸來看一種複雜場景:假設後續不滿足於騰訊雲的認證,為了保證可用性以及更多的流量,需要對接更多的認證平臺
可用性:平臺的介面不太可能保證全年百分百可用,需要有容災降級或者替換方案
更多的流量:騰訊雲認證介面限流 100次 / S
這個時候策略模式的優點就體現出來了,簡化程式碼的複雜性 以及 保證開閉原則,增加程式的健壯性以及可擴充套件性
後續再增加三方認證平臺和認證方式,都不需要改動原有邏輯,新增對應實現即可
責任鏈模式
在責任鏈模式中,多個處理器(參照攔截器)依次處理同一個請求。一個請求先經過 A 處理器處理,然後再把請求傳遞給 B 處理器,B 處理器處理完後再傳遞給 C 處理器,以此類推,形成一個鏈條,鏈條上的每個處理器 各自承擔各自的處理職責
這裡主要將責任鏈模式應用於,規避無意義呼叫三方認證服務
- 已認證過的人員資訊,在有效期內沒必要再次呼叫
- 呼叫認證結果錯誤,依然會扣錢,比如說名稱中包含非中文,身份證格式錯誤等等
我們可以將處理器儘量職責單一,方便後續其它認證方式的 複用和編排
模板方法
模板方法模式在一個方法中定義一個 演算法骨架,並將某些步驟推遲到 子類中實現。模板方法模式可以讓子類在 不改變演算法整體結構的情況下,重新定義演算法中的某些步驟
模版方法主要作用:複用性 和 擴充套件性
- 複用性:核心思想就是 父級定義公共實現,由子級進行調取使用
- 擴充套件性:在不修改方法邏輯的前提下,變更其中的某些步驟
通俗來講 : 定義一個抽象類 AbstractTemplate
,並定義一個或若干抽象方法 abstractMethod
。程式碼大致如下:
public abstract class AbstractAuthenticationService<T extends AuthenticationRequest> {
void before(T request) {
}
void after(T request) {
}
// 抽象方法
protected abstract void practicalExecute(T request);
public void authentication(T request) {
// 前置攔截操作,包括不限於責任鏈模式呼叫
before(request);
// 策略模式實現,呼叫具體認證類,比如二要素認證或三要素認證
practicalExecute(request);
// 資源清理或記錄認證完成資訊
after(request);
}
騰訊雲二要素認證實現類,程式碼如下:
@Slf4j
@Component
@RequiredArgsConstructor
// BaseAuthenticationStrategy 是策略模式實現,定義了 mark、execute 方法
public class NameIdCardAuthenticationByTencentResolver extends AbstractAuthenticationService<NameIdCardAuthenticationReqDTO>
implements BaseAuthenticationStrategy<NameIdCardAuthenticationReqDTO> {
private static final String SUCCESS = "0";
// 責任鏈容器
private final NameIdCardHandlerChain nameIdCardHandlerChain;
@Override
public String mark() {
return AuthenticationEnum.TENCENT.name();
}
@Override
public void execute(NameIdCardAuthenticationReqDTO request) {
authentication(request);
}
@Override
public void before(NameIdCardAuthenticationReqDTO request) {
// 責任鏈呼叫
nameIdCardHandlerChain.doFilter(request);
}
@Override
public void practicalExecute(NameIdCardAuthenticationReqDTO request) {
// 騰訊雲二要素認證具體行為
}
}
最後總結
丟擲一個老生常談的問題,學習設計模式有什麼作用?
設計模式主要是為了應對 程式碼的複雜性,讓其滿足 開閉原則,提高程式碼的 擴充套件性;合適的場景合理運用的設計模式,可以幫助程式碼實現 高內聚、低耦合 等的優點
你無法決定別人的程式碼,但你可以決定自己的。時間充足的情況下,儘量以重構的方式去寫每一行程式碼
最後希望小夥伴讀過文章後有所收穫,祝好。