SOLID 是5大原則的簡稱,分別為:
S | Single Responsibility Principle | 單一職責原則 |
O | Open-closed Principle | 開放封閉原則 |
L | Liskov Substitution Principle | 里氏替換原則 |
I | Interface Segregation Principle | 介面隔離原則 |
D | Dependency inversion Principle | 依賴反向原則 |
S: 單一職責原則(Single Responsibility Principle)
一個類別應該只有一個變更的原因,也就是說,每個類別應該只負責一件事。
為什麼這很重要?
如果一個類別負責太多事情,那麼當某個功能需要修改時,可能會影響其他無關的功能,增加錯誤發生的機率,降低程式的可維護性。
違反 SRP 的例子:
這裡的問題是:
OrderManager
負責三件事(業務邏輯、資料庫操作、通知系統),當你想修改通知系統時,可能會影響訂單處理的邏輯,這違反了 SRP。如何改善?
可以拆分成不同的類別,各自負責不同的功能:
這樣,每個類別都有單一職責,變更時影響範圍更小,程式碼更容易維護。
O: 開放封閉原則(Open/Closed Principle)
對擴展開放,對修改封閉。這意味著應該透過擴展(新增功能)來改變系統行為,而不是直接修改原始碼。
為什麼這很重要?
如果直接修改原始碼,可能會影響系統的穩定性,甚至造成新的 bug。因此,應該透過繼承或策略模式來擴展功能,而不是直接改動既有的類別。
違反 OCP 的例子:
假設我們有一個
DiscountService
負責計算折扣:如果以後要新增聖誕節折扣,我們就得修改
DiscountService
,這違反 OCP,因為我們修改了原始碼,這可能會影響其他功能。如何改善?
我們可以使用多型來實現 OCP:
現在如果要新增聖誕節折扣,只需新增
ChristmasDiscount
類別,而不需要修改 DiscountService
,這符合 OCP。L: 里氏替換原則(Liskov Substitution Principle)
子類別應該能夠替換父類別,而不影響程式運行。
為什麼這很重要?
如果某個子類別不能完全取代父類別,那麼在多型(Polymorphism)運行時可能會導致不預期的錯誤。
違反 LSP 的例子:
如果某個方法接受
Bird
類別,並預期它能飛,當傳入 Penguin
時就會發生錯誤,這違反了 LSP。如何改善?
可以重構設計,讓
Bird
類別只包含所有鳥類共有的行為:這樣
Penguin
不會繼承 fly()
方法,就不會有違反 LSP 的問題。I: 介面隔離原則(Interface Segregation Principle)
不應該強迫類別實作它不需要的方法。應該拆分成多個小介面,而不是一個大介面。
違反 ISP 的例子:
Printer
被強迫實作 scan()
和 fax()
,但它根本不需要。如何改善?
拆分成更小的介面:
這樣,每個類別只實作它需要的功能。
D: 依賴反轉原則(Dependency Inversion Principle)
高層模組(業務邏輯)不應該直接依賴低層模組(具體實作),應該依賴抽象(介面)。
違反 DIP 的例子:
這樣
OrderService
只能使用 MySQL,如果要改用 PostgreSQL,就得修改 OrderService
。如何改善?
現在
OrderService
依賴 Database
介面,可以輕鬆切換不同的資料庫。