程式碼的壞味道和重構

靜默虛空發表於2019-03-07

:notebook: 本文已歸檔到:「blog

第一次讀《重構:改善既有程式碼的設計》時,我曾整理過一個簡單的筆記。最近,因為參與一個重構專案,再一次溫習了《重構:改善既有程式碼的設計》。過程中,萌發了認真總結、整理重構方法的衝動,於是有了這系列文字。

程式碼的壞味道還有幾篇沒有完稿,後面我會陸續補充。。。

症與藥

對程式碼的壞味道的思考

“有病要早治,不要放棄治療”。多麼樸素的道理 ,人人都懂。

病,就是不健康。

人有病,可以通過打針、吃藥、做手術來進行治療。

如果把程式碼的壞味道(程式碼質量問題)比作病症,那麼重構就是治療程式碼的壞味道的藥。

個人認為,在重構這件事上,也可以應用治病的道理:

  • 防病於未然。 —— 春秋戰國時期的一代名醫扁鵲,曾經有個很著名的醫學主張:防病於未然。 我覺得這個道理應用於軟體程式碼的重構亦然。程式設計前要有合理的設計、程式設計時要有良好的程式設計風格,儘量減少問題。從這個層面上說,瞭解程式碼的壞味道,不僅僅是為了發現問題、解決問題。更重要的作用是:指導我們在程式設計過程中有意識的去規避這些問題。

  • 小病不醫,易得大病。 —— 劉備說過:“勿以善小而不為,勿以惡小而為之”。發現問題就及時修改,程式碼質量自然容易進入良性迴圈;反之,亦然。要重視積累的力量,別總以為程式碼出現點小問題,那都不是事兒。

  • 對症下藥。 —— 程式出現了問題,要分析出問題的根本,有針對性的制定合理的重構方案。大家都知道吃錯藥的後果,同樣的,瞎改還不如不改

  • 忌猛藥 —— 醫病用猛藥容易產生副作用。換一句俗語:步子大了容易扯著蛋。重構如果大刀闊斧的幹,那你就要有隨時可能撲街的心理準備。推倒重來不是重構,而是重寫。重構應該是循序漸進,步步為營的過程。當你發現重寫程式碼比重構程式碼更簡單,往往說明你早就該重構了。

重構的原則

前面把程式碼質量問題比作病症,而把重構比作藥。這裡,我們再進一步討論一下重構的原則。

何謂重構(What)

重構(Refactoring) 的常見定義是:不改變軟體系統外部行為的前提下,改善它的內部結構。

個人覺得這個定義有點生澀。不妨理解為:重構是給程式碼治病的行為。而程式碼有病是指程式碼的質量(可靠性、安全性、可複用性、可維護性)和效能有問題。

重構的目的是為了提高程式碼的質量和效能

注:功能不全或者不正確,那是殘疾程式碼。就像治病治不了殘疾,重構也解決不了功能問題。

為何重構(Why)

翻翻書,上網搜一下,談到重構的理由大體相同:

  • 重構改進軟體設計
  • 重構使軟體更容易理解
  • 重構幫助找到 bug
  • 重構提高程式設計速度

總之就是,重構可以提高程式碼質量

何時重構(When)

關於何時重構,我先引用一下 重構並非難在如何做,而是難在何時開始做 一文的觀點。

對於一個高速發展的公司來說,停止業務開發,專門來做重構專案,從來就不是一個可接受的選項,“邊開飛機邊換引擎”才是這種公司想要的。

我們不妨來衡量一下重構的成本和收益。

  • 重構的成本

    重構是有成本的,費時費力(時間、人力)不說,還有可能會使本來正常執行的程式出錯。所以,很多人都抱著“不求有功,但求無過”的心理得過且過。

    還有一種成本:重構使用較新且較為複雜的技術,學習曲線不平滑,團隊成員技術切換困難,短期內開發效率可能不升反降。

    但是,如果一直放任程式碼腐朽下去,技術債務會越來越沉重。當程式碼最終快要跑不動時,架構師們往往還是不得不使用激進的手段來治療程式碼的頑疾。但是,這個過程通常都是非常痛苦的,而且有著很高的失敗風險。

  • 重構的收益

    重構的收益是提高程式碼的質量和效能,並提高未來的開發效率。但是,應當看到,重構往往並不能在短期內帶來實際的效益,或者很難直觀看出效益。而對於一個企業來說,沒有什麼比效益更重要。換句話說,沒有實際效益的事,通常也沒有價值。很多領導,尤其是非技術方向的領導,並不關心你應用了什麼新技術,讓程式碼變得多麼優雅等等。

  • 重構的合適時機

    從以上來看,重構實在是個吃力不討好的事情。

    於是,很多人屈服於萬惡的 KPI 和要命的 deadline,一邊吐槽著以前的程式碼是垃圾,一邊自己也在造垃圾。

    但是,**重構本應該是個漸進式的過程,不是隻有傷筋動骨的改造才叫重構。**如果非要等到程式碼已經爛到病入膏肓,再使用激進方式來重構,那必然是困難重重,風險極高。

    《重構》書中提到的重構時機應該在新增功能、修復功能、審查程式碼時,不建議專門抽出時間專門做重構專案。

    我認為,其思想就是指:重構應該是在開發過程中實時的、漸進的演化過程。

  • 重構的不恰當時機

    但是,這裡我也要強調一下:不是所有軟體開發過程都一定要重構。

    較能凸顯重構價值的場景是:程式碼規模較大、生命週期還較長、承擔了較多責任、有一個較大(且較不穩定,人員流動頻繁)團隊在其上工作的單一程式碼庫。

    與之相反,有一些場景的重構價值就很小:

    • 程式碼庫生命週期快要走到尾聲,開發逐漸減少,以維護為主。
    • 程式碼庫當前版本馬上要釋出了,這時重構無疑是給自己找麻煩。
    • 重構代價過於沉重:重構後功能的正確性、穩定性難以保障;技術過於超前,團隊成員技術遷移難度太大。

如何重構(How)

重構行為在我看來,也是可以分層級的。由高到低,越高層級難度越大:

  • 服務、資料庫 現代軟體往往業務複雜、龐大。使用微服務、資料遷移來拆分業務,降低業務複雜度成為了主流。但是,這些技術的測試、部署複雜,技術難度很高。

  • 元件、模組、框架 元件、模組、框架的重構,主要是針對程式碼的設計問題。解決的是程式碼的整體結構問題。需要對框架、設計模式、分散式、併發等等有足夠的瞭解。

  • 類、介面、函式、欄位 《重構》一書提到了**“程式碼的壞味道”**以及相關的重構方法。這些都是對類、介面、函式、欄位級別程式碼的重構手段。由於這一級別的重構方法較為簡單,所以可操作性較強。具體細節可以閱讀《程式碼的壞味道》篇章。

前兩種層級的重構已經涉及到架構層面,影響較大,難度較高,如果功力不夠不要輕易變動。由於這兩個層級涉及領域較廣,這裡不做論述。

此處為分割線。下面是程式碼的壞味道系列。。。

程式碼的壞味道

《重構:改善既有程式碼的設計》中介紹了 22 種程式碼的壞味道以及重構手法。這些壞味道可以進一步歸類。我總覺得將事物分類有助於理解和記憶。所以本系列將壞味道按照特性分類,然後逐一講解。


程式碼的壞味道和重構

程式碼壞味道之程式碼臃腫

程式碼臃腫(Bloated)這組壞味道意味著:程式碼中的類、函式、欄位沒有經過合理的組織,只是簡單的堆砌起來。這一型別的問題通常在程式碼的初期並不明顯,但是隨著程式碼規模的增長而逐漸積累(特別是當沒有人努力去根除它們時)。

程式碼壞味道之濫用物件導向

濫用物件導向(Object-Orientation Abusers)這組壞味道意味著:程式碼部分或完全地違背了物件導向程式設計原則。

程式碼壞味道之變革的障礙

變革的障礙(Change Preventers)這組壞味道意味著:當你需要改變一處程式碼時,卻發現不得不改變其他的地方。這使得程式開發變得複雜、代價高昂。

程式碼壞味道之非必要的

非必要的(Dispensables)這組壞味道意味著:這樣的程式碼可有可無,它的存在反而影響整體程式碼的整潔和可讀性。

程式碼壞味道之耦合

耦合(Couplers)這組壞味道意味著:不同類之間過度耦合。

擴充套件閱讀

參考資料

相關文章