SOLID原則
讓軟體達到高內聚,低耦合的心法
提高面對需求變更的可維護性和應變能力
- Single Responsibility Principle
- Open-Close Principle
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle
Single Responsibility Principle
一言以敝之
單一物件單一職責
一個物件專心負責一件事,藉此提高物件的內聚力
舉例:
一個Runner物件的run()方法可讓跑者跑起來,其他所有Runner的方法都只是為了讓跑者跑得更快,而不會影響到TennisPlayer物件
結果:
- Runner內的所有方法皆是特定服務的相關功能,擁有高內聚力
- 不容易發生Divergent Change
Open-Close Principle
一言以敝之
類別應該對擴充開發,對修改關閉
當新增需求的時候(功能變化),在不改變現有程式碼的前提之下,藉由增加新的程式碼來實作新的功能
但不可能整個系統每一個類別都遵守此原則,因為通常會引入新的抽象層次,所以要找出最有可能改變的地方,才遵守此原則
舉例:
- Decorator Pattern
- Factory Pattern
- Java EE AOP
結果:
- 類別間降低耦合性
- 面對部分新增需求或既有需求變更類別不需要更改既有程式碼
Liskov Substitution Principle
講的是繼承,一言以敝之
Design by Contract,程序中的物件應該是可以在不改變程序正確性的前提下被它的子類所替換的
子類別要覆寫或實現父類別的功能時,需要與父類別做一樣的事情
舉例:
火箭基底,定義了發射火箭的功能
馬克一號實現了該功能並正常運作(遵照Design by Contract)
客戶提出破壞者號需求,工程師發現不需要發射功能,但需要自爆功能,所以工程師偷懶直接把它放到發射火箭功能上
老闆問工程師,說客戶想知道破壞者號有沒有發射功能
工程師看到繼承火箭基底,所以回答有吧
開始試射破壞者號 【5…4…3…2…1】
結果:
- 與父類別做一樣的事情,依賴於父類別的API使用者,不會發生無法預料的事情
- 減少執行期才會發生的問題
Interface Segregation Principle
一言以敝之
多個特定客戶端接口要好於一個寬泛用途的接口
你有一個擁有100個函數的介面,現在有三個不同的客戶都需要使用這個介面其中的30、50、20個函數。如果直接把這個介面傳給三個不同的客戶端,則這些客戶端很可能會被迫要實作他不需要的介面
舉例:
-
工人介面有
工作
和吃飯
兩個方法:工廠工人
實作工人介面的工作和吃飯方法。
超級賽亞人
實作工人介面的工作和吃飯方法(做得多,吃得多)。
機器人
實作工人介面的工作方法,但機器人不用吃飯
此時卻被迫要實作
吃飯方法。
解決方案是將工作和吃飯方法切割出各自的行為介面,取代原本的較大的工人介面
工廠工人和超級賽亞人實作兩個行為介面
機器人只要實作工作行為介面,不用實作吃飯行為介面。 -
Adapter Pattern
結果:
1. 客戶端物件不需要依賴於自己用不到的介面方法,降低耦合性
2. 介面本身職責更為清楚,提高內聚力
Dependency Inversion Principle
講的是caller和callee之間的關係,一言以敝之
依賴於抽象而不是一個實例,Program to an Interface, not an Implementation.
上層模組直接使用下層模組,對於低層次元件的依賴限制了高層次元件被重用的可行性。
需要更改成底層與上層都相依於抽象介面
避免caller(上層程式)因為callee(底層程式)改變而被迫改變
舉例:
- Adapter Pattern
- Service Locator Pattern
- Facade Pattern
結果:
- 上層程式不會因為下層程式改變而被迫要修改程式碼
- 降低系統耦合性
總結
SOLID原則是讓軟體提高內聚力,降低耦合性的心法,而其精神的具體實現常在Design Pattern中看到。
可以觀察到不管是哪一種原則都常常透過增加介面或中間層的方式來達到其目的,與一句電腦科學界的名言不謀而合。
Any problem in computer science can be solved by anther layer of indirection.
計算機科學領域的任何問題都可以通過增加一個間接的中間層來解決