代码怎么写
1、失敗項目復盤
代碼亂》bug 多》排查問題耗時》復用度低》加班 996》士氣低落……
癥結 1:組件粒度過大、API 泛濫
組件臃腫:Service 組件的個數跟領域實體對象個數基本相當,必然造成個別 Service 組件變得非常臃腫——API 非常多,代碼行數達到幾千行;
職責模糊:業務邏輯往往跨多個領域實體,無論放在哪個 Service 都不合適,同樣的,要找一個功能的實現邏輯也無法確定在哪個 Service 中;
代碼重復 or 邏輯糾纏的兩難選擇:當遇到一個業務邏輯,其中的某個環節在另一個業務邏輯 API 中已經實現,這時如果不想忍受重復實現和代碼,就只能去調用那個 API。但這樣就造成了業務邏輯組件之間的耦合與依賴,這種耦合與依賴很快會擴散——新的 API 又會被其它業務邏輯依賴,最終形成蜘蛛網一樣的復雜依賴甚至循環依賴;
復用代碼、減少重復雖然是好的,但是復雜耦合依賴的害處也很大——趕走一只狼引來了一只虎。兩杯毒酒給你選!
藥方 1:倒金字塔結構——業務邏輯組件職責單一、禁止層內依賴
業務邏輯層應該被設計成一個個功能非常單一的小組件,所謂小是指 API 數量少、代碼行數少;
由于職責單一因此必然組件數量多,每一個組件對應一個很具體的業務功能點(或者幾個相近的);
復用(調用、依賴)只應該發生在相鄰的兩層之間——上層調用下層的 API 來實現對下層功能的復用;
于是系統架構就自然呈現出倒立的金字塔形狀:越接近頂層的業務場景組件數量越多,越往下層的復用性高,于是組件數量越少。
癥結 2:低內聚、高耦合
業界關于“復用性”的認識存在一個誤區——認為包括業務邏輯組件在內的任何層面的組件都應該追求最大限度的可復用性;
復用當然是好的,但那應該有個前提條件:不增加系統復雜度的情況下的復用,才是好的。
什么樣的復用會增加系統復雜性、是不好的呢?前面提到的,一個業務邏輯 API 被另一個業務邏輯 API 復用——就是不好的:
損害了穩定性:因為業務邏輯本身是跟現實世界的業務掛鉤的,而業務會發生變化;當你復用一個會發生變化的 API,相當于在沙子上建高樓——地基是松動的;
藥方 2:復用的兩種正確姿勢——打造自己的 lib 和 framework
lib 庫是供你(應用程序)調用的,它幫你實現特定的能力(比如日志、數據庫驅動、json 序列化、日期計算、http 請求)。
framework 框架是供你擴展的,它本身就是半個應用程序,定義好了組件劃分和交互機制,你需要按照其規則擴展出特定的實現并綁定集成到其中,來完成一個應用程序。
lib 就是組合方式的復用,framework 則是繼承式的復用,繼承的 Java 關鍵字是 extends,所以本質上是擴展。
再展開一下:lib 既可以是第三方的(log4j、httpclient、fastjson),也可是你自己工程的(比如你的持久層 Dao、你的 utils);
framework 同理,既可以是第三方的(springmvc、jpa、springsecurity),也可以是你項目內封裝的面向具體業務領域的(比如 report、excel 導出、paging 或任何可復用的算法、流程)。
癥結 3:抽象不夠、邏輯糾纏——High Level 業務邏輯和 Low Level 實現邏輯糾纏
可讀性變差:兩個維度的復雜性——業務復雜性和底層實現的技術復雜性——被摻雜在了一起,復雜度 1+1>2 劇增,給其他人閱讀代碼增加很大負擔;
可維護性差:可維護性通常指排查和解決問題所需花費的代價高低,當兩個 level 的邏輯糾纏在一起,會使排查問題變的更困難,修復問題時也更容易出錯;
可擴展性無從談起:擴展性通常指為系統增加一個特性所需花費的代價高低,代價越高擴展性越差;與排查修復問題類似,邏輯糾纏顯然也會使添加新特性變得困難、一不小心就破壞了已有功能。
藥方 3:控制邏輯分離——業務模板 Pattern of NestedBusinessTemplate
解決“邏輯糾纏”最關鍵是要找到一種隔離機制,把兩個 Level 的邏輯分開——控制邏輯分離,分離的好處很多:
根據經驗,當我們著手維護一段代碼時,一定是想先弄清楚它的整體流程、算法和行為,而不是一上來就去研究它的細枝末節;
控制邏輯分離后,只需要去看 High Level 部分就能了解到上述內容,閱讀代碼的負擔大幅度降低,代碼可讀性顯著增強;
讀懂代碼是后續一切維護、重構工作的前提,而且一份代碼被讀的次數遠遠高于被修改的次數(高一個數量級),因此代碼對人的可讀性再怎么強調都不為過,可讀性增強可以大幅度提高系統可維護性,也是重構的最主要目標。
同時,根據我的經驗,High Level 業務邏輯的變更往往比 Low Level 實現邏輯變更要來的頻繁,畢竟前者跟業務直接對應。當然不同類型項目情況不一樣,另外它們發生變更的時間點往往也不同;
在這樣的背景下,控制邏輯分離的好處就更明顯了:每次維護、擴充系統功能只需改動一個 Levle 的代碼,另一個 Level 不受影響或影響很小,這會大幅降低修改成本和風險。
設計模式——業務模板 Pattern of NestedBusinessTemplat,可以非常簡單、有效的分離兩類邏輯
癥結 4:無處不在的 if else 牛皮癬
if else if ...else 以及類似的 switch 控制語句,本質上是一種 hard coding 硬編碼行為,如果你同意“magic number 魔法數字”是一種錯誤的編程習慣,那么同理,if else 也是錯誤的 hard coding 編程風格;
hard coding 的問題在于當需求發生改變時,需要到處去修改,很容易遺漏和出錯;
藥方 4:充血枚舉類型——Rich Enum Type
在 enum 枚舉類型基礎上進一步抽象封裝,得到一個所謂的“充血”的枚舉類型
enum NOTIFY_TYPE { //1、定義一個包含通知實現機制的“充血”的枚舉類型
email("郵件",NotifyMechanismInterface.byEmail()),
sms("短信",NotifyMechanismInterface.bySms()),
wechat("微信",NotifyMechanismInterface.byWechat());
String memo;
NotifyMechanismInterface notifyMechanism;
private NOTIFY_TYPE(String memo,NotifyMechanismInterface notifyMechanism){//2、私有構造函數,用于初始化枚舉值
this.memo=memo;
this.notifyMechanism=notifyMechanism;
}
//getters ...
}
總結:
職責單一、小顆粒度、高內聚、低耦合的業務邏輯層組件——倒金字塔結構;
打造項目自身的 lib 層和 framework——正確的復用姿勢;
業務模板 Pattern of NestedBusinessTemplate——控制邏輯分離;
充血的枚舉類型 Rich Enum Type——消滅硬編碼風格的 if else 條件判斷;
https://mp.weixin.qq.com/s/msB50Wp1qrehoSastZBEbQ
總結
- 上一篇: 宽带连路由器怎么连 如何宽带连接路由器
- 下一篇: 矩阵等价和向量组等价的区别和联系