重構 - 程式碼整潔之道

螞蟻金服資料體驗技術發表於2018-01-14

作者簡介 新茗 螞蟻金服·資料體驗技術團隊

前言

之前也介紹過我們團隊的前端專案從零開始經歷8個月迭代業務程式碼10萬行(僅為產品長期規劃需求的20%),至今仍然在不斷迭代的過程。

團隊成員除了設計好的架構來管理這種複雜度極高的前端應用,還開始補充設計模式以及重構方面的知識,目的是為了讓專案程式碼在不斷迭代的過程中優化專案程式碼,保持程式碼的新鮮度,魯棒性,可維護性… 讓後續加入的團隊新人也可以快速加入我們的產品開發中

PS: 不管對於何種語言,重構都是軟體開發過程中不可或缺的一部分。如果已經瞭解重構的基礎,可以直接跳往至文章後面的重構案例部分。

重構背景

“如果尿布臭了,就換掉它”。

  • 隨著業務需求的不斷加入,程式碼隨著時間的推移變得越來越糟。
  • 這其中可能包括以下壞味道(僅列舉):
    • 重複的程式碼
    • 過長的函式
      • 遵循一條原則: 每當感覺需要註釋來說明什麼的時候,可以嘗試將需要說明的東西寫進一個函式中
    • 冗贅類
      • 當子類沒有做足夠的工作的時候,或者說在可見的預期內,不會有新的情況出現,考慮將類內聯化。
    • 過長的類
      • 這種情況容易出現冗餘程式碼。比如如果類內出現了多個變數帶有相同的字首或者字尾,這意味著你可以考慮把他們提煉到某個元件內,或者考慮這個元件是否可以成為子類,使用提煉類的手法來重構。

什麼是重構

我們回過頭來看一下"什麼是重構"

  • 不改變軟體可觀察行為的前提下,改善其內部結構

  • 以提高理解性和降低修改成本

    摘自《重構 - 改善既有程式碼的設計》(下面簡稱《重構》)
    複製程式碼

何時重構?

我們需要明確的一點是: 重構不是一件應該特地撥出一段時間來做的事情。重構不是目的,但是重構可以幫助你把事情做好。

事不過三,三則重構

  1. 重複性工作,既有的程式碼無法幫助你輕鬆新增新特性時
  2. 修補bug時,排查邏輯困難
  3. code review 可以讓他人來複審程式碼檢查是否具備可讀性,可理解性
  4. 太多的程式碼無註釋,已然連自己都無法快速理清程式碼邏輯

重構的衡量指標

  • 數量: 程式碼的行數
  • 質量: 程式碼複雜度,耦合度,可讀性,架構依賴複雜度等
  • 成本: 花費的時間
  • 回報(成果): 支援後續功能的快速疊加,解決現有因程式碼設計問題無法優化的矛盾等

抓重點 抓重點啦

說了這麼多廢話,其實大家都明白沒有與實踐結合的理論都是空虛的。

但是 重構設計模式一樣,也是需要一個"學習——領悟——突破"的過程。第一步的學習讓你瞭解基本的重構手法,第二步的實踐勾起你對重構手法的回憶以及重溫應用,第三步的應用以及實踐經驗激發你的思考,領悟以及總結,以致於靈活運用。

但凡是人,總是在不斷學習,不斷溫習,以達到具體場景具體應用,靈活自如。 重構是一個很大的話題,《重構》作者本人也是經歷了N多的專案,以及多年的經驗才總結出來的重構技巧。

重構技巧

《重構》一書作者總結的重構手法實在是太多了,只能通過圖片來展示一下所有作者總結的重構列表。
具體的補充,大家可以看看《重構》一書。

重構 - 程式碼整潔之道

重構 - 程式碼整潔之道

重構的實踐

作者推薦的一種做法:

  1. 隨機挑選一個目標 先給自己選擇一個目標(譬如“去掉一堆不必要的子類”),然後朝著目標前進,每一步走得小而堅定
  2. 沒把握就停下來 當你無法證明自己所做的一切能夠保證原有程式的邏輯和語義時,請你停下來思考:既有的重構是改善了還是毫無成果需要撤銷。
  3. 保證每次重構後的測試都能正常跑通

作為開發者, 應當把重構作為開發的一部分,一邊開發一邊重構。在快速堆疊程式碼,實現基本需求功能的基礎上,寫好測試用例,保證功能不變,逐步重構。
這也是我們團隊要求每個人都掌握重構這門必備技能的原因。優秀的程式設計師應當儘量避免低質量的程式碼。

重構案例

故事場景

  1. 有三種型別的電影,顧客可以進行租賃
  2. 租賃規則
    1. 價格計算規則:
      普通片兒 —— 起步價2¥,超過2天的部分每天每部電影收費1.3元
      新片兒 —— 每天每部3元
      兒童片 —— 起步價2¥,超過3天的部分每天每部電影收費0.8元
    2. 積分計算規則:
      每借一部電影積分加1,新片每部加2

原始程式碼

程式結果:(請保證重構後結果不變~)

重構 - 程式碼整潔之道

  • 類圖

重構 - 程式碼整潔之道

重構 - 程式碼整潔之道
有興趣的可以先看看原始程式碼,考慮一下其中的原始物件關係,再行考慮如何重構程式碼。 原始程式碼其實是有很多問題可以挖掘的,下面是我們的討論整理:

  • 劃分職責關係,遵循單一職責原則
    • statement列印賬單函式承擔了很多功能,包括收費計算,積分計算以及結果展示等等
      • 解法1: 6.1 Extract Method(提煉函式) —— 最常用的重構手法
      • 解法2: 9.1 Decompose Conditional(分解條件表示式)
    • 使用者類中承擔了不屬於它的職責,包括:收費規則、積分規則。這些職責應該是屬於電影型別的。
  • 整理清楚其中的業務邏輯,比如收費規則和積分規則 - 見故事場景
  • 不要直接訪問物件的資料。容易發生其他物件改變該物件的資料,而擁有該資料的物件卻一無所知。
    • 解法:8.10 Encapsulate Field(封裝欄位手法) —— 使資料和行為想分離
  • 重構不應該被外界所感知,保證testcases依然可行

部分重構

這裡為了更好的展示重構的手法,使用TS,根據上面的討論進行了部分重構,重構的方式其實是根據業務未來的擴充套件方向而定,並沒有最優解,有興趣的可以加入我們,丟擲你的見解~

重構 - 程式碼整潔之道

  • 重構後的類圖關係

重構 - 程式碼整潔之道

基本技巧

  • 小步前進,頻繁測試(保證足夠的測試來支援你的重構行動)
  • 使用智慧開發工具(比如VSCode右鍵可以將過長的函式程式碼拆解函式化)

推薦書籍

重構其實是一件需要長期投入,並且投入產出比很高的事情。對重構感興趣的同學可以關注專欄或者傳送簡歷至'tao.qit####alibaba-inc.com'.replace('####', '@'),歡迎有志之士加入~

原文地址:github.com/ProtoTeam/b…

相關文章