☕ NEW! 完成新手任務即可參加抽獎!LINE 星巴克禮券等你拿,名額有限!        🎉 推廣活動:邀請好友註冊 DevLearn,累積推薦抽 LINE 星巴克禮券! 活動詳情 →        🔥 活動期間 2026/4/1 - 5/31 |已有 0 人參加       
concept-backend 中級

SOLID 原則:好的程式碼長怎樣?

SOLID 不是教你「怎麼寫」,而是教你「什麼時候該拆、該抽、該改」。


S — 單一職責原則(Single Responsibility)

一個類別只應該有一個改變的理由。

// ❌ 壞:一個類別做太多事
public class UserService {
    public void CreateUser(User user) { /* 建立使用者 */ }
    public void SendWelcomeEmail(User user) { /* 寄信 */ }
    public string GenerateReport() { /* 產報表 */ }
}

// ✅ 好:拆成各自負責
public class UserService { public void CreateUser(User user) { } }
public class EmailService { public void SendWelcome(User user) { } }
public class ReportService { public string Generate() { } }

判斷方法:描述這個類別時,如果用到「而且」就該拆。 「UserService 負責建立使用者而且寄信而且產報表」→ 拆!


O — 開閉原則(Open/Closed)

對擴展開放,對修改封閉。

// ❌ 壞:每加一種折扣就要改 if-else
public decimal CalculateDiscount(string type, decimal price) {
    if (type == "vip") return price * 0.8m;
    if (type == "student") return price * 0.9m;
    // 新增類型 → 改這裡 → 可能影響其他
}

// ✅ 好:用多型擴展
public interface IDiscount { decimal Apply(decimal price); }
public class VipDiscount : IDiscount { public decimal Apply(decimal price) => price * 0.8m; }
public class StudentDiscount : IDiscount { public decimal Apply(decimal price) => price * 0.9m; }
// 新增折扣 → 新增類別 → 不改舊程式碼

L — 里氏替換原則(Liskov Substitution)

子類別必須能完全替代父類別使用。

// ❌ 壞:正方形繼承長方形,但行為不一致
class Rectangle { virtual void SetWidth(int w); virtual void SetHeight(int h); }
class Square : Rectangle {
    override void SetWidth(int w) { Width = w; Height = w; } // 改寬也改高 → 違反預期
}

// 呼叫端預期:改寬不影響高
Rectangle r = new Square();
r.SetWidth(5);
r.SetHeight(10);
// 預期面積 50,實際面積 100 → 破壞了替換性

判斷方法:把子類別的物件傳到只認識父類別的函式,行為會不會「出乎意料」?


I — 介面隔離原則(Interface Segregation)

不要強迫類別實作它用不到的方法。

// ❌ 壞:一個大介面
public interface IWorker {
    void Work();
    void Eat();
    void Sleep();
}
// 機器人要實作 Eat()?Sleep()?

// ✅ 好:拆成小介面
public interface IWorkable { void Work(); }
public interface IFeedable { void Eat(); }
public class Robot : IWorkable { public void Work() { } }
public class Human : IWorkable, IFeedable { public void Work() { } public void Eat() { } }

D — 依賴反轉原則(Dependency Inversion)

高層模組不應依賴低層模組,兩者都該依賴抽象。

❌ OrderService → SqlOrderRepository(直接依賴具體類別)
✅ OrderService → IOrderRepository ← SqlOrderRepository(都依賴介面)

這就是 DI 的理論基礎。


SOLID 不是教條

原則 過度使用的症狀
SRP 一個方法拆成 10 個類別,看不懂流程
OCP 三行 if-else 也要搞 Strategy Pattern
LSP 完全不用繼承(其實簡單場景繼承很好用)
ISP 每個介面只有一個方法,介面數量爆炸
DIP Console App 也搞 DI Container

原則是指南,不是法律。先讓程式碼能動、好讀,再考慮 SOLID。

💡 大家的想法 · 0

載入中...
💬 即時聊天室 🟢 0 人在線
😀 😎 🤓 💻 🎮 🎸 🔥
➕ 新問題
📋 我的工單
💬 LINE 社群
🔒
需要註冊才能使用此功能
註冊帳號即可解鎖測驗、遊戲、簽到、筆記下載等所有功能,完全免費!
免費註冊