天天看點

遺留代碼處理技巧與案例示範

1 什麼是遺留代碼

本質是一種技術債務,産生原因一方面是業務原因:如業務本身場景繁多、流程複雜等;另一方面是技術原因:如代碼不規範、設計不合理、祖傳代碼文檔注釋缺失等。它會影響我們的程式很多方面:如可讀性、可修改性、可複用性、可維護性、可測試性等。

2 遺留代碼處理過程拆解

劃分為梳理->重構/重寫->替換/驗證三個階段

遺留代碼處理技巧與案例示範

2.1 梳理

遺留代碼的處理是一種逆向工程,從已有的代碼+資料模型+文檔倒推出業務模型、互動和規則,在保真的前提下再重新建構代碼+資料模型+文檔。

我們這裡可以參考下DDD領域驅動設計裡戰略設計部分常用的工具(事件風暴法)來進行這部分梳理工作。

遺留代碼處理技巧與案例示範

事件風暴本質上是一種系統模組化的方法,與它處于對等位置的,會有“UML模組化”、“事件驅動模組化”等。事件風暴跟靈活開發裡的一些理念(如使用者故事)的産生背景類似,都是在理性思考無法應對變化頻繁且文字難以描述的情況下,通過一些輔助性的提示卡片、視覺手段,輔以相關人員的集中、高頻溝通來完成對于業務的準确把握和抽象模組化。

事件風暴的過程:

  1. 通過梳理業務流程,建立相應的領域事件(Event)
  2. 補充引發每個領域事件的指令(Command)
  3. 通過實體/聚合把指令和事件關聯起來
  4. 劃分領域邊界及事件流動線條
  5. 識别使用者操作所需的關聯視圖及其角色

事件風暴的産物:

  1. 領域對象 即實體/聚合。這裡的領域對象并非資料庫模型, 而是與業務緊密聯系的“對象”。因為事件風暴是一種面向對象的模組化方式, 而不是面向資料庫的模組化方式。
  2. 領域事件 即對象在某些操作或特點時點下所産生的事件, 這些事件将決定之後多個聚合和限界上下文(BC)之間的通訊方式。
  3. 限界上下文 當所有的對象(實體/聚合)被梳理出來後,屬于同一種“通用語言”的對象, 則會被歸入同一個限界上下文邊界内;不屬于同一種“通用語言”的對象, 則會被邊界給分割開,劃入不同的子域或限界上下文。

梳理結果示例:

遺留代碼處理技巧與案例示範

2.2 重構/重寫

通過重構/重寫對軟體要素進行重新組織,使其不改變外部行為的情況下,提升代碼的可讀性或使其結構更合理。

遺留代碼處理技巧與案例示範

針對不同層次的軟體要素要做不同的處理和控制:

遺留代碼處理技巧與案例示範

并且整個重構/重寫過程有些需要遵照的原則:

  1. 單一職責:可以将依賴歸攏,統一行為和控制。權責明确,場景明确。
  2. 單一原則:消除重複的資料聲明、行為;因為單一是以保證了複用,統一标準 ,可裝配性。
  3. 封裝原則:不需要過度關心依賴類内部實作,最好一個.就能調用。
  4. 歸屬原則:上帝的歸上帝,凱撒的歸凱撒。誰提供的資料更多,歸屬于誰。
  5. 抽象層次:越高層的抽象越穩定,越細節的東西越容易變化。舉例:接口應傳遞職責而非實作細節。
  6. 開閉原則:對修改關閉,對擴充開放。
  7. kiss原則: 好了解,好維護。
  8. 清晰原則:隻讀小部分代碼就可以知道怎麼改邏輯,做擴充。而不是要通讀所有代碼,才能理清。

其中有兩點落地細節我們具體分析下:

  1. 業務邏輯的處理

    業務代碼和技術代碼解耦主流程代碼和附加流程代碼解耦長鍊路的拆解編排

  2. 關注點的分離

    雙向依賴:上下文之間缺少一層未被澄清的上下文,或者兩個上下文其實可被合為一個;循環依賴:任何一個上下文發生變更,依賴鍊條上的上下文均需要改變;過深的依賴:自身依賴的資訊不能直接從依賴者擷取到,需要通過依賴者從其依賴的上下文擷取并傳遞,依賴鍊路過長,依賴鍊條上的任何一個上下文發生變更,其鍊條後的任何一個上下文均可能需要改變;

2.3 替換驗證

大概分為以下幾個要點:

  1. 領會意圖,抽取用例,增加可複測性
  2. 增加可監測性
  3. 分成小塊,逐漸替換
  4. 試點、看到成效

可借助過程管理工具如PDCA法進行管理

遺留代碼處理技巧與案例示範

3 案例示範

3.1 案例1:針對強耦合的實作做重構

原始需求:案例為一個轉賬服務,使用者可以通過銀行網頁轉賬給另一個賬号,支援跨币種轉賬。同時因為監管和對賬需求,需要記錄本次轉賬活動。

原始架構:是一個傳統的三層分層結構:UI層、業務層、和基礎設施層。上層對于下層有直接的依賴關系,導緻耦合度過高。在業務層中對于下層的基礎設施有強依賴,耦合度高。我們需要對這張圖上的每個節點做抽象和整理,來降低對外部依賴的耦合度。

遺留代碼處理技巧與案例示範

重構關鍵設計點:

遺留代碼處理技巧與案例示範

重構後代碼特征:

業務邏輯清晰,資料存儲和業務邏輯完全分隔。

Entity、Domain Primitive、Domain Service都是獨立的對象,沒有任何外部依賴,但是卻包含了所有核心業務邏輯,可以單獨完整測試。

原有的轉賬服務不再包括任何計算邏輯,僅僅作為元件編排,所有邏輯均delegate到其他元件。

3.2 案例2:提高老代碼的複用性

原始需求:現有幾個政策實作類,被很多代碼使用。現在需要根據不同的業務方在每個政策執行前做不同的前置邏輯處理。

解法分析:盡量避免把邏輯耦合到已有的實作類中。引入外部類進行控制反轉。這裡我們使用通路者模式。

通路者模式把資料結構和作用于結構上的操作解耦合,使得操作集合可相對自由地演化。通路者模式适用于資料結構相對穩定算法又易變化的系統。因為通路者模式使得算法操作增加變得容易。若系統資料結構對象易于變化,經常有新的資料對象增加進來,則不适合使用通路者模式。通路者模式的優點是增加操作很容易,因為增加操作意味着增加新的通路者。通路者模式将有關行為集中到一個通路者對象中,其改變不影響系統資料結構。其缺點就是增加新的資料結構很困難。

遺留代碼處理技巧與案例示範

具體實作:

遺留代碼處理技巧與案例示範
遺留代碼處理技巧與案例示範

重構後代碼特征:

4 總結

繼續閱讀