code smells可以理解為代碼中讓人感覺到不舒服的地方??赡苁谴a規(guī)范問題,也可能是設(shè)計上的缺陷。
很多時候一段代碼符合基本邏輯,能夠正常運行,并不代表它是不“丑”的。代碼中可能會存在諸如可讀性差、結(jié)構(gòu)混亂、重復(fù)代碼太多、不夠健壯等問題。
示例代碼
上述代碼實現(xiàn)了一個簡單的“員工管理系統(tǒng)”。
Employee 類代表公司里的員工,有姓名、角色、假期等屬性??梢哉埣伲ǎ?,或者單獨請一天,或者以 5 天為單位將假期兌換為報酬
HourlyEmployee 和 MonthlyEmployee 分別代表以時薪或者月薪來計算工資的員工
Company 類代表公司,可以招收員工()、返回特定角色的員工列表(如 )、發(fā)放薪資等()
code smells
上面的代碼中存在著很多可以改進的地方。
用 Enum 類型替代 str 作為員工的 role 屬性
上面的 類使用了 類型來存儲 屬性的值,比如用 代表經(jīng)理,用 代表實習(xí)生。
實際上 String 過于靈活,可以擁有任何含義,用來表示角色屬性時不具有足夠清晰的指向性。不同的拼寫規(guī)則和大小寫習(xí)慣都會導(dǎo)致出現(xiàn)錯誤的指向,比如 和 , 和 ??梢允褂?Enum 替代 str。
修改 類中 屬性的定義:
類中 等方法也做相應(yīng)的修改:
方法中使用新的 role 創(chuàng)建員工對象:
消除重復(fù)代碼
類中有一個功能是返回特定角色的員工列表,即 、、 三個方法。
這三個方法實際上有著同樣的邏輯,卻分散在了三個不同的函數(shù)里??梢院喜⒊梢粋€方法來消除重復(fù)代碼。
同時將 函數(shù)中的 、、 都改為如下形式:
盡量使用內(nèi)置函數(shù)
上面版本中的 方法,包含了一個 循環(huán)。實際上該部分邏輯可以使用 Python 內(nèi)置的列表推導(dǎo)來實現(xiàn)。
合理的使用 Python 內(nèi)置函數(shù)可以使代碼更短、更直觀,同時內(nèi)置函數(shù)針對很多場景在性能上也做了一定的優(yōu)化。
更清晰明確的變量名
舊版本:
新版本:
isinstance
當(dāng)你在代碼的任何地方看到 這個函數(shù)時,都需要特別地加以關(guān)注。它意味著代碼中有可能存在某些有待提升的設(shè)計。
比如代碼中的 函數(shù):
這里 的使用,實際上在 函數(shù)中引入了對 的子類的依賴。這種依賴導(dǎo)致各部分代碼之間的職責(zé)劃分不夠清晰,耦合性變強。
方法需要與 的子類的具體實現(xiàn)保持同步。每新增一個新的員工類型( 的子類),此方法中的 也就必須再新增一個分支。即需要同時改動不同位置的兩部分代碼。
可以將 的實現(xiàn)從 類轉(zhuǎn)移到具體的 子類中。即特定類型的員工擁有對應(yīng)的報酬支付方法,公司在發(fā)薪時只需要調(diào)用對應(yīng)員工的 方法,無需實現(xiàn)自己的 方法。由 引入的依賴關(guān)系從而被移除。
再把 函數(shù)中的 改為 。
由于每一個特定的 子類都需要實現(xiàn) 方法,更好的方式是將 實現(xiàn)為虛擬基類, 成為子類必須實現(xiàn)的虛擬方法。
Bool flag
類中的 方法有一個名為 的參數(shù)。它是布爾類型,作為一個開關(guān),來決定某個員工是請一天假,還是以 5 天為單位將假期兌換為報酬。
這個開關(guān)實際上導(dǎo)致了 方法包含了兩種不同的職責(zé),只通過一個布爾值來決定具體執(zhí)行哪一個。
函數(shù)原本的目的就是職責(zé)的分離。使得同一個代碼塊中不會包含過多不同類型的任務(wù)。
因此 方法最好分割成兩個不同的方法,分別應(yīng)對不同的休假方式。
Exceptions
方法中有一步 代碼。但該部分代碼實際上對 Exception 沒有做任何事。對于 Exception 而言:
如果需要 catch Exception,就 catch 特定類型的某個 Exception,并對其進行處理;如果不會對該 Exception 做任何處理,就不要 catch 它。
在此處使用 會阻止異常向外拋出,導(dǎo)致外部代碼在調(diào)用 時獲取不到異常信息。此外,使用 而不是某個特定類型的異常,會導(dǎo)致所有的異常信息都被屏蔽掉,包括語法錯誤、鍵盤中斷等。
因此,去掉上述代碼中的 。
使用自定義 Exception 替代 ValueError
是 Python 內(nèi)置的在內(nèi)部出現(xiàn)值錯誤時拋出的異常,并不適合用在自定義的場景中。最好在代碼中定義自己的異常類型。
最終版本
參考資料
7 Python Code Smells: Olfactory Offenses To Avoid At All Costs
來源:
https://www.starky.ltd/2021/12/06/7-python-code-smells-by-practical-example/