如何搭建一個系統(tǒng)?
首先要學(xué)會搭建一個系統(tǒng)贸辈,你需要先成為一個優(yōu)秀的工程師肠槽。一個優(yōu)秀的工程師應(yīng)該【具備扎實(shí)的基礎(chǔ)能力】、【書寫高質(zhì)量代碼】秸仙、【擁有良好的風(fēng)險控制能力】以及【具備一定的測試基礎(chǔ)】。
(1)席吴,系統(tǒng)架構(gòu)
1)捞蛋,評價一個優(yōu)秀的系統(tǒng)的指標(biāo)
- 代碼具備一定的可讀性、擴(kuò)展性拟杉。 => 也使得代碼的干凈,不混亂啼染。 => 這里混亂的定義是相對的焕梅,也就是說,如果你自己按照一定的邏輯封裝的贞言,那么對于自己而言是清晰的;但是如果這個代碼給到其他的人看弟蚀,在不清楚你的思路的情況下會存在一些問題(
變量名任意起酗失、方法一坨等
),所以面對可讀性的挑戰(zhàn)规肴,我們就可以通過良好的結(jié)構(gòu)去管理這些復(fù)雜性夜畴,我們把這個結(jié)構(gòu)稱之為【系統(tǒng)架構(gòu)】贪绘。 - 代碼的具備的一定的健壯性央碟。
- 在排查問題的時候也容易定位。
2)亿虽,用發(fā)展的眼觀去看待一個功能的設(shè)計
ps:不僅僅用發(fā)展的眼光去看待一個功能的設(shè)計,其實(shí)編碼更像一種“救世主”模式粘秆,你通過代碼實(shí)現(xiàn)的一個個功能,就是你的一個個的作品翻擒,而于這些作品而言牛哺,它的好壞完全取決于你,基于換位思考的原則巩趁。
A01:示例偽代碼
假設(shè)我們有一個http接口,需要返回用戶的信息议慰。用戶信息包括:用戶昵稱奴曙、用戶vip等級、用戶標(biāo)簽洽糟、用戶余額、余額歷史以來用戶最貴一次消費(fèi)拍霜。
public Result<?> getUserInfo(String userId){
log.info("打印方法入口日志");
log.info("校驗(yàn)入?yún)?);
try{
log.info("ServiceA -> 獲取用戶基礎(chǔ)信息");
log.info("ServiceB -> 獲取用戶特殊信息");
// log.info("ServiceC -> 獲取當(dāng)前用戶充值的總金額");
log.info("ServiceC -> 獲取當(dāng)前用戶余額");
log.info("ServiceD -> 獲取用戶的消費(fèi)記錄");
log.info("利用strem流的方式獲取最近一次最貴的消費(fèi)");
}catch(Exception ex){
log.error("捕獲異常日志 -> ", ex);
}
log.info("打印訪問成功出口日志");
return ResultGenerator.genSuccessResult("返回接口處理結(jié)果");
}
分析上述代碼中存在的問題:
-
業(yè)務(wù)變動 => 如果新增一個接口的話薪介?
- 通用日志模塊是存在優(yōu)化空間的,比如打印方法入?yún)⒌劳怠⒊鰠ⅰ?=> 通俗的講就是如果新增接口,每個接口都要添加入?yún)⑹愿怼⒊鰠⒌拇蛴 ?/li>
- 通用異常處理模塊抠蚣。 => 這里如果是針對業(yè)務(wù)本身的而設(shè)計的異常處理,那么無可厚非嘶窄,但是如果從
try ... catch ...
看的話,如果是為了替代全局異常而將所有的方法都添加在函數(shù)體內(nèi)的話就會存在問題了吻谋。
-
功能變動 => 如果替換需求中的“計算”部分现横,轉(zhuǎn)為獲取用戶的充值總額,用戶的基礎(chǔ)等不變戒祠。
- 對應(yīng)的入?yún)?
增加用戶姓名查詢
)是可能變動的,那么意味著入?yún)⒌拇蛴〉颓А⑿r?yàn)都需要修改馏颂。 - 其他的用戶信息的獲取也是需要是復(fù)制一份的。
- 對于返回信息而言也是如此救拉,此時需要修改新的新的信息的話,那就需要往這個代碼里添加新的業(yè)務(wù)邏輯告喊。而這個類一旦有變化壹无,就涉及對這個類的回歸驗(yàn)證。
- 對應(yīng)的入?yún)?
-
問題抽象:
- 存在部分冗余代碼地淀,在邏輯變動的時候會發(fā)現(xiàn),代碼的冗余會帶來修改的效率低下帮毁、存在漏改的風(fēng)險、存在邏輯不一致的風(fēng)險烈疚。
A02:通過模版方法解決部分冗余的問題
- 算法簡介:統(tǒng)一算法框架,將算法要素暴露給子類去實(shí)現(xiàn)猾浦。
-
算法核心:
- 控制業(yè)務(wù)執(zhí)行步驟,例如先打印日志金赦、再校驗(yàn)对嚼,執(zhí)行業(yè)務(wù)邏輯,統(tǒng)一處理異常漠烧。
- 和業(yè)務(wù)相關(guān)的核心要素實(shí)現(xiàn),在每個業(yè)務(wù)中是不一樣的已脓,所以這些需要子類自己去實(shí)現(xiàn)乏奥。
- 偽代碼示例:
public abstract class ServiceTemplate<T, R> {
private final Logger logger = LoggerFactory.getLogger(getClass());
/**業(yè)務(wù)執(zhí)行步驟*/
public R process(T request) {
//1,打印入口日志
Logger.info("start invoke, request=" + request);
try {
//2,校驗(yàn)參數(shù)(抽象方法)
validParam(request);
//3,子類實(shí)現(xiàn)邏輯(業(yè)務(wù)的具體實(shí)現(xiàn))
R response = doProcess(request):
//4,打印出口日志
logger.info("end invoke, response=" + response + ", costTime=" + timeCost);return response;
} catch (Exception e) {
}
}
/**父類抽象方法:參數(shù)校驗(yàn),具體的邏輯由子類自己實(shí)現(xiàn)*/
protected abstract void validParam(T request);
/**父類抽象方法:執(zhí)行業(yè)務(wù)邏輯恨诱,具體的邏輯由子類自己實(shí)現(xiàn)*/
protected abstract R doProcess(T request);
}
- 子類實(shí)現(xiàn)修改:
public Result<?> getUserInfo(String userId){
return (new ServiceTemplate<String, Result<?>>){
@Override
public void validParam(string request) {
log.info("校驗(yàn)入?yún)?);
}
@Override
public Result<?> doProcess(String request) {
log.info("ServiceA -> 獲取用戶基礎(chǔ)信息");
log.info("ServiceB -> 獲取用戶特殊信息");
// log.info("ServiceC -> 獲取當(dāng)前用戶充值的總金額");
log.info("ServiceC -> 獲取當(dāng)前用戶余額");
log.info("ServiceD -> 獲取用戶的消費(fèi)記錄");
log.info("利用strem流的方式獲取最近一次最貴的消費(fèi)");
return ResultGenerator.genSuccessResult("返回接口處理結(jié)果");
}
}).process(userId); // 調(diào)用業(yè)務(wù)執(zhí)行步驟的相關(guān)方法
}
-
優(yōu)化后分析好處與仍然存在的問題:
- 相關(guān)公共的邏輯被抽象了照宝,例如日志打印句葵、異常處理等。
- 不用擔(dān)心接口有遺漏步驟及搞錯步驟順序乍丈,例如入?yún)⑿r?yàn)在執(zhí)行業(yè)務(wù)流程之前。
- 子類只需要關(guān)注自己業(yè)務(wù)邏輯的實(shí)現(xiàn)即可忆矛。
- 如果對于接口要增加一些公用的能力的話请垛,可以直接在方法內(nèi)部抽象接口中添加洽议,
A03:通過流程引擎來優(yōu)化步驟的執(zhí)行順序
- 算法簡介:將要執(zhí)行的邏輯看成是一個個步驟的串接漫拭,由統(tǒng)一的角色來管理步驟的執(zhí)行順序,這個角色就是流程引擎审胚。
-
算法核心:算法的核心體現(xiàn)就是“管理者”角色的出現(xiàn),當(dāng)然需要將可以獨(dú)立的功能抽象成一個一個的 執(zhí)行器菲盾,這些執(zhí)行器可以是不同的業(yè)務(wù)領(lǐng)域類各淀,而管理者的職責(zé)就是將這些通用的流程放在一個池子中诡挂,根據(jù)不同的耦合業(yè)務(wù)場景從池子中獲取不同的執(zhí)行器,去編排到不同的業(yè)務(wù)流程中璃俗。
- 【核心優(yōu)勢 -> 避免冗余】:利用抽象和池化思想,將不同的執(zhí)行器統(tǒng)一管理苟穆,從而避免了代碼的冗余唱星。
- 【核心優(yōu)勢 -> 最小修改】:如果業(yè)務(wù)邏輯在組織過程中,需要增加一個環(huán)節(jié)间聊,可以方便的通過直接添加一個執(zhí)行器即可。
- 【核心優(yōu)勢 -> 方便追蹤】:在原始的模板方法中可以增加耗時統(tǒng)計處理型豁,此時也可以印證模板方法的優(yōu)勢尚蝌,根據(jù)耗時統(tǒng)計可以知道每個執(zhí)行器的耗時情況,方便追蹤日志飘言,去找到最耗時的執(zhí)行器去進(jìn)行精細(xì)化的定位。
- 【核心優(yōu)勢 -> 利于分工】:每個處理器在約定職責(zé)的情況下可以獨(dú)立開發(fā)泵喘,當(dāng)然就可以獨(dú)立測試泪电,減少整體出錯導(dǎo)致排查問題混亂的情況出現(xiàn)相速。 => 同時代碼的可讀性也會翻倍增加鲜锚。 => 當(dāng)然模塊化的設(shè)計,也就更加利于各個處理器以循環(huán)和分支的方式進(jìn)行組合芜繁。
![[Pasted image 20231030214347.jpg|給出參考連接作者的一副圖方便理解對應(yīng)的優(yōu)勢]]
Pasted image 20231030214347.jpg
- 示例代碼:這里不再對文章中的方法進(jìn)行整理,感興趣的話可以直接查看原文蔬捷。我更傾向于這是一種代碼的組織方式榔袋,你可以通過示例中去通過接口和對應(yīng)的實(shí)現(xiàn)類去創(chuàng)建不同的執(zhí)行器,然后通過模板方法再去組織業(yè)務(wù)凰兑,也就是通過模板方法中抽象接口去組織業(yè)務(wù)流程,充當(dāng)一個管理者的角色吏够。 => 從這個角度看,去實(shí)現(xiàn)自己的流程引擎就需要根據(jù)自己的代碼架構(gòu)去實(shí)現(xiàn)了播急,而實(shí)現(xiàn)方式則不拘泥于這一種。