重構不改變外部行為,透過微小步驟持續改善程式碼結構,提升可讀性、擴充性與維護性。Martin Fowler《重構》雖以 Java 為例,但其核心模式 (提煉函式、封裝變數、移動特性等) 完全語言無關。本文結合嵌入式/底層 C 語言開發實戰,分享關鍵重構手法。
C 語言傳統偏好縮寫 (`rd_data`, `proc_tx`),但簡潔 ≠ 含糊。建立團隊命名公約:
好名稱讓呼叫處如閱讀註解,大幅降低認知負荷。
超過 50 行、需上下翻頁的函式必須拆分。提煉準則:
內聯函式 (`static inline`) 消除呼叫開銷,嵌入式環境無效能顧慮。
複雜條件 `if ((a > 0 && b < 10) || (c != NULL && c->flag == 0xFF))` 應提煉為:
static inline bool is_valid_threshold(int a, int b) { return a > 0 && b < 10; }
static inline bool is_flag_cleared(const struct_t* c) { return c && c->flag == 0xFF; }
if (is_valid_threshold(a, b) || is_flag_cleared(c)) { ... }
條件語意瞬間清晰,除錯時可直接對提煉函式下斷點。
參數超過 4 個即屬「壞味道」。解法:
// 重構前
void uart_send(uint8_t port, uint32_t baud, uint8_t parity, uint8_t stop_bits, const uint8_t* data, size_t len);
// 重構後
typedef struct { uint8_t port; uint32_t baud; uint8_t parity; uint8_t stop_bits; } uart_cfg_t;
void uart_send(const uart_cfg_t* cfg, const uint8_t* data, size_t len);
// 配合建構輔助函式
static inline uart_cfg_t uart_cfg_default(void) { return (uart_cfg_t){.port=1, .baud=115200, ...}; }
呼叫端僅需關注變動參數,預設值由輔助函式提供。
全域變數被任意修改是除錯地獄。三步走封裝:
多執行緒/行程環境下,用巨集包裝存取點並印出檔案/函式/行號,同樣有效。
C 語言允許隱式函式宣告 (預設回傳 `int`),導致指標/結構體參數截斷卻能編譯通過、執行時崩潰。鐵律:
臨時新增的函式隨意放置會破壞模組邊界。重構節點:
許多重構手法 (好名稱、小函式、封裝全域) 在初次編寫時即可落地。養成習慣:
C 語言雖無類別、繼承、多態,但模組化、封裝、單一職責、依賴倒置等設計原則同樣適用。重構不改變語言,改變的是我們駕馭複雜度的能力。