1. 程式人生 > 其它 >聊一聊:一個大型軟體系統該如何重構

聊一聊:一個大型軟體系統該如何重構

  在網際網路行業,每當新員工入職一家新公司時,都要學習一套新的軟體系統。如果該公司的程式碼非常規範,架構設計非常合理,那麼新員工上手的速度會非常快。當然,你這個螺絲釘的角色也就非常明顯了。另一方面,如果面對『屎山』一樣的祖傳程式碼,就會有很多抱怨,學習起來也很痛苦。

  從質量上,我把軟體大致分為以下幾種型別:

  第一種:它們對穩定性、規範性要求非常高,所以程式碼中異常判斷、校驗非常多,程式碼看上去就很冗餘,最典型代表是華為的電信級產品。第二種:寫得就很隨意,風格各式各樣,代表的例子是開源專案。第三種:也是最糟糕的,員工在 PM(產品經理)的高壓下日夜加班趕出來的,毫無設計風格,能跑就行,追求最快速度地實現。

  重構應該也是網際網路公司開發工作的一部分吧。當一個軟體系統需要重構時,必然是因為以下某種原因:比如二次開發難度越來越大、新員工上手越來越困難,每個模組只有特定的人懂;面對日益增長的功能需求,現有架構已經滿足不了;模組耦合非常嚴重,導致不同的開發團隊之間互相依賴,嚴重阻礙了開發進度。

  模組之間耦合嚴重,類間呼叫關係形成雙向依賴。大量的 if/else 分支、執行流程分支太多,圈複雜度爆表,根本理不清。當我們想要增加一個新功能,即使這個功能很小很小,牽扯的鏈條也非常長,涉及需要改動的周邊函式、檔案數量巨多。

  軟體系統從最初的 demo 開始,不斷完善,一點點新增新功能,以適應不同的應用場景。比如滴滴這款軟體,最初只要把司機端和乘客接進來就行,加上排程系統負責派單,使用者規模也比較小;後來,使用者規模陡增,需要擴充套件伺服器數量,又牽扯到負載均衡、訊息中介軟體、高併發等需求 (分散式技術 3:高併發系統的設計思路以及C++ 實現 - 知乎);再後來,新增各種服務型別,比如順風車、專車、豪華車等等,然後又是各種紅包、優惠券等。

  微服務的前提條件就是模組間能解耦,這不僅是上雲的需求,也能提高研發團隊整體的開發效率,更重要的是為了實現服務編排,可以給任意子的服務提供靈活的資源,從而最大化叢集的資源利用率,也就是說能更好地做到彈性擴縮容和容錯。(從單體到微服務,論軟體系統如何逐步地進行解耦 - 知乎)

  傳統概念中對程式碼重構的理解是『不引入任何新功能』。我的看法是,程式碼重構和新功能開發結合起來,這樣更有利於最大化重構效果。

  重構也是軟體架構設計的一種,這裡我稱之為『重構設計』。

  首先,你要清楚重構的目標是什麼。比如側重滿足二次開發,或者側重模組解耦,或者相容各種硬體平臺、程式語言等等。

  其次,你要對基本的軟體架構和

軟體設計風格有清晰的瞭解,以下是一些必備技能:

  模組原則:使用簡潔的介面拼合簡單的部件清晰原則:清晰勝於技巧組合原則:設計時考慮拼接組合分離原則:策略同機制分離,介面同引擎分離簡潔原則:設計要簡潔,複雜度能低則低吝嗇原則:除非卻無他法,不要編寫龐大的程式透明性原則:設計要可見,以便審查和除錯健壯原則:健壯源於透明與簡潔表示原則:把知識疊入資料以求邏輯質樸而健壯通俗原則:介面設計避免標新立異緘默原則:如果一個程式沒什麼好說的,就沉默補救原則:出現異常時,馬上退出並給出足夠多的錯誤資訊經濟原則:寧花機器一分,不花程式設計師一秒生成原則:避免手工hack,儘量編寫程式去生成程式優化原則:雕琢前要先有圓形,跑之前先學會走多樣性原則:絕不相信所謂”不二法門“的斷言擴充套件原則:設計著眼未來,未來總比預想來得快

  23 種設計模式,你不一定要完全瞭解程式碼怎麼寫,但一定要知道每一種設計模式背後的設計思想是什麼。有一段時間,我試圖在我的程式碼應用各種設計模式,可最終程式碼看起來特別冗餘而且不是那麼必要。從個人經驗上來講,平時業務程式碼中用設計模式的場合非常少,最常用的無非是工廠、介面卡、責任鏈等,而且效果並沒那麼大,設計模式真正適合的場合是更高層級的,比如模組間設計等等。

  單一職責、開閉原則、里氏替換、迪米特法則、介面隔離、依賴倒置。

  有了解過一些,沒有親身實踐過。它大體上就是模組解耦和分層的思想:API(對外訪問層)、Domain(領域層)、Repository(資料來源訪問代理層)及基礎設施層(DB、Redis、HTTP、RPC 等)

  參考馬丁.福勒那本經典的《重構:改善程式碼的設計》,比如過長的函式、過長的引數、資料泥團怎麼處理等等。

  這裡,也說下個人的小建議:比如縱向的呼叫關係變為橫向的,減少函式呼叫棧深度;不要過度封裝。相信使用者能找到底層類的實現介面;邏輯上相關的程式碼物理上儘可能放在一塊;對於某個小的具體功能,涉及的鏈條越短越好;面向介面程式設計;訪問 A 表資料的 class 中不能存在訪問 B 表資料的 function;模組對外暴露的介面部分,資料型別的選擇上儘量做到寬進嚴出(介面要考慮通用性);寫操作介面,接收引數儘可能少;讀操作介面,返回引數儘可能多;減少不必要的類和資料結構等等。

  詳解微服務之間3大通訊方式:閘道器 API、RPC 和 SideCar - 知乎

  毫無疑問,雲是未來數字世界的基礎設施。

  詳解雲原生五大關鍵技術 - 知乎

  比如約定/注入外掛、事件外掛、插槽外掛等等。

  外掛的一個簡單實現 - 知乎

  好了,這裡不再列舉了,因為根據哈弗大學心理學博士米勒的研究,對於每一類產品,使用者最多隻能記住七類品牌,這裡我也就列七個重構原則~

  以面嚮物件語言為例,這裡我把它分為了幾個步驟:

  舊系統的類間呼叫關係圖 vs 新系統的類間呼叫關係圖舊系統的整體架構圖 vs 新系統的整體架構圖舊系統的執行流程圖 vs 新系統的執行流程圖分模組逐一拆解:先畫出重構前的樣子,再畫出重構後的樣子新增功能有哪些,怎麼在重構後的系統裡新增考慮對舊系統的影響:相容性問題考慮實施可行性: 重構成本和收益之間如何權衡

  總體上有兩種方式:

  1. 新建分支(不推薦)

  2. 新建目錄(推薦),這樣能保證原來舊的那套程式碼能用。這裡又有兩種方式:

  做減法(先跑通舊的程式碼,然後一點點刪除冗餘程式碼,或重構具體子模組)-- 推薦,因為如果實在沒有開發時間了,某些未完成的模組還可以沿用以前的老程式碼。做加法(從每個子功能開始重構,相當於重新構建這套軟體系統)-- 不推薦,因為時間成本不可控

  用設計原則和架構理論武裝自己,閱讀優秀的原始碼驗證理論,深入理解具體業務,方能設計出一套優雅的軟體系統。

  各位朋友們,想要了解更多方面的技術內容,請關注本人同名zhihu 賬號“自由技藝”,上面的內容更新的會比較勤哦~