抽絲剝繭 - 實例簡析重構代碼的三板斧

I. 太陽之下無新事:如何面對既有代碼

既有代碼

能夠說,任何軟件系統從設計部署好的次日起,就都變成了 既有代碼(existing code)。一個幾年的系統和一個幾周的系統中存在的問題,並沒有本質上的差別。react

俗話說,創業容易守業難。比新搭建一個系統更常見的工做,正是對既有系統的平常維護,通常包括:架構

  • 添加新特性
  • 修正bug
  • 優化設計和性能

如何有效進行這些工做,而不是陷入「修復一個bug,還你幾個新bug」的惡性循環,是每一個開發者必然面對的課題。函數

遺留代碼

既有代碼的一種極端狀況,就 是遺留代碼(legacy code),通常指那些無人再維護的,或架構很是過期,亦或運行在老舊的操做系統上的代碼。性能

相比於在大抵上每幾天就打個照面的既有代碼中修修改改;當面對一頭霧水的遺留代碼時,若是沒有正確的方法,即使再當心翼翼,也總會陷入一籌莫展的境地。單元測試

II. 既有代碼的困境

每一個開發者可能都見過奇奇怪怪的各類具體問題,但概括起來,主要有這麼三種狀況:測試

  • 太重的依賴:實例化或方法調用的過程當中,過多過深的依賴於其餘類或組件
  • 錯誤的封裝:類或組件承擔了不該有或過多的功能
  • 裸奔的功能:沒有測試代碼或過期、不完整的測試覆蓋

III. 重構的意義

面對以上困境,須要作的就是重構:優化

在不改變代碼功能的前提下,改善其設計的行爲被稱爲 重構(refactor)ui

重構的意義:使既有代碼更具可維護性,並消除其不肯定性操作系統

重構的關鍵:在其過程當中不該該有任何功能上的改變設計

在以前提到過的幾種平常工做中,無一例外不須要先進行有效的重構,才能保證工做的順利進行。

IV. 重構三板斧:解開依賴、合理封裝、測試覆蓋

一樣顯而易見的是,將三重困境一一化解,就能夠達到理想的重構:

  1. 依賴:

    • 簡化致使邏輯複雜的太重依賴,將關注點從散佈在艱深冗長的調用鏈條中拉回來
    • 設置setter方法或借鑑interface的思路等,解決因爲關聯了太多其餘類或組件,從而沒法在測試用例中實例化和調用方法的問題
  2. 封裝:

    • 類或組件只應該承擔儘可能簡單而少許的職責,過長的類或組件應抽取成多個
    • 類或組件不該當包含其子層級或兄弟層級的邏輯
    • 出如今多處的重複代碼老是可疑的,應該儘可能抽象和提取出來
  3. 測試:

    • 測試的做用就是檢驗正確性和檢驗變化
    • 迴歸測試(regression testing):週期性的運行測試,來檢驗已知的良好行爲是否依然正常工做。
    • 單元測試(unit testing):是指對軟件中的最小可測試單元進行檢查和驗證;在 JS 中「單元」通常能夠認爲是一個函數或一個類。
    • 測試用例(test case):就是設定輸入數據,運行被測試函數,而後判斷實際輸出是否符合預期

簡而言之,前兩項是最後「落在實處」的工做,而測試的重要性並不亞於任何工做,甚至是保證前兩項進行下去的關鍵。 **測試覆蓋率(test coverage)**越高,說明所覆蓋部分的可靠性越有保證,而沒必要時時擔憂改動帶來的未知影響。

遵循爲獨立單元(視狀況爲函數、類或組件等)編寫測試的理念,就能夠寫出小而易理解的一個個測試用例,也反過來使得代碼比寫註釋更容易理解。

對於新開發的功能,能夠用測試驅動開發(TDD)的方法,即重複「寫一點代碼->編寫測試->失敗->修改代碼->測試經過」的過程,最終達到方法的完成。

對於既有代碼,能夠根據平常需求,對涉及到的部分逐步引入單元測試,持續不斷的提升系統的測試覆蓋率。

V. 一個stepper組件的重構實例

這裏舉一個足夠簡單也比較典型的例子:重構stepper組件

場景描述:

在這個由 react 組件構建的既有系統中,在若干界面中都引用了一個常見的 數字選擇器(numeric stepper),其變化會觸發判斷邏輯,提示商品對應的數量是否有足夠庫存、是否達到了限購數量等

既有的結構和問題:

  • 「全局」組件NumberStepper裏糅雜了具體業務邏輯「限購」和「庫存」的判斷
  • 以上判斷邏輯冒泡到各類容器組件中,「演化」出了同一函數的不一樣簽名形式 -- 梳理後發現:雖然參數定義不一樣且含糊不清,實際要達成的邏輯倒是同樣的
  • 相關的邏輯判斷代碼和彈窗jsx結構,均重複出現於不一樣組件中
  • NumberStepper和各類容器組件中,均分別存在用 0|1|2|3 定義的判斷和顯示邏輯,且無註釋說明

問題的分解:

困境 問題A 問題B 問題C
依賴太重
封裝錯誤
缺少測試

問題的解決:

  • 在測試的保護下,將NumberStepper中的具體業務邏輯依賴刪除
  • NumberStepper暴露props.checkLimit,在數量增減時響應,將判斷邏輯交給調用者
  • 統一判斷邏輯onLimitOver的函數簽名,明確參數的意義
  • 將展示部分的邏輯和界面分別提取爲單一的部分,統一調用
  • 將以前用數字區分的邏輯概括成常量類,交由各處統一調用
  • NumberStepperOverLimit等組件編寫測試,保證改動的全面和正確性

VI. 總結

至此,以前的結構獲得了有效的梳理;再進行相關的功能添加就有章可循、心中有數了。

  • 處理大部分「添加新特性、修正bug和優化」類的平常工做時,須要科學具體的方法論
  • 太重的依賴、錯誤的封裝、缺乏測試,是既有代碼的常見問題
  • 有針對性的從以上三個方面入手,並輔以必要的單元測試,就能夠保證工做有條理的進行下去

VII. 參考資料

  • https://book.douban.com/subject/2248759/
  • https://en.wikipedia.org/wiki/Legacy_code
  • https://www.tuicool.com/articles/3QRBRr
  • https://baike.baidu.com/item/單元測試


(end)


長按二維碼或搜索 fewelife 關注咱們哦
相關文章
相關標籤/搜索