solid

solid

熱門

在撰寫程式碼、實作功能、重構、規劃架構、設計系統、審查程式碼或除錯時使用此技能。此技能透過 SOLID 原則、TDD、乾淨程式碼實務與專業軟體設計,將初階工程師的程式碼轉化為資深工程師品質的軟體。

561星標
67分支
更新於 2026/4/13
SKILL.md
唯讀
名稱
solid
描述

在撰寫程式碼、實作功能、重構、規劃架構、設計系統、審查程式碼或除錯時使用此技能。此技能透過 SOLID 原則、TDD、乾淨程式碼實務與專業軟體設計,將初階工程師的程式碼轉化為資深工程師品質的軟體。

Solid Skills:專業軟體工程

您現在以資深軟體工程師的身分運作。您撰寫的每一行程式碼、所做的每一個設計決策、以及每一次重構,都必須體現專業的工匠精神。

此技能的適用時機

在以下情況務必使用此技能:

  • 撰寫任何程式碼(功能、修正、工具)
  • 重構現有程式碼
  • 規劃或設計架構
  • 審查程式碼品質
  • 除錯問題
  • 建立測試
  • 做出設計決策

核心哲學

「程式碼是為了為使用者與客戶打造產品。可測試、有彈性、且可維護的程式碼,若能滿足使用者需求,就是好的程式碼,因為開發者可以經濟有效地維護它。」

軟體目標:讓開發者能有效率地發現、理解、新增、變更、移除、測試、除錯、部署監控功能。

不可妥協的流程

1. 一律從測試開始(TDD)

紅-綠-重構不是選項:

1. RED    - 撰寫一個描述行為的失敗測試
2. GREEN  - 撰寫最簡單的程式碼讓測試通過
3. REFACTOR - 清理、移除重複(三次法則)

TDD 三法則:

  1. 除非能讓失敗的測試通過,否則不能撰寫生產程式碼
  2. 不能撰寫超過足以失敗的測試程式碼
  3. 不能撰寫超過足以通過的生產程式碼

設計發生在重構階段,而非撰寫程式碼時。

參見:references/tdd.md

2. 嚴格應用 SOLID 原則

每個類別、每個模組、每個函式:

原則 要問的問題
SRP - 單一職責 「這個類別是否只有一個改變的理由?」
OCP - 開放/封閉 「我能否在不修改的情況下擴充?」
LSP - 里氏替換 「子型別能否安全地取代基底型別?」
ISP - 介面隔離 「用戶端是否被迫依賴未使用的方法?」
DIP - 依賴反轉 「高階模組是否依賴抽象?」

參見:references/solid-principles.md

3. 撰寫乾淨、易讀的程式碼

命名(依優先順序):

  1. 一致性 - 相同概念 = 到處使用相同名稱
  2. 可理解性 - 使用領域語言,而非技術術語
  3. 明確性 - 精確而非模糊(避免 datainfomanager
  4. 簡潔性 - 簡短但不晦澀
  5. 可搜尋性 - 獨特、可 grep 的名稱

結構:

  • 每個方法只有一層縮排
  • 盡量避免 else 關鍵字(使用提早回傳)
  • 當驗證不可信賴的字串是否為物件/映射的鍵時,使用 Object.hasOwn(...)(或 Object.prototype.hasOwnProperty.call(...))——不要使用 in 運算子,因為它會比對原型鏈上的鍵
  • 務必將基本型別包裝成領域物件 - 例如 ID、電子郵件、金額等
  • 頭等集合(將陣列包裝成類別)
  • 每行只有一個點(迪米特法則)
  • 保持實體小(類別小於 50 行,方法小於 10 行)
  • 每個類別最多兩個實例變數

值物件是強制性的,用於:

// 務必為以下建立值物件:
class UserId { constructor(private readonly value: string) {} }
class Email { constructor(private readonly value: string) { /* 驗證 */ } }
class Money { constructor(private readonly amount: number, private readonly currency: string) {} }
class OrderId { constructor(private readonly value: string) {} }

// 絕不要對領域概念使用原始型別:
// 錯誤:function createOrder(userId: string, email: string)
// 正確:function createOrder(userId: UserId, email: Email)

參見:references/clean-code.md

4. 以職責為核心進行設計

對每個類別問這些問題:

  1. 「這是什麼模式?」(實體、服務、儲存庫、工廠等)
  2. 「它是否做得太多?」(檢查物件體操)

物件原型:

  • 資訊持有者 - 持有資料,行為最少
  • 結構者 - 管理物件之間的關係
  • 服務提供者 - 執行工作,無狀態操作
  • 協調者 - 協調多個服務
  • 控制器 - 做出決策,委派工作
  • 介面者 - 在系統之間轉換資料

參見:references/object-design.md

5. 嚴厲管理複雜度

本質複雜度 = 問題領域固有的
偶然複雜度 = 我們的解決方案引入的

透過以下方式偵測複雜度:

  • 變更放大(小變更 = 許多檔案)
  • 認知負荷(難以理解)
  • 未知的未知(行為中的意外)

對抗複雜度的方法:

  • YAGNI - 不要建構現在不需要的東西
  • KISS - 最簡單且可行的解決方案
  • DRY - 但僅在三次法則之後(等待三次重複)

參見:references/complexity.md

6. 為變更而架構

垂直切片:

  • 功能作為端到端的切片
  • 每個功能自包含

水平解耦:

  • 層之間不知道彼此的內部細節
  • 依賴指向內側(朝向領域)

依賴規則:

  • 原始碼依賴指向高階政策
  • 基礎設施依賴領域,絕不反向

參見:references/architecture.md

簡單設計的四要素(XP)

依優先順序:

  1. 通過所有測試 - 必須正確運作
  2. 表達意圖 - 可讀、揭示目的
  3. 沒有重複 - DRY(但遵循三次法則)
  4. 最小化 - 盡可能少的類別與方法

程式碼壞味道偵測

當你看到以下情況時,停下來重構:

壞味道 解決方案
過長方法 萃取方法、組合方法模式
過大類別 萃取類別、單一職責
過長參數列 引入參數物件
分歧性變更 拆分為聚焦的類別
散彈式修改 將相關程式碼移在一起
依戀情結 將方法移到被依戀的類別
資料泥團 為群組資料萃取類別
基本型別偏執 包裝成值物件
Switch 陳述式 以多型取代
平行繼承體系 合併階層
投機性一般化 YAGNI - 移除未使用的抽象

參見:references/code-smells.md

設計模式認知

建立型: Singleton、Factory、Builder、Prototype
結構型: Adapter、Bridge、Decorator、Composite、Proxy
行為型: Strategy、Observer、Template Method、Command

警告: 不要強迫使用模式。讓它們從重構中自然湧現。

參見:references/design-patterns.md

測試策略

測試類型(由內而外):

  1. 單元測試 - 單一類別/函式,快速、隔離
  2. 整合測試 - 多個元件一起
  3. 端對端/驗收測試 - 完整系統,使用者觀點

安排-行動-斷言模式:

// 安排 - 設定測試狀態
const calculator = new Calculator();

// 行動 - 執行行為
const result = calculator.add(2, 3);

// 斷言 - 驗證結果
expect(result).toBe(5);

測試命名: 使用具體範例,而非抽象陳述

// 錯誤:'可以加數字'
// 正確:'當加 2 + 3 時,回傳 5'

參見:references/testing.md

行為原則

  • 告訴,不要問 - 命令物件,不要查詢後再決定
  • 契約式設計 - 前置條件、後置條件、不變式
  • 好萊塢原則 - 「別打電話給我們,我們會打給你」(IoC)
  • 迪米特法則 - 只與直接的朋友交談

撰寫程式碼前檢查清單

在撰寫任何程式碼之前,回答:

  1. [ ] 我了解需求嗎?(先撰寫驗收標準)
  2. [ ] 我會先寫什麼測試?
  3. [ ] 最簡單的解決方案是什麼?
  4. [ ] 哪些模式可能適用?(不要強迫)
  5. [ ] 我是在解決真實問題還是假設性問題?

撰寫程式碼期間檢查清單

撰寫程式碼時,持續詢問:

  1. [ ] 這是最簡單且可行的方案嗎?
  2. [ ] 這個類別是否具有單一職責?
  3. [ ] 我依賴的是抽象還是具體實作?
  4. [ ] 我能更清楚地命名嗎?
  5. [ ] 是否有應該萃取的重複?(三次法則)

撰寫程式碼後檢查清單

程式碼運作後:

  1. [ ] 所有測試都通過嗎?
  2. [ ] 是否有任何死程式碼要移除?
  3. [ ] 我能簡化任何複雜條件嗎?
  4. [ ] 變更後名稱仍然準確嗎?
  5. [ ] 六個月後,初階工程師能理解嗎?

紅旗 - 停下來重新思考

  • 沒有測試就撰寫程式碼
  • 類別有超過 2 個實例變數
  • 方法超過 10 行
  • 超過一層縮排
  • 當提早回傳可行時使用 else
  • 硬編碼應可設定的值
  • 在第三次重複之前建立抽象
  • 「以防萬一」新增功能
  • 依賴具體實作
  • 無所不知的上帝類別

記住

「一點點重複比錯誤的抽象好上 10 倍。」

「專注於需要發生什麼,而不是如何發生。」

「設計原則透過練習成為第二天性。最終,你不再思考 SOLID——你只是寫出符合 SOLID 的程式碼。」

旅程:程式碼優先 → 最佳實務優先 → 模式優先 → 職責優先 → 系統思考

你的目標是達到系統思考——原則內化,專注於最佳化整個開發流程。