重構 (Refactoring) 學習心得筆記 – 壞味道 (Bad Smell / Code Smell)

文章最後更新於 2021 年 2 月 11 日

何謂壞味道 (Bad Smell / Code Smell)

程式中需要進行重構的部分,被稱為壞味道或程式碼臭味 (Bad Smell / Code Smell)

當程式中有下列這些問題時,就可能存在壞味道:

  • 難以理解
    • 例:函式、類別名稱名命太籠統不明確;程式碼太長,或函式做太多事;Magic Number之類的神秘數字
  • 難以修改
    • 例:改個功能要改很多地方,重複的程式碼太多
  • 難以擴張
    • 例:沒有使用物件導向的方式去設計

有上述三種情況之一,代表該處隱藏重建的可能性

如何判斷程式中有壞味道?

可以透過以下的口訣來判斷程式中有壞味道:

重複了

在很多個地方散佈著相似或相同的程式碼的狀態,如果要做修改,就要改很多地方,才能使其保持一致,這樣的寫法很不好

太長了

函式或類別如果太長,或是函式要帶入的參數太多(超過3個),就會變得很難讓人理解,也不好維護

名不符其實

函式或變數名稱如果無法表達其代表的功能,會讓程式不容易被理解

公開太多了

變成public之後的函式可能被其他所呼叫,會有「這個函式若移除了,會不會有其他程式影響?」的疑慮

不太像是物件導向

比方說大量使用switch敘述跟if敘述,充滿分歧的流程處理,或是大量使primitive variables (int, double...etc),不建立專用的類別,這讓程式不夠物件導向,不易擴展與維護。

壞味道與重構

壞味道的重構行進方式:

  • 搜尋與確認程式碼中的壞味道
  • 搜尋重構目錄,找出此壞味道應透過何種重構方法解決
  • 根據重構方式進行程式碼之修改

有哪些壞味道?

這一部分大概說明有哪種幾壞味道與其特性,不會細講每種壞味道的重構方法,重構方法可參考外連網站的說明,或等日後再補充

Bloaters (肥哥的味道)

巨大的類別、很肥的Method或Classes,難以維護,通常積累了一段時間,可以再細分為下列這幾種特性

  • Long Method (過長函式,囉唆,迂迴)
    • 指的是函式中有太多行程式碼,通常超過10行就會讓人開始想問說這個Method或Function在做什麼
  • Large Class (巨大類別,肥Code,大泥球,God Class)
  • Primitive Obsession (基本型別痴迷狂,偷懶不用OOP設計)
    • 老是用 Primitives (int, String, etc.) 而不使用小的Class來處理一些簡單的事
    • 用太多常數(Constant),像是用USER_ADMIN_ROLE = 1來表示管理者權限
    • 在資料陣列中以字串常數做為屬性名稱,喜歡用String來表示任何東西
  • Long Parameter List (過長的參數列)
    • 方法的參數超過三個或四個,越來越長,越長就越難讀
    • 通常是慢慢加上去的,想多傳入一個參數就直接加在後面
  • Data Clumps (資料泥團)
    • 一個Long Parameter + Primitive Obsession 的範例: String printAddress( String country, String state, String city, String address1, String address2, String zipCode) {...} (什麼都String就是怎樣)
    • 像地址本身就可以用一個class Address來取代變成只傳入Address就好: String printAddress(Address address) {...}

Object-Orientation Abusers (物件導向濫用者的味道)

程式沒有遵守物件導向程式設計的原則,程式寫的很不OOP,可以再分為下列這幾種特性

  • Switch Statements
    • 用了太多層或太複雜的Switch或If-else statements讓整個程式流程很混亂
    • 通常可以利用OOP的多型的特性解決
  • Temporary Field (迷之暫時欄位)
    • 只有在某些特定情況才需要用到的暫時性Field,其他情況不需要,讓人困惑
  • Refused Bequest (被拒絕的遺贈)
    • 濫用繼承,子類別只用到了父類的某些方法或欄位,不需要用的到就像是被拒絕的遺贈
    • 例如Bird類別有Fly這個方法,有一個子類別是駝鳥但不會Fly,跟本不需要這個方法
  • Alternative Classes with Different Interfaces
    • 多個methods存在於不同的classes但做一樣的事

Change Preventers (改不動的味道)

這個壞味道指的是,當你要改一個地方的Code,你得必需要去改其他很多地方,這樣的Code一旦存在且越來越多,會導致程式越來越難以維護,改不動,有下列幾種特性

  • Divergent Change (發散式變化)
    • 當你只改了類別的一個東西,你發現你還需要改同類別的其他方法,舉例來說,當你新增了一個新的product type,你必需再去改其他的方法像是finding, displaying及ordering product,這並不是你這個change的重點
  • Shotgun Surgery (散彈修改)
    • 做一個小修改需後,還需要再對其他不同的類別做額外的小修改
  • Parallel Inheritance Hierarchies (平行繼承體系)
    • 當你加了一個subclass到某個類別,你同時必需加另一個類似的subclass到另一個類別下

Couplers (緊密耦合的味道)

發生在Class間存在了緊密耦合的關係,分為下列幾種特性

  • Feature Envy
    • 一個類別用了其他的類別方法勝過使用自己的,有點像是迷戀了別的類別的Feature
  • Inappropriate Intimacy
    • 一個類別用了太多其他類別的細節,例如直接使用別的類別的public fields
  • Message Chains
    • 一個方法串太多層去拿到結果,像是: $a->b()->c()->d()
  • Middle Man
    • 一個類別只做了一件事:就是去叫另一個類別做事,那要他幹嘛?

Dispensables (多餘的味道)

  • Comments (無用的註解)
    • Comment 通常都會說謊XD
    • 不該有沒用的Comment,程式最好是不需要註解就能理解
  • Duplicate Code (重複的程式)
    • 同樣的程式出現在不同的地方
  • Lazy Class (懶類別)
    • Class做的事太少,根本不需要它的存在
  • Data Class (資料類別)
  • Dead Code (死掉或無用的程式碼)
    • 一些變數、參數、方法、類別已經沒有人在使用的
  • Speculative Generality
    • 現階段根本用不到的程式,只是幻想以後會用到
    • YAGNI: Your ain’t gonna need it

Other Smells (其他壞味道)

心得

預防勝於治療,學習了解Code Smell的特徵與其未來所造成的成本,能讓工程師在寫程式的初期就避免掉一些技術債的產生,自己在寫程式的時候,就會注意到是否會產壞味道,另外在團隊做Code Review時,也能為程式品質做把關,不要讓有壞味道的程式進入專案程式碼中,有助於程式未來的可讀性及可維護性。

參考資料

文章同步刊載於Medium:重構 (Refactoring) 學習心得筆記 — 壞味道 (Bad Smell / Code Smell)

關於作者

卡哥
卡哥
我是Oscar (卡哥),前Yahoo Lead Engineer、高智商同好組織Mensa會員,超過十年的工作經驗,服務過Yahoo關鍵字廣告業務部門、電子商務及搜尋部門,喜歡彈吉他玩音樂,也喜歡投資美股、虛擬貨幣,樂於與人分享交流!

如果對文章內容有任何問題,歡迎在底下留言讓我知道。
如果你喜歡我的文章,可以按分享按鈕,讓更多的人看見我的文章。