Java的SOLID程式設計原則 - Filippo Buletto
SOLID闡述了五種設計原則,可幫助開發人員輕鬆擴充套件和維護軟體:
S - 單一責任原則
O - 開放原則
L - Liskov替代原理
I - 介面隔離原理
D - 依賴倒置原則
單一責任原則
一個類應該有一個,而且只有一個改變的理由。
一個類應該只有一個責任,這意味著類應該高度凝聚並實現強相關的邏輯。實現功能1和功能2和功能3(依此類推)的類違反了SRP。
SRP示例:
// BAD public class UserSettingService { public void changeEmail(User user) { if (checkAccess(user)) { //Grant option to change } } public boolean checkAccess(User user) { //Verify if the user is valid. } } // GOOD public class UserSettingService { public void changeEmail(User user) { if (securityService.checkAccess(user)) { //Grant option to change } } } public class SecurityService { public static boolean checkAccess(User user) { //check the access. } } |
SRP味道
- 單個類中不止一個上下文分隔的程式碼
- 測試中的大型setup初始化設定(TDD在檢測SRP違規時非常有用)
SRP好處
- 負責給定用例的分隔類現在可以在應用程式的其他部分中重用
- 負責給定用例的分隔類現在可以單獨測試
開/閉原則
您應該能夠擴充套件類行為,而無需對其進行修改。
類應該開啟以進行擴充套件並關閉以進行修改。您應該能夠擴充套件類行為而無需修改其實現:
// BAD public class Logger { String logging; public Logger(String logging) { this.logging = logging; } public void log() { if ("console".equals(logging)) { // Log to console } else if ("file".equals(logging)) { // Log to file } } } // GOOD public interface Log { void log(); } public class ConsoleLog implements Log { void log() { // Log to console } } public class FileLog implements Log { void log() { // Log to file } } public class Logger { Log log; public Logger(Log log) { this.log = log; } public void log() { this.log.log(); } } |
OCP程式碼味道:
- 如果你注意到類X直接引用其程式碼庫中的其他類Y,則表明類Y應該傳遞給類X(透過建構函式/單個方法),例如透過依賴注入
- 複雜的if-else或switch語句
OCP好處:
- 使用封裝在單獨類中的新功能可以輕鬆擴充套件X類功能,而無需更改類X實現(它不知道引入的更改)
- 程式碼鬆散耦合
- 注入的Y類可以在測試中輕易模擬
利斯科夫替代原則
派生類必須可替代其基類。
這是開/閉原則的延伸。派生類不應更改基類的行為(繼承方法的行為)。如果類Y是類X的子類,則任何引用類X的例項也應該能夠引用類Y(派生型別必須完全替代它們的基型別)。
// BAD public class DataHashSet extends HashSet { int addCount = 0; public boolean function add(Object object) { addCount++; return super.add(object); } // the size of count will be added twice! public boolean function addAll(Collection collection) { addCount += collection.size(); return super.addAll(collection); } } // GOOD: Delegation Over Subtyping public class DataHashSet implements Set { int addCount = 0; Set set; public DataHashSet(Set set) { this.set = set; } public boolean add(Object object) { addCount++; return this.set.add(object); } public boolean addAll(Collection collection) { addCount += collection.size(); return this.set.addAll(collection); } } |
LSP程式碼味道:
- 如果它看起來像一隻鴨子,嘎嘎叫像鴨子但需要電池才能達到這個目的 - 這可能違反了LSP
- 修改子類中的繼承行為
- 在重寫的繼承方法中引發的異常
LSP好處:
- 避免意外和不正確的結果
- 明確區分共享繼承介面和擴充套件功能
介面隔離原理
製作客戶端特定的細粒度介面。
一旦介面變得太大/太胖,我們絕對需要將其拆分為更具體的小介面。介面將由將使用它的客戶端定義,這意味著介面的客戶端將只知道與它們相關的方法。
// BAD public interface Car { Status open(); Speed drive(Gas gas); Engine changeEngine(Engine newEngine); } public class Driver { public Driver(Car car) {} public Speed ride() { this.car.open(); return this.car.drive(new Gas(10)); } } public class Mechanic { public Mechanic(Car car) {} public Engine fixEngine(Engine newEngine) { return this.car.changeEngine(newEngine); } } // GOOD public interface RidableCar { Status open(); Speed drive(Gas gas); } public interface FixableCar { Engine changeEngine(Engine newEngine); } public class Driver { // Same with RidableCar } public class Mechanic { // Same with FixableCar } |
ISP程式碼味道
- 由許多類實現的一個胖介面,其中沒有一個類實現100%的介面方法。這種胖介面應該分成適合客戶需求的較小介面
ISP好處
- 高度凝聚力的程式碼
- 避免使用單個胖介面在所有類之間進行耦合(一旦單個胖介面中的方法得到更新,所有類 - 無論是否使用此方法 - 都被迫相應地更新)
- 透過將職責分組到單獨的介面中,明確分離業務邏輯
依賴倒置原則
依賴於抽象,而不是實現
如果您的實現細節將取決於更高階別的抽象,它將幫助您獲得正確耦合的系統。而且,它將影響該系統的封裝和內聚。
// BAD public class SQLDatabase { public void connect() { String connectionstring = System.getProperty("MSSQLConnection"); // Make DB Connection } public Object search(String key) { // Do SQL Statement return query.find(); } } public class DocumentDatabase { // Same logic but with document details } // GOOD public interface Connector { Connection open(); } public interface Finder { Object find(String key); } public class MySqlConnector implements Connector {} public class DocumentConnector implements Connector {} public class MySqlFinder implements Finder {} public class DocumentFinder implements Finder {} public class Database { public Database(Connector connector, Finder finder) { this.connector = connector; this.finder = finder; } public Connection connect() { return connector.open(); } public Object search(String key) { return finder.find(key); } } |
DIP味道:
- 在高階模組中例項化低階模組
- 呼叫低階模組/類的類方法
DIP好處:
- 透過使更高階別的模組獨立於較低階別的模組來提高其可重用性
- 可以採用依賴性注入1來促進所選擇的低階元件實現的執行時供應到高階元件
- 注入類可以在測試中輕易模擬
相關文章
- SOLID 設計原則Solid
- 面向JavaScript的SOLID設計原則JavaScriptSolid
- SOLID架構設計原則Solid架構
- SOLID 原則:軟體設計的基本原則Solid
- SOLID 五大設計原則Solid
- 六大設計原則(SOLID)Solid
- 鮑勃大叔:SOLID原則適合函式程式設計嗎?Solid函式程式設計
- SOLID原則Solid
- SOLID 原則Solid
- 物件導向設計的六大原則(SOLID原則)-——里氏替換原則物件Solid
- C#實踐設計模式原則SOLIDC#設計模式Solid
- SOLID:物件導向設計的前五項原則Solid物件
- 架構設計的五大原則-SOLID架構Solid
- 實踐GoF的23種設計模式:SOLID原則(上)Go設計模式Solid
- 【架構設計】你真的理解軟體設計中的SOLID原則嗎?架構Solid
- 講講solid原則Solid
- SOLID原則筆記Solid筆記
- 程式設計原則程式設計
- SOLID原則的堅實指南| BaeldungSolid
- 程式設計原則(整理)程式設計
- 物件導向設計原則和建立SOLID應用的5個方法物件Solid
- 好程式設計師Java教程分享Java設計模式的6大原則程式設計師Java設計模式
- 【設計原則】物件導向程式設計的六大原則物件程式設計
- 比SOLID更重要的與DDD設計相關的GRASP原則 - Kamil GrzybekSolid
- Java中的設計模式和原則Java設計模式
- 程式碼審查“查”什麼?(5):SOLID原則Solid
- 程式設計師應該遵守的程式設計原則程式設計師
- 不止於物件導向的SOLID原則物件Solid
- 開閉原則——物件導向程式設計原則物件程式設計
- 好程式設計師Java培訓分享Java設計模式的六大原則程式設計師Java設計模式
- Java中物件導向的設計原則Java物件
- JAVA程式設計規則:Java程式設計
- Java程式設計規則Java程式設計
- Java程式設計師應當知道的10個物件導向設計原則Java程式設計師物件
- Java程式設計師應瞭解的10個物件導向設計原則Java程式設計師物件
- 物件導向設計原則,以及包的設計原則物件
- 設計模式的設計原則設計模式
- SOLID:物件導向設計的五個基本原則Solid物件