
隨著專案越來越大,如果缺乏良好的架構設計,程式碼會變得像義大利麵一樣糾纏不清(Spaghetti Code),這時「牽一髮而動全身」,改一個 bug 卻產生三個新 bug。
SOLID 原則是五個設計原則的縮寫,由 Robert C. Martin (Uncle Bob) 整理提出,旨在讓軟體更易維護、更易擴充。
1. S - 單一職責原則 (SRP)
Single Responsibility Principle
一個類別應該只有一個改變的理由。(做好一件事)
❌ 錯誤範例:UserManager 既管登入,又管寄 email,還管寫 log。
class UserManager:
def login(self, username, password):
# 驗證邏輯...
pass
def send_email(self, message):
# SMTP 邏輯...
pass
✅ 正確範例:拆分職責。
class EmailService:
def send(self, message): ...
class Logger:
def log(self, info): ...
class UserManager:
def __init__(self, email_service: EmailService, logger: Logger):
self.email = email_service
self.log = logger
def login(self, username, password):
# 只專注於登入邏輯
self.log.log(f"{username} logged in")
self.email.send("Welcome!")
2. O - 開放封閉原則 (OCP)
Open-Closed Principle
軟體實體應該對「擴充」開放,對「修改」封閉。
要新增功能時,應該是加新代碼,而不是改舊代碼。
❌ 錯誤範例:每次新增付款方式都要改 PaymentProcessor 的 if-else。
class PaymentProcessor:
def pay(self, type, amount):
if type == "credit":
# 信用卡邏輯
elif type == "paypal":
# PayPal 邏輯
✅ 正確範例:使用多型 (Polymorphism)。
from abc import ABC, abstractmethod
class PaymentMethod(ABC):
@abstractmethod
def pay(self, amount): pass
class CreditCard(PaymentMethod):
def pay(self, amount): print(f"刷卡 {amount}")
class PayPal(PaymentMethod):
def pay(self, amount): print(f"PayPal {amount}")
# 新增 LinePay 只需要新增一個 class,完全不用動舊程式碼
class LinePay(PaymentMethod):
def pay(self, amount): print(f"LinePay {amount}")
3. L - 里氏替換原則 (LSP)
Liskov Substitution Principle
子類別必須能完全替換掉父類別,而不會讓程式出錯。
如果你繼承了一個類別,卻在方法裡丟出 NotImplementedError 或者改變了行為預期,那就違反了 LSP。
❌ 錯誤範例:鴕鳥是鳥,但如果 Bird 有 fly() 方法, Ostrich 繼承它卻不能飛,就會出事。
4. I - 介面隔離原則 (ISP)
Interface Segregation Principle
客戶端不應該強迫依賴它用不到的介面。
Python 用 ABC (Abstract Base Class) 來定義介面。不要定義一個「胖介面」塞滿所有功能,而是定義多個「小介面」。
❌ 錯誤範例:MultiFunctionDevice 介面強迫可以列印的機器也要實作傳真功能。
5. D - 依賴反轉原則 (DIP)
Dependency Inversion Principle
高層模組不應依賴低層模組,兩者都應依賴抽象。
這通常透過 依賴注入 (Dependency Injection) 來實現。在 SRP 的範例中,UserManager 依賴的是 EmailService 類別 (或介面),而不是在內部直接 import smtplib 寫死發信邏輯。
6. 總結
SOLID 是寫出高品質物件導向程式碼的基石:
- SRP: 各司其職。
- OCP: 擴充不修舊。
- LSP: 繼承不只為了重用,要是真正的
Is-A關係。 - ISP: 介面要小而美。
- DIP: 依賴抽象不依賴實作。
下一章,我們將基於這些原則,繼續探討更多實用的 Design Patterns (設計模式) PART II!
延伸閱讀: