程式設計原則:提升可讀性、可維護性與可擴展性
可讀性高、可維護、可擴展的程式碼是我們追求的目標,但是網路上沒有統整的資源都分成很多篇文章,學的時候總是在想我學完了嗎?這個絕對的嗎?於是這裡整理常見原則讓你能一次學完。和前言說的一樣,這裡不會深入細節,因為這些東西網路上有超級多資源我沒必要重寫一次,只會統整讓你知道有哪些東西。
要強調的是很多文章都把這些原則介紹的像是至高無上的真理一樣,這絕對是錯誤的,正確的是透過這些原則幫助我們寫出「可讀性高、可維護、可擴展」的程式碼而不是追求原則,更不是盲目追求 clean code。
再次強調這些原則不是絕對,真正的目標是撰寫可讀性高、可維護、可擴展的程式碼。
這些原則沒有哪個原則一定負責什麼什麼性,只是排版覺得這樣比較好讀,很多原則可以同時服務可讀、可維護、好擴展,例如 SRP 就是一個例子。
可讀性
想像一下接手一個亂成一團的老專案,亂就算了還沒人問,要獨自理解程式邏輯會有多痛苦。可讀性的本質是降低理解和維護程式碼的認知負擔,優秀的程式碼理解起來應該要很輕鬆,邏輯流暢、意圖明確、結構簡潔。好的可讀性意味著:
- 新人可以更快加入專案
- bug 修復變得更加直觀
- 團隊協作效率提升
- 程式碼重構和擴展變得更加容易
撰寫易於除錯的程式碼,從意識到未來會忘記這些程式碼開始。
From Write code that’s easy to delete, and easy to debug too.
單一職責原則 SRP
單一職責原則 (Single Responsibility Principle, SRP) 是 SOLID 設計原則之一,但是就算不看 SOLID 在大部分的程式碼都應該遵守他,核心理念是一個類別或模組應該僅負責一個單一的職責。什麼是職責?職責指的是某個類或模組所負責的特定功能或行為,也可以理解成該模組「改變的理由」,可以幫助程式碼易於理解、測試與替換。
講白了就是各管各的降低耦合,你不會想要一個既管理資料庫操作又處理使用者介面邏輯的物件,因為任何一個環節出問題都可能連累整個系統。
KISS
KISS (Keep It Simple, Stupid) 建議保持程式簡單明瞭,避免不必要的複雜性,簡單的程式碼易於理解、維護與擴展,降低錯誤機率,提升效率。KISS 原則不是反對複雜性,而是反對不必要的複雜性。
- 使用清晰的命名和簡潔的邏輯
- 避免多餘功能或過度抽象
- 避免炫技,不寫複雜難懂的程式碼
- 避免為了效能寫複雜難懂的程式碼,除非能有一個 order 的效能提升才改,但這種效能提升通常是架構設計層面的問題,尤其是在沒有 profiling 的情況下很有可能 80% 的努力帶來 5% 的效能提升
顯式優於隱式
來自 Zen of Python,強調程式碼應該清晰、直觀而不是用隱式表達讓人還要推敲才能理解。
- 程式碼的意圖應該明確,避免隱式表達
- 隱示代表程式語言本身任何的隱示表達方式,例如
from package import *
語法約束好於邏輯約束
字典取鍵值
顯式的寫出 if-else
就算只是補上 else 都比懶惰不寫好
撰寫有意義的註釋
核心在於解釋「為什麼」(意圖)而非「是什麼」(表面行為),因為後者通常已由程式碼本身表達。
- 適當註解可以幫助解釋程式目的和邏輯,但避免過度註釋
- 甚至於程式改了註釋記得刪掉也是,別讓註解變成騙人的東西
- 好的程式碼光是用變數和常見邏輯就可讀懂意義,過多的註釋就是廢話或程式碼太複雜!
程式碼風格一致性
- 遵循一致的程式碼風格指南 (PEP 8)
- 使用程式碼格式化工具確保一致性 (Ruff formatter)
- 避免魔術數字與硬編碼
- 明確的條件判斷
- 適當的空白與縮排
- 清晰的錯誤處理
可維護性
隨著時間推移,專案成長,我們勢必要花越來越多的時間維護程式。可維護性建構在好的可讀性之上,但是比可讀性更複雜,是一種系統性的思考方式:
- 能被輕鬆地理解和修改
- 即使專案規模擴大仍然保持程式邏輯清晰
- 降低修改時影響其他模組的風險
開放封閉原則
開放封閉原則 (Open–Closed Principle, OCP) 是 SOLID 原則的其中一項,你一定看過這句話:對擴展開放,對修改封閉:
- 開放:能夠新增功能或改變系統行為,而不必直接更動已經穩定的程式碼。
- 封閉:對一個已經開發完成的模組應該避免直接修改其原始碼,而應透過擴展來新增功能,以減少對既有程式碼的影響,保持穩定性。
兩者都是避免修改原有程式碼,不過前者強調輕鬆增加功能,後者強調保護既有功能不被破壞。這是一個比較模糊的概念,簡單的範例就是插件系統,我們在 Chrome 或者 Vim 的插件可以隨便搞輕鬆的新增功能,但是原有的瀏覽器和文字編輯功能永遠不會被更動,就是 OCP 的體現。
介面隔離原則
介面隔離原則 (Interface-Segregation Principles, ISP) 只透出最小介面, 不要給一個大父類強迫所有人實作介面,第一用不到功能,第二增加耦合性造成修改困難。
高內聚低耦合
一句話解釋,模組內部專注於單一的職責,模組間的依賴關係最小化,減少模組間的互相影響。
我不知道為什麼一堆介紹內聚和耦合的文章第一句話甚至是標題就要說物件導向,就算不是物件導向的程式語言高內聚低耦合也存在且必要。一個簡單的概念是,修改功能 A 模組就不要改到 A 以外模組,錯誤來自於修改,改的範圍越大代表耦合性越高,越容易造成動一髮牽全身,程式越容易出錯,這個概念明顯不限於物件導向程式設計。
YAGNI
YAGNI (You Aren't Gonna Need It) 說明避免過度設計和不必要的複雜性,專注於解決當前的具體問題
- 不要過度預測需求,只實現當前需要的功能
- 保持程式碼靈活性,降低維護成本
DRY
DRY (Don't Repeat Yourself) 很多人會這樣解釋:同樣的程式碼重複超過三次就抽象成函數,雖然這個說法不算錯一開始學也可以這樣用,但是詳情請見別寫乾淨的程式。
- 避免重複程式碼,提高可重用性和可維護性
- 將重複邏輯提取到函式或類別中