代碼整潔之道(重構(gòu))
前言
之前也介紹過我們團隊的前端項目從零開始經(jīng)歷8個月迭代業(yè)務(wù)代碼10萬行(僅為產(chǎn)品長期規(guī)劃需求的20%),至今仍然在不斷迭代的過程。
團隊成員除了設(shè)計好的架構(gòu)來管理這種復(fù)雜度極高的前端應(yīng)用,還開始補充設(shè)計模式以及重構(gòu)方面的知識,目的是為了讓項目代碼在不斷迭代的過程中優(yōu)化項目代碼,保持代碼的新鮮度,魯棒性,可維護性… 讓后續(xù)加入的團隊新人也可以快速加入我們的產(chǎn)品開發(fā)中
PS: 不管對于何種語言,重構(gòu)都是軟件開發(fā)過程中不可或缺的一部分。如果已經(jīng)了解重構(gòu)的基礎(chǔ),可以直接跳往至文章后面的重構(gòu)案例部分。
重構(gòu)背景
“如果尿布臭了,就換掉它”。
- 隨著業(yè)務(wù)需求的不斷加入,代碼隨著時間的推移變得越來越糟。
- 這其中可能包括以下壞味道(僅列舉):
- 重復(fù)的代碼
- 過長的函數(shù)
- 遵循一條原則: 每當感覺需要注釋來說明什么的時候,可以嘗試將需要說明的東西寫進一個函數(shù)中
- 冗贅類
- 當子類沒有做足夠的工作的時候,或者說在可見的預(yù)期內(nèi),不會有新的情況出現(xiàn),考慮將類內(nèi)聯(lián)化。
- 過長的類
- 這種情況容易出現(xiàn)冗余代碼。比如如果類內(nèi)出現(xiàn)了多個變量帶有相同的前綴或者后綴,這意味著你可以考慮把他們提煉到某個組件內(nèi),或者考慮這個組件是否可以成為子類,使用提煉類的手法來重構(gòu)。
什么是重構(gòu)
我們回過頭來看一下"什么是重構(gòu)"
- 不改變軟件可觀察行為的前提下,改善其內(nèi)部結(jié)構(gòu)
- 以提高理解性和降低修改成本
摘自《重構(gòu) - 改善既有代碼的設(shè)計》(下面簡稱《重構(gòu)》)
何時重構(gòu)?
我們需要明確的一點是: 重構(gòu)不是一件應(yīng)該特地撥出一段時間來做的事情。重構(gòu)不是目的,但是重構(gòu)可以幫助你把事情做好。
事不過三,三則重構(gòu)
- 重復(fù)性工作,既有的代碼無法幫助你輕松添加新特性時
- 修補bug時,排查邏輯困難
- code review 可以讓他人來復(fù)審代碼檢查是否具備可讀性,可理解性
- 太多的代碼無注釋,已然連自己都無法快速理清代碼邏輯
重構(gòu)的衡量指標
- 數(shù)量: 代碼的行數(shù)
- 質(zhì)量: 代碼復(fù)雜度,耦合度,可讀性,架構(gòu)依賴復(fù)雜度等
- 成本: 花費的時間
- 回報(成果): 支持后續(xù)功能的快速疊加,解決現(xiàn)有因代碼設(shè)計問題無法優(yōu)化的矛盾等
抓重點 抓重點啦
說了這么多廢話,其實大家都明白沒有與實踐結(jié)合的理論都是空虛的。
但是 重構(gòu)和 設(shè)計模式一樣,也是需要一個"學習——領(lǐng)悟——突破"的過程。第一步的學習讓你了解基本的重構(gòu)手法,第二步的實踐勾起你對重構(gòu)手法的回憶以及重溫應(yīng)用,第三步的應(yīng)用以及實踐經(jīng)驗激發(fā)你的思考,領(lǐng)悟以及總結(jié),以致于靈活運用。
但凡是人,總是在不斷學習,不斷溫習,以達到具體場景具體應(yīng)用,靈活自如。
重構(gòu)是一個很大的話題,《重構(gòu)》作者本人也是經(jīng)歷了N多的項目,以及多年的經(jīng)驗才總結(jié)出來的重構(gòu)技巧。
重構(gòu)技巧
《重構(gòu)》一書作者總結(jié)的重構(gòu)手法實在是太多了,只能通過圖片來展示一下所有作者總結(jié)的重構(gòu)列表。
具體的補充,大家可以看看《重構(gòu)》一書。
重構(gòu)的實踐
作者推薦的一種做法:
隨機挑選一個目標
先給自己選擇一個目標(譬如“去掉一堆不必要的子類”),然后朝著目標前進,每一步走得小而堅定沒把握就停下來
當你無法證明自己所做的一切能夠保證原有程序的邏輯和語義時,請你停下來思考:既有的重構(gòu)是改善了還是毫無成果需要撤銷。- 保證每次重構(gòu)后的測試都能正常跑通
作為開發(fā)者, 應(yīng)當把重構(gòu)作為開發(fā)的一部分,一邊開發(fā)一邊重構(gòu)。在快速堆疊代碼,實現(xiàn)基本需求功能的基礎(chǔ)上,寫好測試用例,保證功能不變,逐步重構(gòu)。
這也是我們團隊要求每個人都掌握重構(gòu)這門必備技能的原因。優(yōu)秀的程序員應(yīng)當盡量避免低質(zhì)量的代碼。
重構(gòu)案例
故事場景
有三種類型的電影,顧客可以進行租賃
租賃規(guī)則
價格計算規(guī)則:
普通片兒 —— 起步價2¥,超過2天的部分每天每部電影收費1.3元
新片兒 —— 每天每部3元
兒童片 —— 起步價2¥,超過3天的部分每天每部電影收費0.8元
積分計算規(guī)則:
每借一部電影積分加1,新片每部加2
原始代碼
程序結(jié)果:(請保證重構(gòu)后結(jié)果不變~)
類圖
有興趣的可以先看看原始代碼,考慮一下其中的原始對象關(guān)系,再行考慮如何重構(gòu)代碼。原始代碼其實是有很多問題可以挖掘的,下面是我們的討論整理:
- 劃分職責關(guān)系,遵循單一職責原則
- statement打印賬單函數(shù)承擔了很多功能,包括收費計算,積分計算以及結(jié)果展示等等
- 解法1: 6.1 Extract Method(提煉函數(shù)) —— 最常用的重構(gòu)手法
- 解法2: 9.1 Decompose Conditional(分解條件表達式)
- 用戶類中承擔了不屬于它的職責,包括:收費規(guī)則、積分規(guī)則。這些職責應(yīng)該是屬于電影類型的。
- statement打印賬單函數(shù)承擔了很多功能,包括收費計算,積分計算以及結(jié)果展示等等
- 整理清楚其中的業(yè)務(wù)邏輯,比如收費規(guī)則和積分規(guī)則 - 見故事場景
- 不要直接訪問對象的數(shù)據(jù)。容易發(fā)生其他對象改變該對象的數(shù)據(jù),而擁有該數(shù)據(jù)的對象卻一無所知。
- 解法:8.10 Encapsulate Field(封裝字段手法) —— 使數(shù)據(jù)和行為想分離
- 重構(gòu)不應(yīng)該被外界所感知,保證testcases依然可行
部分重構(gòu)
這里為了更好的展示重構(gòu)的手法,使用TS,根據(jù)上面的討論進行了部分重構(gòu),重構(gòu)的方式其實是根據(jù)業(yè)務(wù)未來的擴展方向而定,并沒有最優(yōu)解,有興趣的可以加入我們,拋出你的見解~
CODEPEN 執(zhí)行結(jié)果:
重構(gòu)后的類圖關(guān)系
基本技巧
- 小步前進,頻繁測試(保證足夠的測試來支持你的重構(gòu)行動)
- 使用智能開發(fā)工具(比如VSCode右鍵可以將過長的函數(shù)代碼拆解函數(shù)化)
推薦書籍
《代碼整潔之道》
《重構(gòu) - 改善既有代碼的設(shè)計》
《修改代碼的藝術(shù)》
《代碼大全》
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
ES6模板字符串和標簽?zāi)0宓膽?yīng)用實例分析
這篇文章主要介紹了ES6模板字符串和標簽?zāi)0宓膽?yīng)用,結(jié)合實例形式分析了ES6模板字符串和標簽?zāi)0宓墓δ堋⒃?、用法及相關(guān)操作注意事項,需要的朋友可以參考下2019-06-06基于構(gòu)造函數(shù)的五種繼承方法小結(jié)
下面小編就為大家?guī)硪黄跇?gòu)造函數(shù)的五種繼承方法小結(jié)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07