靠譜程式設計師必備技能——重構
http://gitbook.cn/books/591837cbc9b8f67d6a6a94df/index.html?utm_source=tuicool&utm_medium=referral
為什麼要重構
你可能正在面對一個遺留系統,增加一個需求要改動好幾個檔案,定位 Bug 經常要花掉一整天時間,修復一個 Bug 可能又製造了 3 個新的 Bug。你也可能會為了軟體設計和同事爭得面紅耳赤,討論如何應對未來可能出現的需求變化。
為了開發一個新需求,你開啟一份原始碼,完全不知所云嘛,你吐槽著誰能寫出如此不堪入目的程式碼,於是決定檢視版本記錄,把這個傢伙找出來鄙視一下。然後你在提交歷史裡看到了自己的名字... 恭喜你,你進步了。如果你是一個積極進取的程式設計師,通常在幾個月甚至幾個星期之後就認不出自己寫的程式碼。你總能發現更好的實現方式,讓程式碼更加優雅。 隨著增加新特性或需求變更,程式碼會變得越來越難以維護。敏捷軟體開發的十二條原則中有一條是:我們始終擁抱需求變化,哪怕是在軟體開發的後期。為了達到這種狀態,我們就要在開發過程中持續地優化程式碼。
而重構這項技術,為我們提供了一種更可控的方式來優化程式碼。
重構是什麼
重構,通常指的是「程式碼重構」,起源於 Smalltalk 圈子。
在日常工作中,我們把重構既作為名詞又作為動詞來使用,作為名詞時,它的意思是:
對軟體內部結構的一種調整,目的是在不改變軟體可觀察行為的前提下,提高其可理解性,降低其修改成本。
所以我們會說,「這裡需要做一個重構」,「這個重構有點問題」等。
而在其它時候,我們也會說:「我們來重構一下這段程式碼吧」,「我正在重構一個遺留系統」,這時就是把重構當做動詞在用,它的意思是:
使用一系列重構手法,在不改變軟體可觀察行為的前提下,調整其結構。
重構本質上是一種程式碼整理技術,這項技術使得程式碼整理的效率更高,風險更小。
如何做
接下來從幾個方面來說說如何做重構:
-
什麼時候開始
-
什麼時候停止
-
前提條件
-
重構的過程
什麼時候開始
重構不應該是一個單獨的環節,應該融入到開發軟體編寫程式碼的過程中,就像使用版本控制系統提交程式碼一樣,是一個必須做的動作。你不會跟專案經理說,我需要申請一段時間來提交程式碼,所以也不用說服專案經理給你時間重構。你可以在開發新功能,修復 Bug 的過程中就把重構做了,除了你的程式設計師同伴,沒有人知道你做了什麼。而他們會認為你做了一件了不起的事情,因為你讓程式碼結構更清晰了,以後新增新特性就會更容易,而 Bug 也無處藏身。
如果你採用 TDD 的方式(測試驅動開發),那重構已完全融入到了開發過程中。如果沒有采用 TDD,通常有四個時機可以考慮要不要重構:
事不過三
如果有段程式碼讓你修改起來很不舒服,前兩次還可以忍耐,第三次就無需再忍了,果斷操起 IDE 重構之。因為出現了三次修改,說明有很大概率以後還會修改,這是一筆劃算的投資。
新增新功能
有時候我們發現要新增一個新功能很難,我們可以對程式碼做一些重構,讓新增新功能變得容易。
修復缺陷
在修 Bug 時,我們大部分的時間會花在定位 Bug 上,為什麼這麼難以找到呢?多半是因為程式碼結構不清晰,如果程式碼在同一抽象層次上,每個方法都在 10 行以內,每個方法名和變數名都能清晰地表達意圖,Bug 就再無藏身之處。所以,通過重構程式碼,可以讓 Bug 自動浮現出來。
程式碼評審
Code Review 已是一個廣泛採用的實踐,在 Code Review 時,其他程式設計師會提出程式碼修改的意見,記錄下來,等 Code Review 結束之後就可以開始重構了。
什麼時候停止
重構到什麼時候,我們就認為可以停止了呢?
有兩個標準可以參考,一個是「簡單設計」的四條原則:
-
通過所有測試
-
沒有重複
-
表達意圖
-
最少化程式元素(類,介面,變數,方法等)
另一個是滿足《Clean Code》(整潔程式碼)的要求。
前提條件
現代 IDE,尤其是 JetBrains 公司的一系列產品,支援常用的重構手法,極大地降低了重構的風險。但為了保證不改變軟體的可觀察行為,還是需要完善的測試。我也做過一些沒有測試程式碼保護的重構,通常會加一個端到端測試以保證不破壞最重要的功能。實在很難編寫測試程式碼,至少也要手工測試來保證重構真的沒有改變軟體行為。
另一個重要前提是,使用版本控制系統,比如 Git。因為我們的重構並不一定總是令人滿意,也有可能出現錯誤,導致軟體變得不可用,所以最好是小步提交,以保證可以隨時放棄變更,回到上一次滿意的狀態。
重構的過程
重構的基本步驟是:
-
測試保護
-
識別味道
-
採用手法
-
執行測試
-
提交程式碼
測試保護
如果沒有測試程式碼,就要先新增測試程式碼。如果有測試程式碼,先執行一下,保證在開始重構之前,測試是執行通過的。還要認真審查一下測試程式碼,看是否有遺漏一些場景,有遺漏的話要補充遺漏的測試場景。
識別味道
怎麼知道哪些程式碼需要重構呢?首先,程式碼是可以工作的,我們並不能說它有問題,但它又不像我們期望的那樣好。受 Kent Beck 剛出生的女兒的使用的尿布的啟發,Martin Fowler 和 Kent Beck 決定用「味道」這個詞來表示需要重構的程式碼。他們在《重構》一書中列舉了 22 中常見的味道,如果你看《Clean Code》的話,會發現還有更多。不過,他們並沒有給出一個具體的標準,而是需要我們的直覺來判斷。比如多大的類算「過大的類」?多少行程式碼算「過長的方法」?這些需要自行判斷,而直覺的形成有兩種方法,一是隨著編碼經驗的增多自然形成,另一種更快的方式是大量閱讀優秀的開原始碼,提高自己的程式碼審美。
《重構》一書中的味道可以分為五類:
-
膨脹劑
-
OO 使用不合理
-
難以修改
-
可有可無
-
耦合
書中都有詳細的解釋,這裡不再贅述。

 發散式變化和散彈式修改是比較容易混淆的兩個味道。前者指一個類的職責過多,有很多因素會引起它的變化,具體的表現就是,不同的需求都會修改同一個檔案,導致經常衝突,不能順利地並行開發。後者指的是改一個需求要修改很多個檔案,說明沒有把強內聚的程式碼歸攏到一起。
 大部分的註釋都是沒有必要的,註釋應該描述「做了什麼」和「為什麼做」而不是「怎麼做」,方法體內的註釋基本都可以通過抽取方法並指定一個有意義的名字來解決。很多為了應對未來需求變化而寫的程式碼基本永遠不會被執行。
 你可能發現了,有些味道是比較容易識別的,比如重複程式碼,註釋等。而有些就比較高階,比如特性依戀,中間人等,要識別高階味道,需要理解面向物件的特性和設計原則。
採用手法
識別到味道之後,就要知道有什麼對應的手法可以消除這個味道,執行完這個手法之後程式碼會變成什麼樣子。
在《重構》一書中,列舉了 66 個常用手法,可以分為六大類:
-
重組函式
-
搬移特性
-
組織資料
-
簡化條件
-
簡化呼叫
-
處理概括
這些手法在書中都有詳細的講解,我就不在這裡重複了。只整理出來,給大家一個巨集觀的印象:

   
執行測試
在採用了手法修改程式碼之後,就要執行測試以確保真的沒有改變軟體的行為。可能有時會發現,做了重構之後測試會失敗,但實現並沒有問題,我們需要修改測試程式碼讓它成功。這就說明測試寫的不合理,給重構帶來了負擔,所以我們測試的粒度要把握好,太細的粒度就會增加維護成本。比如,有些人會給每個私有方法都寫單元測試,那有可能採用「行內函數」這個手法之後這個方法就不存在了,就需要修改測試。這裡說起來話就長了,以後再寫一篇如何寫有效的測試的文章吧。重點是重構之後,一定要執行測試,不管是手工測試或自動化測試。
提交程式碼
最後,如果你採用了一個比較複雜的手法,或者即將採用一個複雜的手法,最好先提交一下程式碼,以保證出現意外後能快速回滾,避免浪費時間。
重構要採取「小步快跑」的原則,儘量採用安全的手法,讓測試一直處於通過的狀態。 從低階的壞味道開始,消除低階味道之後,高階味道才會浮現出來。
進階
重構與設計的關係
在沒有重構這個技術之前,廣泛採用的是 Big Front Design,在開始編碼之前要進行非常詳細的設計,考慮應對未來出現的各種變化。而有了重構技術之後,前期設計的壓力就小了,畢竟可以隨時通過重構來改善設計,應對變化。所以你大可不必一上來就應用《設計模式》把程式碼搞複雜,先用簡單的實現滿足當前需求即可。等變化真正來臨時,再通過重構技術調整設計,模式給我們提供了一個方向,但並不是最終目標。還記得簡單設計的四條原則嗎?通過測試,沒有重複,表達意圖,最少元素。除了這四條原則,還有 SOLID,DRY,KISS 等設計原則。只要最終的程式碼符合好的原則,乾淨整潔沒有壞味道,管它符不符合某個模式呢?!
大型遺留系統的重構
對於程式碼上百萬,千萬行的遺留系統,怎麼重構呢?滿地都是壞味道,一點點去重構,什麼時候是個頭?
這時,選擇哪些程式碼來重構就非常重要,影響到投資回報。如果對程式碼進行分類,將會得出幾種型別:
-
不會被執行的爛程式碼
-
執行穩定,基本不會改動的爛程式碼
-
經常發現 Bug 的爛程式碼
-
經常需要變更的爛程式碼
不會被執行的程式碼,直接刪除就好了。執行穩定的又不需要改動的,動它反而可能引入風險,當然,在時間充裕的情況下,還是可以重構的。真正有價值,值得重構的,投入產出比最高的,是經常出問題和經常會有需求變更的爛程式碼。優化了這部分程式碼,可以減少 Bug 和進行需求變更的時間。
總結
好了。關於重構我想分享的就是這些,我們來回顧一下:
為什麼要重構?
為了讓軟體始終可以維護,保證開發效率。
什麼是重構?
一種以可控的方式整理程式碼的技術,在不改變軟體可觀察行為的前提下改善其內部結構。
什麼時候開始?
事不過三,新增功能,修復 Bug,程式碼評審時。
什麼時候停止?
重構到符合簡單設計四條原則的 Clean Code。
前提條件
測試保護,版本控制。
重構的過程
執行測試,識別味道(常見的 22 種),採用手法(66 個),執行測試,提交程式碼。
重構與設計的關係
有了重構技術,我們不用在前期做非常詳細的設計,做適當的設計,然後通過重構讓設計浮現出來。不用在乎軟體是否符合模式,只要符合原則即可。
大型遺留系統的重構
在經常需要修改的爛程式碼上做重構才有最大收益。
最後推薦一些學習資源:
相關推薦
靠譜程式設計師必備技能——重構
http://gitbook.cn/books/591837cbc9b8f67d6a6a94df/index.html?utm_source=tuicool&utm_medium=referral 為什麼要重構 你可能正在面對一個遺留系統,增加一個需求要改動好幾
招聘靠譜程式設計師系列:1 程式碼風格優化與糾錯
問:風格糾錯 答: 使用NS_ENUM而不是C語言型別的列舉enum typedef NS_ENUM(NSInteget, XBYGender) { //使用gender比sex正式 XBYGenderMan, XBYGenderWo
招聘靠譜程式設計師系列:2 什麼情況使用 weak 關鍵字,相比 assign 有什麼不同?
答: 1、什麼情況下使用weak: a、在ARC中修飾代理 b、使用@IBOutlet連線控制元件 c、當block會造成迴圈引用 2、與assign的不同 assign可以用於非物件型別,而weak必須用於物件型別 參考資料: 1、區別
Java程式設計師必備技能:程式設計師如何閱讀Java原始碼
對於程式設計師來說,對原始碼真的是有又愛又恨,愛的是原始碼蘊藏的知識太多,學會了對技術的提高大有裨益,恨的是原始碼往往非常難啃,對於初級Java程式設計師來說閱讀原始碼並不是那麼容易。本文小編分享一些閱讀原始碼的技巧和心得,大家有其他的想法也可以一起交流。 正文: 原始碼閱讀,我覺
月薪3萬的Java程式設計師必備技能有哪些?
拋開工作經驗,專案經驗,學歷背景,單從技術點分析,哪些方面可以判斷一個Java程式設計師的技術紮實程度,怎樣才能知道他值多少月薪呢?本文為你解答。本著理論結合實踐的方法,我一般都不問上面這種純知識和理論性問題,而是讓他寫一段程式來證明HashMap是執行緒不安全的。然後,再讓
程式設計師必備技能之 Git 的體系結構與歷史
十幾年前,Linux 之父 Linus Torvalds 在個人休假時,發現自己掌控下的 Linux 核心在開發過程中遇到了一些問題,於是鬱悶無比,經過多天的琢磨與實踐之後研發出一款小工具——Git,以望幫助更多的開發者有效、高速地處理從很小到非常龐大的專案版本管理。萬萬沒想到
程式設計師必備技能
--必備技能-- 熟練開發工具 做為一名程式設計師至少熟練掌握兩到三種開發工具的使用,這是程式設計師的立身之本,其中C/C++和JAVA是重點推薦的開發工具,C/C++以其高效率和高度的靈活性成為開發工具中的利器,很多系統級的軟體還是用C/C編寫。而JAVA的跨平臺和
C#回顧學習筆記二十五:程式設計師必備技能,除錯
編寫程式碼免不了會出現各種錯誤,在執行時會發現不是拋異常就是執行結果跟預期結果有差異。這時候就需要思考如何去解決這個問題。 首先應該清楚一點:程式碼都是從頭開始,一句一句往下執行。程式設計師在發現程式碼有問題時,首先要想到的就是,這個錯誤可能發生在第幾句程式碼。有經驗的程式
程式設計師必備技能——怎樣快速接手一個專案
作為一個程式設計師,我們很少能從頭到尾參與一個新專案的開發。如果你經常開發的是新專案,那你真是太幸福了。 更多的情況是半路進入一個專案組進行開發,或者是有其他同事離職了,之前由他維護的系統轉交給你維護。 還有一種情況就是領導不知道從哪裡弄過來一個系統和一堆文件,然後就直接就把系統交給你了維護了。 遇到以上
程式設計師必備,來看看2019年需求最高的TOP 10項技能!
對於希望充分利用資料的企業而言,掌握資料探勘和處理相關技能的人才需求仍然很高,DevOps工程師、Python程式設計師、資料工程師和機器學習工程師已經成為企業的核心技術人才。本文,我們列出了IT專業人員在人才競爭中必須具備的十大關鍵技能,這也是2019年技術人員需要掌握的關鍵技能。雖然新技術和工具
不難!月薪50k程式設計師必備5大核心技能
最近看到過一個真實的事件,程式設計師小A 和小B 同時應聘一家公司。 小A有7年的Java工作經驗,小B只有2年。但是最終,小B的月薪卻是小A的2倍。 為什麼,同樣的Java工程師,月薪卻差了這麼多? 月薪50k的程式設計師說出了真正的原因:你否掌握了真正核心技能決定了你的薪水水
程式設計師必備畫圖技能之——流程圖
作為一個程式設計師,經常需要畫流程圖來展示系統的執行流程或者是來表述某些業務的業務邏輯。可以說畫流程圖已經是程式設計師必須掌握的一個技能了。本文就從什麼是流程圖、流程圖的適用場景以及怎麼畫好一個流程圖這幾個方面來介紹下流程圖的基本知識。 什麼流程圖 流程圖=流程+圖。 流程:Flow, 是指特定主體為了滿
程式設計師必備畫圖技能之——時序圖
## 什麼是時序圖 時序圖(Sequence Diagram),又名序列圖、循序圖,是一種UML互動圖。它通過描述物件之間傳送訊息的時間順序顯示多個物件之間的動態協作。 ## 使用場景 時序圖的使用場景非常廣泛,幾乎各行各業都可以使用。當然,作為一個軟體工作者,我這邊主要列舉和軟體開發有關的場景。 *
程式設計師必備的網站推薦
程式設計師必備的網站推薦 一、開原始碼託管平臺 1. GitHub(https://github.com) gitHub是一個面向開源及私有軟體專案的託管平臺,因為只支援git 作為唯一的版本庫格式進行託管,故名gitHub。 gitHub於
程式設計師必備法寶(漲薪祕籍)
職業要求 一般的程式設計師都有四年的在專業領域的學習,需要一個在程式領域的學士學位獲得者,不論是數學方面的還是工程方面的都是可以的。 程式設計師 大約有20%的人在這一領域的電腦科學和工程學擁有更高的學位。還有很小一部分程式設計師是自學的,儘管一些專業性的學校或者綜合大學可以提供
這是一份程式設計師必備的開源面試圖譜 ~
金九銀十的秋招季,許多人都在尋找更好的工作機會。那麼首先你要考慮的就是面試問題,面試除了你平常的積累以外,面試前的準備也是十分重要的 今天要和大家分享的開源專案是一個面試圖譜 —— Interview Map。 截至今日,
珍藏版Chrome外掛送給你們,不僅是程式設計師必備
大家好,消失了幾天我又滿血復活歸來了,最近這幾天太忙了一直在加班工作,這不昨天又幹到凌晨一點,今天早上七點就起來了,到現在還都沒有休息,現在只剩半血了,不對應該說現在只能爬著走了,但是一想到幾天沒有更新文章了,還是強忍著上來更新,證明我還行。 以前啊,熬夜隨隨便便,現在隨著年齡的增長真是不行了,一天都是懵的
【 English 】程式設計師必備單詞
原文地址 application 應用程式 應用、應用程式 application framework 應用程式框架、應用框架 應用程式框架 architecture 架構、系統架構 體系結構 argument 引數(傳給函式的值)。叄見 parameter 叄數、實
轉載:程式設計師必備基礎
因為和同事有約定再加上LZ自己也喜歡做完一件事之後進行總結,因此有了這篇文章。這篇文章大部分內容都是面向整個程式設計師群體的,當然因為LZ本身是做Java開發的,因此有一部分內容也是專門面向咱們Java程式設計師的。 簡單先說一下,LZ座標杭州,13屆本科畢業,算上年前在阿里巴巴B2B事業部的面
程式設計師必備知識:常見進位制轉換
價值不是你擁有多少,而是你留下多少。