代碼整潔之道(重構(gòu))
前言
之前也介紹過(guò)我們團(tuán)隊(duì)的前端項(xiàng)目從零開(kāi)始經(jīng)歷8個(gè)月迭代業(yè)務(wù)代碼10萬(wàn)行(僅為產(chǎn)品長(zhǎng)期規(guī)劃需求的20%),至今仍然在不斷迭代的過(guò)程。
團(tuán)隊(duì)成員除了設(shè)計(jì)好的架構(gòu)來(lái)管理這種復(fù)雜度極高的前端應(yīng)用,還開(kāi)始補(bǔ)充設(shè)計(jì)模式以及重構(gòu)方面的知識(shí),目的是為了讓項(xiàng)目代碼在不斷迭代的過(guò)程中優(yōu)化項(xiàng)目代碼,保持代碼的新鮮度,魯棒性,可維護(hù)性… 讓后續(xù)加入的團(tuán)隊(duì)新人也可以快速加入我們的產(chǎn)品開(kāi)發(fā)中
PS: 不管對(duì)于何種語(yǔ)言,重構(gòu)都是軟件開(kāi)發(fā)過(guò)程中不可或缺的一部分。如果已經(jīng)了解重構(gòu)的基礎(chǔ),可以直接跳往至文章后面的重構(gòu)案例部分。
重構(gòu)背景
“如果尿布臭了,就換掉它”。
- 隨著業(yè)務(wù)需求的不斷加入,代碼隨著時(shí)間的推移變得越來(lái)越糟。
- 這其中可能包括以下壞味道(僅列舉):
- 重復(fù)的代碼
- 過(guò)長(zhǎng)的函數(shù)
- 遵循一條原則: 每當(dāng)感覺(jué)需要注釋來(lái)說(shuō)明什么的時(shí)候,可以嘗試將需要說(shuō)明的東西寫(xiě)進(jìn)一個(gè)函數(shù)中
- 冗贅類(lèi)
- 當(dāng)子類(lèi)沒(méi)有做足夠的工作的時(shí)候,或者說(shuō)在可見(jiàn)的預(yù)期內(nèi),不會(huì)有新的情況出現(xiàn),考慮將類(lèi)內(nèi)聯(lián)化。
- 過(guò)長(zhǎng)的類(lèi)
- 這種情況容易出現(xiàn)冗余代碼。比如如果類(lèi)內(nèi)出現(xiàn)了多個(gè)變量帶有相同的前綴或者后綴,這意味著你可以考慮把他們提煉到某個(gè)組件內(nèi),或者考慮這個(gè)組件是否可以成為子類(lèi),使用提煉類(lèi)的手法來(lái)重構(gòu)。
什么是重構(gòu)
我們回過(guò)頭來(lái)看一下"什么是重構(gòu)"
- 不改變軟件可觀(guān)察行為的前提下,改善其內(nèi)部結(jié)構(gòu)
- 以提高理解性和降低修改成本
摘自《重構(gòu) - 改善既有代碼的設(shè)計(jì)》(下面簡(jiǎn)稱(chēng)《重構(gòu)》)
何時(shí)重構(gòu)?
我們需要明確的一點(diǎn)是: 重構(gòu)不是一件應(yīng)該特地?fù)艹鲆欢螘r(shí)間來(lái)做的事情。重構(gòu)不是目的,但是重構(gòu)可以幫助你把事情做好。
事不過(guò)三,三則重構(gòu)
- 重復(fù)性工作,既有的代碼無(wú)法幫助你輕松添加新特性時(shí)
- 修補(bǔ)bug時(shí),排查邏輯困難
- code review 可以讓他人來(lái)復(fù)審代碼檢查是否具備可讀性,可理解性
- 太多的代碼無(wú)注釋?zhuān)讶贿B自己都無(wú)法快速理清代碼邏輯
重構(gòu)的衡量指標(biāo)
- 數(shù)量: 代碼的行數(shù)
- 質(zhì)量: 代碼復(fù)雜度,耦合度,可讀性,架構(gòu)依賴(lài)復(fù)雜度等
- 成本: 花費(fèi)的時(shí)間
- 回報(bào)(成果): 支持后續(xù)功能的快速疊加,解決現(xiàn)有因代碼設(shè)計(jì)問(wèn)題無(wú)法優(yōu)化的矛盾等
抓重點(diǎn) 抓重點(diǎn)啦
說(shuō)了這么多廢話(huà),其實(shí)大家都明白沒(méi)有與實(shí)踐結(jié)合的理論都是空虛的。
但是 重構(gòu)和 設(shè)計(jì)模式一樣,也是需要一個(gè)"學(xué)習(xí)——領(lǐng)悟——突破"的過(guò)程。第一步的學(xué)習(xí)讓你了解基本的重構(gòu)手法,第二步的實(shí)踐勾起你對(duì)重構(gòu)手法的回憶以及重溫應(yīng)用,第三步的應(yīng)用以及實(shí)踐經(jīng)驗(yàn)激發(fā)你的思考,領(lǐng)悟以及總結(jié),以致于靈活運(yùn)用。
但凡是人,總是在不斷學(xué)習(xí),不斷溫習(xí),以達(dá)到具體場(chǎng)景具體應(yīng)用,靈活自如。
重構(gòu)是一個(gè)很大的話(huà)題,《重構(gòu)》作者本人也是經(jīng)歷了N多的項(xiàng)目,以及多年的經(jīng)驗(yàn)才總結(jié)出來(lái)的重構(gòu)技巧。
重構(gòu)技巧
《重構(gòu)》一書(shū)作者總結(jié)的重構(gòu)手法實(shí)在是太多了,只能通過(guò)圖片來(lái)展示一下所有作者總結(jié)的重構(gòu)列表。
具體的補(bǔ)充,大家可以看看《重構(gòu)》一書(shū)。


重構(gòu)的實(shí)踐
作者推薦的一種做法:
隨機(jī)挑選一個(gè)目標(biāo)先給自己選擇一個(gè)目標(biāo)(譬如“去掉一堆不必要的子類(lèi)”),然后朝著目標(biāo)前進(jìn),每一步走得小而堅(jiān)定沒(méi)把握就停下來(lái)當(dāng)你無(wú)法證明自己所做的一切能夠保證原有程序的邏輯和語(yǔ)義時(shí),請(qǐng)你停下來(lái)思考:既有的重構(gòu)是改善了還是毫無(wú)成果需要撤銷(xiāo)。- 保證每次重構(gòu)后的測(cè)試都能正常跑通
作為開(kāi)發(fā)者, 應(yīng)當(dāng)把重構(gòu)作為開(kāi)發(fā)的一部分,一邊開(kāi)發(fā)一邊重構(gòu)。在快速堆疊代碼,實(shí)現(xiàn)基本需求功能的基礎(chǔ)上,寫(xiě)好測(cè)試用例,保證功能不變,逐步重構(gòu)。
這也是我們團(tuán)隊(duì)要求每個(gè)人都掌握重構(gòu)這門(mén)必備技能的原因。優(yōu)秀的程序員應(yīng)當(dāng)盡量避免低質(zhì)量的代碼。
重構(gòu)案例
故事場(chǎng)景
有三種類(lèi)型的電影,顧客可以進(jìn)行租賃
租賃規(guī)則
價(jià)格計(jì)算規(guī)則:
普通片兒 —— 起步價(jià)2¥,超過(guò)2天的部分每天每部電影收費(fèi)1.3元
新片兒 —— 每天每部3元
兒童片 —— 起步價(jià)2¥,超過(guò)3天的部分每天每部電影收費(fèi)0.8元
積分計(jì)算規(guī)則:
每借一部電影積分加1,新片每部加2
原始代碼
程序結(jié)果:(請(qǐng)保證重構(gòu)后結(jié)果不變~)

類(lèi)圖


有興趣的可以先看看原始代碼,考慮一下其中的原始對(duì)象關(guān)系,再行考慮如何重構(gòu)代碼。原始代碼其實(shí)是有很多問(wèn)題可以挖掘的,下面是我們的討論整理:
- 劃分職責(zé)關(guān)系,遵循單一職責(zé)原則
- statement打印賬單函數(shù)承擔(dān)了很多功能,包括收費(fèi)計(jì)算,積分計(jì)算以及結(jié)果展示等等
- 解法1: 6.1 Extract Method(提煉函數(shù)) —— 最常用的重構(gòu)手法
- 解法2: 9.1 Decompose Conditional(分解條件表達(dá)式)
- 用戶(hù)類(lèi)中承擔(dān)了不屬于它的職責(zé),包括:收費(fèi)規(guī)則、積分規(guī)則。這些職責(zé)應(yīng)該是屬于電影類(lèi)型的。
- statement打印賬單函數(shù)承擔(dān)了很多功能,包括收費(fèi)計(jì)算,積分計(jì)算以及結(jié)果展示等等
- 整理清楚其中的業(yè)務(wù)邏輯,比如收費(fèi)規(guī)則和積分規(guī)則 - 見(jiàn)故事場(chǎng)景
- 不要直接訪(fǎng)問(wèn)對(duì)象的數(shù)據(jù)。容易發(fā)生其他對(duì)象改變?cè)搶?duì)象的數(shù)據(jù),而擁有該數(shù)據(jù)的對(duì)象卻一無(wú)所知。
- 解法:8.10 Encapsulate Field(封裝字段手法) —— 使數(shù)據(jù)和行為想分離
- 重構(gòu)不應(yīng)該被外界所感知,保證testcases依然可行
部分重構(gòu)
這里為了更好的展示重構(gòu)的手法,使用TS,根據(jù)上面的討論進(jìn)行了部分重構(gòu),重構(gòu)的方式其實(shí)是根據(jù)業(yè)務(wù)未來(lái)的擴(kuò)展方向而定,并沒(méi)有最優(yōu)解,有興趣的可以加入我們,拋出你的見(jiàn)解~
CODEPEN 執(zhí)行結(jié)果:
重構(gòu)后的類(lèi)圖關(guān)系

基本技巧
- 小步前進(jìn),頻繁測(cè)試(保證足夠的測(cè)試來(lái)支持你的重構(gòu)行動(dòng))
- 使用智能開(kāi)發(fā)工具(比如VSCode右鍵可以將過(guò)長(zhǎng)的函數(shù)代碼拆解函數(shù)化)
推薦書(shū)籍
《代碼整潔之道》
《重構(gòu) - 改善既有代碼的設(shè)計(jì)》
《修改代碼的藝術(shù)》
《代碼大全》
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
詳解JavaScript的變量和數(shù)據(jù)類(lèi)型
這篇文章主要介紹了詳解JavaScript的變量和數(shù)據(jù)類(lèi)型,需要的朋友可以參考下2015-11-11
bootstrap模態(tài)框?qū)崿F(xiàn)拖拽效果
這篇文章主要為大家詳細(xì)介紹了bootstrap模態(tài)框?qū)崿F(xiàn)拖拽效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
把JavaScript代碼改成ES6語(yǔ)法不完全指南(分享)
下面小編就為大家?guī)?lái)一篇把JavaScript代碼改成ES6語(yǔ)法不完全指南(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就想給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09
?javascript學(xué)數(shù)組中的foreach方法和some方法
這篇文章主要介紹了?javascript學(xué)數(shù)組中的foreach方法和some方法,文章相關(guān)內(nèi)容和代碼詳細(xì),具有一定的參考價(jià)值,需要的小伙伴可以參考一下,希望對(duì)你的學(xué)習(xí)有所幫助2022-03-03
js和jq使用submit方法無(wú)法提交表單的快速解決方法
下面小編就為大家?guī)?lái)一篇js和jq使用submit方法無(wú)法提交表單的快速解決方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考2016-05-05
Javascript 繼承機(jī)制的實(shí)現(xiàn)
要用ECMAScript實(shí)現(xiàn)繼承機(jī)制,首先從基類(lèi)入手。所有開(kāi)發(fā)者定義的類(lèi)都可作為基類(lèi)。出于安全原因,本地類(lèi)和宿主類(lèi)不能作為基類(lèi),這樣可以防止公用訪(fǎng)問(wèn)編譯過(guò)的瀏覽器級(jí)的代碼,因?yàn)檫@些代碼可以被用于惡意攻擊。2009-08-08

