:notebook: 本文已歸檔到:「blog」
第一次讀《重構:改善既有程式碼的設計》時,我曾整理過一個簡單的筆記。最近,因為參與一個重構專案,再一次溫習了《重構:改善既有程式碼的設計》。過程中,萌發了認真總結、整理重構方法的衝動,於是有了這系列文字。
程式碼的壞味道還有幾篇沒有完稿,後面我會陸續補充。。。
症與藥
對程式碼的壞味道的思考
“有病要早治,不要放棄治療”。多麼樸素的道理 ,人人都懂。
病,就是不健康。
人有病,可以通過打針、吃藥、做手術來進行治療。
如果把程式碼的壞味道(程式碼質量問題)比作病症,那麼重構就是治療程式碼的壞味道的藥。
個人認為,在重構這件事上,也可以應用治病的道理:
-
防病於未然。 —— 春秋戰國時期的一代名醫扁鵲,曾經有個很著名的醫學主張:防病於未然。 我覺得這個道理應用於軟體程式碼的重構亦然。程式設計前要有合理的設計、程式設計時要有良好的程式設計風格,儘量減少問題。從這個層面上說,瞭解程式碼的壞味道,不僅僅是為了發現問題、解決問題。更重要的作用是:指導我們在程式設計過程中有意識的去規避這些問題。
-
小病不醫,易得大病。 —— 劉備說過:“勿以善小而不為,勿以惡小而為之”。發現問題就及時修改,程式碼質量自然容易進入良性迴圈;反之,亦然。要重視積累的力量,別總以為程式碼出現點小問題,那都不是事兒。
-
對症下藥。 —— 程式出現了問題,要分析出問題的根本,有針對性的制定合理的重構方案。大家都知道吃錯藥的後果,同樣的,瞎改還不如不改。
-
忌猛藥 —— 醫病用猛藥容易產生副作用。換一句俗語:步子大了容易扯著蛋。重構如果大刀闊斧的幹,那你就要有隨時可能撲街的心理準備。推倒重來不是重構,而是重寫。重構應該是循序漸進,步步為營的過程。當你發現重寫程式碼比重構程式碼更簡單,往往說明你早就該重構了。
重構的原則
前面把程式碼質量問題比作病症,而把重構比作藥。這裡,我們再進一步討論一下重構的原則。
何謂重構(What)
重構(Refactoring)
的常見定義是:不改變軟體系統外部行為的前提下,改善它的內部結構。
個人覺得這個定義有點生澀。不妨理解為:重構是給程式碼治病的行為。而程式碼有病是指程式碼的質量(可靠性、安全性、可複用性、可維護性)和效能有問題。
重構的目的是為了提高程式碼的質量和效能。
注:功能不全或者不正確,那是殘疾程式碼。就像治病治不了殘疾,重構也解決不了功能問題。
為何重構(Why)
翻翻書,上網搜一下,談到重構的理由大體相同:
- 重構改進軟體設計
- 重構使軟體更容易理解
- 重構幫助找到 bug
- 重構提高程式設計速度
總之就是,重構可以提高程式碼質量。
何時重構(When)
關於何時重構,我先引用一下 重構並非難在如何做,而是難在何時開始做 一文的觀點。
對於一個高速發展的公司來說,停止業務開發,專門來做重構專案,從來就不是一個可接受的選項,“邊開飛機邊換引擎”才是這種公司想要的。
我們不妨來衡量一下重構的成本和收益。
-
重構的成本
重構是有成本的,費時費力(時間、人力)不說,還有可能會使本來正常執行的程式出錯。所以,很多人都抱著“不求有功,但求無過”的心理得過且過。
還有一種成本:重構使用較新且較為複雜的技術,學習曲線不平滑,團隊成員技術切換困難,短期內開發效率可能不升反降。
但是,如果一直放任程式碼腐朽下去,技術債務會越來越沉重。當程式碼最終快要跑不動時,架構師們往往還是不得不使用激進的手段來治療程式碼的頑疾。但是,這個過程通常都是非常痛苦的,而且有著很高的失敗風險。
-
重構的收益
重構的收益是提高程式碼的質量和效能,並提高未來的開發效率。但是,應當看到,重構往往並不能在短期內帶來實際的效益,或者很難直觀看出效益。而對於一個企業來說,沒有什麼比效益更重要。換句話說,沒有實際效益的事,通常也沒有價值。很多領導,尤其是非技術方向的領導,並不關心你應用了什麼新技術,讓程式碼變得多麼優雅等等。
-
重構的合適時機
從以上來看,重構實在是個吃力不討好的事情。
於是,很多人屈服於萬惡的 KPI 和要命的 deadline,一邊吐槽著以前的程式碼是垃圾,一邊自己也在造垃圾。
但是,**重構本應該是個漸進式的過程,不是隻有傷筋動骨的改造才叫重構。**如果非要等到程式碼已經爛到病入膏肓,再使用激進方式來重構,那必然是困難重重,風險極高。
《重構》書中提到的重構時機應該在新增功能、修復功能、審查程式碼時,不建議專門抽出時間專門做重構專案。
我認為,其思想就是指:重構應該是在開發過程中實時的、漸進的演化過程。
-
重構的不恰當時機
但是,這裡我也要強調一下:不是所有軟體開發過程都一定要重構。
較能凸顯重構價值的場景是:程式碼規模較大、生命週期還較長、承擔了較多責任、有一個較大(且較不穩定,人員流動頻繁)團隊在其上工作的單一程式碼庫。
與之相反,有一些場景的重構價值就很小:
- 程式碼庫生命週期快要走到尾聲,開發逐漸減少,以維護為主。
- 程式碼庫當前版本馬上要釋出了,這時重構無疑是給自己找麻煩。
- 重構代價過於沉重:重構後功能的正確性、穩定性難以保障;技術過於超前,團隊成員技術遷移難度太大。
如何重構(How)
重構行為在我看來,也是可以分層級的。由高到低,越高層級難度越大:
-
服務、資料庫 現代軟體往往業務複雜、龐大。使用微服務、資料遷移來拆分業務,降低業務複雜度成為了主流。但是,這些技術的測試、部署複雜,技術難度很高。
-
元件、模組、框架 元件、模組、框架的重構,主要是針對程式碼的設計問題。解決的是程式碼的整體結構問題。需要對框架、設計模式、分散式、併發等等有足夠的瞭解。
-
類、介面、函式、欄位 《重構》一書提到了**“程式碼的壞味道”**以及相關的重構方法。這些都是對類、介面、函式、欄位級別程式碼的重構手段。由於這一級別的重構方法較為簡單,所以可操作性較強。具體細節可以閱讀《程式碼的壞味道》篇章。
前兩種層級的重構已經涉及到架構層面,影響較大,難度較高,如果功力不夠不要輕易變動。由於這兩個層級涉及領域較廣,這裡不做論述。
此處為分割線。下面是程式碼的壞味道系列。。。
程式碼的壞味道
《重構:改善既有程式碼的設計》中介紹了 22 種程式碼的壞味道以及重構手法。這些壞味道可以進一步歸類。我總覺得將事物分類有助於理解和記憶。所以本系列將壞味道按照特性分類,然後逐一講解。
程式碼壞味道之程式碼臃腫
程式碼臃腫(Bloated)這組壞味道意味著:程式碼中的類、函式、欄位沒有經過合理的組織,只是簡單的堆砌起來。這一型別的問題通常在程式碼的初期並不明顯,但是隨著程式碼規模的增長而逐漸積累(特別是當沒有人努力去根除它們時)。
程式碼壞味道之濫用物件導向
濫用物件導向(Object-Orientation Abusers)這組壞味道意味著:程式碼部分或完全地違背了物件導向程式設計原則。
程式碼壞味道之變革的障礙
變革的障礙(Change Preventers)這組壞味道意味著:當你需要改變一處程式碼時,卻發現不得不改變其他的地方。這使得程式開發變得複雜、代價高昂。
程式碼壞味道之非必要的
非必要的(Dispensables)這組壞味道意味著:這樣的程式碼可有可無,它的存在反而影響整體程式碼的整潔和可讀性。
程式碼壞味道之耦合
耦合(Couplers)這組壞味道意味著:不同類之間過度耦合。
擴充套件閱讀
參考資料
- 重構——改善既有程式碼的設計 - by Martin Fowler
- https://sourcemaking.com/refactoring