堅實的原則
SOLID 是一組基本原則,旨在增強物件導向程式設計 (OOP) 中程式碼的可管理性和可擴展性。它由五個關鍵原則組成:
- S單一責任原則 — SRP
- O筆閉原則 — OCP
- L伊斯科夫替代原理 — LSP
- I介面隔離原則 — ISP
- D 依存倒置原理 — DIP
這些原則是由 Robert C. Martin(也稱為 Uncle Bob)在 2000 年代初期提出的,此後已在軟體開發社群中廣泛採用。透過遵循 SOLID 原則,開發人員可以創建更易於理解、修改和擴展的程式碼,從而形成更強壯和可維護的軟體系統。
單一職責原則(SRP)
單一職責原則是 OOP 和 SOLID 中第一個也是最基本的原則。顧名思義,這原則的意思是「一個類別應該只負責一個特定的責任」。
假設我們有一個名為 Invoice 的類,其中包含 2 個方法generateInvoice() 和 saveToFiles() 。
public class Invoice { private Long InvoiceNo; public void generateInvoice() { // code to generate Invoice. } public void saveToFiles() { // code to save invoice as a file. } }
這不是一個好的做法,因為 Invoice 類別有兩個職責。更好的方法是將這些功能分成專用的類別。
public class Invoice { private Long InvoiceNo; public void generateInvoice() { // code to generate Invoice. } } public class FileManager { public void saveToFiles(Invoice invoice) { // code to save invoice as a file. } }
在這裡,我們可以看到我們有 2 個用於用例的類別:
- 產生發票
- 儲存到檔案
遵循 SRP 的好處
- 改進的程式碼組織:透過將關注點分成不同的類,程式碼庫變得更有組織性並且更容易導航。
- 更好的可維護性:當一個類別具有單一職責時,更容易理解其目的並進行更改,而不會產生意想不到的副作用。
- 提高可重用性:具有單一職責的類別更有可能在應用程式的不同部分甚至其他項目中重複使用。
- 更容易測試:具有單一職責的類別通常更小、更集中,使它們更容易單獨測試。
開閉原則(OCP)
開閉原則是SOLID的另一個核心原則。此原則由 Bertrand Meyer 於 1997 年提出。這個原則背後的想法是「軟體工件(類別、模組和函數)應該對擴展開放,但對修改關閉。」
例如;
比方說,我們有一個名為 Shape 的類,我們可以用這個類來計算形狀的面積。
public class Invoice { private Long InvoiceNo; public void generateInvoice() { // code to generate Invoice. } public void saveToFiles() { // code to save invoice as a file. } }
在上面的程式碼中,新增形狀需要修改現有的 Shape 類,這不是一個好的做法。
以下是一個程式碼範例,示範如何將開閉原則應用於此場景。
public class Invoice { private Long InvoiceNo; public void generateInvoice() { // code to generate Invoice. } } public class FileManager { public void saveToFiles(Invoice invoice) { // code to save invoice as a file. } }
透過OCP的應用,我們可以在不修改目前實現的情況下添加許多我們想要的形狀。
注意:使用介面並不是實現 OCP 的唯一方法。
遵循 OCP 的好處
- 降低錯誤風險:透過不修改現有程式碼,可以最大限度地降低引入新錯誤或破壞現有功能的風險。
- 提高可維護性:遵循 OCP 的程式碼更容易維護和擴展,因為可以在不更改現有程式碼庫的情況下添加新功能。
- 增強靈活性:抽象和多態性的使用允許更靈活和適應性更強的設計,從而更容易適應不斷變化的需求。
里氏替換原理 (LSP)
里氏替換原則是 OOP 中的另一個重要原則。它是由 Barbara Liskov 於 1987 年在一次關於資料抽象的會議演講中引入的。
該原則指出,「超類別的物件應該可以用其子類別的物件替換,而不改變程式的正確性」。
例如,如果 Circle 和 Rectangle 是 Shape 的子類型,那麼我們應該能夠用 Circle 或 Rectangle 物件取代 Shape 對象,不會有任何問題。
public class Shape { private String shapeType; private double radius; private double length; private double width; public Shape(String shapeType, double radius, double length, double width) { this.shapeType = shapeType; this.radius = radius; this.length = length; this.width = width; } public double area() { if (shapeType.equals("circle")) { return Math.PI * (radius * radius); } else if (shapeType.equals("rectangle")) { return length * width; } else { throw new IllegalArgumentException("Unknown shape type"); } } } // Usage public class Main { public static void main(String[] args) { Shape circle = new Shape("circle", 5, 0, 0); Shape rectangle = new Shape("rectangle", 0, 4, 6); System.out.println(circle.area()); System.out.println(rectangle.area()); } }
如本例所示,遵循里氏替換原則意味著我們應該能夠無縫地用子類別實例取代超類別實例。
遵循 LSP 的好處
- 改進的程式碼可重複使用性:透過確保子類型可以取代其基本類型,使用基本類型的程式碼也可以與其任何子類型一起使用,從而促進程式碼重用。
- 增強的可維護性:遵循LSP的程式碼更容易維護,因為它降低了修改或擴展程式碼庫時引入錯誤的風險。
- 更好的可測試性:LSP 使為類別及其子類型編寫單元測試變得更加容易,因為測試可以針對基本類型編寫,並且應該適用於所有子類型。
介面隔離原則(ISP)
介面隔離原則是 Robert C. Martin 提出的五個 SOLID 原則之一。它指出:「不應強迫客戶端依賴他們不使用的介面」。
換句話說,使用多個特定於任務的介面比使用一個通用介面更好。
下面的範例展示了通用介面的用法。
public class Invoice { private Long InvoiceNo; public void generateInvoice() { // code to generate Invoice. } public void saveToFiles() { // code to save invoice as a file. } }
使用像 MultifunctionPrinter 這樣的通用介面迫使我們實作不必要的方法,這被認為是不好的做法。讓我們探討一下如何將介面隔離原則套用到此場景。
介面
public class Invoice { private Long InvoiceNo; public void generateInvoice() { // code to generate Invoice. } } public class FileManager { public void saveToFiles(Invoice invoice) { // code to save invoice as a file. } }
實施
public class Shape { private String shapeType; private double radius; private double length; private double width; public Shape(String shapeType, double radius, double length, double width) { this.shapeType = shapeType; this.radius = radius; this.length = length; this.width = width; } public double area() { if (shapeType.equals("circle")) { return Math.PI * (radius * radius); } else if (shapeType.equals("rectangle")) { return length * width; } else { throw new IllegalArgumentException("Unknown shape type"); } } } // Usage public class Main { public static void main(String[] args) { Shape circle = new Shape("circle", 5, 0, 0); Shape rectangle = new Shape("rectangle", 0, 4, 6); System.out.println(circle.area()); System.out.println(rectangle.area()); } }
透過應用ISP,我們將其分成更小的、特定於角色的介面 ——例如印表機、掃描器和傳真。這允許每個類別(例如 BasicPrinter、AdvancedPrinter 或 FaxMachine)僅實現相關功能,從而促進模組化並減少不必要的依賴關係。
跟隨 ISP 的好處
- 模組化和可重複使用程式碼:透過將大型介面分解為更小、更具體的接口,程式碼變得更加模組化和可重用。類別或模組可以僅實現它們需要的接口,從而減少不必要的依賴關係,並使在系統的不同部分之間重複使用程式碼變得更容易。
- 降低程式碼複雜度:當類別或模組僅依賴它們所需的介面時,程式碼變得不那麼複雜且更容易理解。這是因為開發人員不必處理不必要的方法或依賴項。這些與其特定用例無關。
- 提高可維護性:透過更小、更集中的介面,可以更輕鬆地維護程式碼。對一個介面的變更不太可能影響系統的其他部分,從而降低了引入錯誤或破壞現有功能的風險。
- 更好的可測試性:更小、更集中的介面使得為各個組件編寫單元測試變得更容易。這是因為測試可以專注於特定的行為,而不會受到不相關的方法或依賴項的影響。
- 增加靈活性:透過遵守ISP,系統變得更加靈活並且更容易擴展或修改。可以透過建立新介面或修改現有介面來新增功能或要求,而不會影響整個系統。
依賴倒置原則(DIP)
依賴倒置原則是SOLID的最終原則。這也是由 Robert C. Martin 介紹的。這促進了鬆散耦合的程式碼。
DIP說明了幾點:
- 高層模組不應該依賴低層模組。
- 兩者都應該依賴抽象。
- 抽像不應該依賴細節。
- 細節應該取決於抽象。
簡單來說,一個類別不應該直接依賴其他特定類別(具體實作),而應該依賴介面或抽象類別。這使得程式碼更加靈活且更易於維護,因為您可以在不更改依賴類別的情況下更換實作。
緊耦合代碼(無 DIP)
public class Invoice { private Long InvoiceNo; public void generateInvoice() { // code to generate Invoice. } public void saveToFiles() { // code to save invoice as a file. } }
如上例所示,Computer 類別直接依賴 Keyboard 類別。
松耦合代碼(帶 DIP)
public class Invoice { private Long InvoiceNo; public void generateInvoice() { // code to generate Invoice. } } public class FileManager { public void saveToFiles(Invoice invoice) { // code to save invoice as a file. } }
現在,Computer 依賴 InputDevice 接口,而不是特定的 Keyboard。這樣可以輕鬆切換到另一個輸入設備,例如 WirelessKeyboard,而無需修改 Computer 類別。
遵循 DIP 的好處
- 鬆散耦合:透過依賴抽象而不是具體實現,程式碼的耦合變得不那麼緊密,使得更容易更改系統的一個部分而不影響其他部分。
- 提高可維護性:低層模組的變化不會影響高層模組,使系統更容易維護和擴展。
- 增強的可測試性:可以使用低階模組的模擬實作來測試高階模組,使測試更快、更可靠。
- 提高可重複使用性:高層模組可以在不同的上下文中重複使用,而不需要更改它們所依賴的低層模組。
結論
總而言之,SOLID 原則:單一職責、開閉、里氏替換、介面隔離和依賴倒置為在物件導向程式設計中編寫乾淨、可維護和可擴展的程式碼提供了基本準則。
透過遵守這些原則,開發人員可以創建更易於理解、修改和擴展的系統,最終帶來更高品質的軟體和更有效率的開發流程。
概括
感謝您閱讀這篇文章!我希望您現在對 SOLID 原則以及如何應用它們來增強您的專案有了深入的了解。
追蹤我:
- LinkedIn — @nsadisha
- GitHub — @nsadisha
- 中 — @nsadisha
- Dev.to — @nsadisha
—薩迪莎·尼薩拉
以上是堅實的原則的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undress AI Tool
免費脫衣圖片

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Callable和Runnable在Java中主要有三點區別。第一,Callable的call()方法可以返回結果,適合需要返回值的任務,如Callable;而Runnable的run()方法無返回值,適用於無需返回的任務,如日誌記錄。第二,Callable允許拋出checked異常,便於錯誤傳遞;而Runnable必須在內部處理異常。第三,Runnable可直接傳給Thread或ExecutorService,而Callable只能提交給ExecutorService,並返回Future對像以

Java支持異步編程的方式包括使用CompletableFuture、響應式流(如ProjectReactor)以及Java19 中的虛擬線程。 1.CompletableFuture通過鍊式調用提升代碼可讀性和維護性,支持任務編排和異常處理;2.ProjectReactor提供Mono和Flux類型實現響應式編程,具備背壓機制和豐富的操作符;3.虛擬線程減少並發成本,適用於I/O密集型任務,與傳統平台線程相比更輕量且易於擴展。每種方式均有適用場景,應根據需求選擇合適工具並避免混合模型以保持簡潔性

JavaNIO是Java1.4引入的新型IOAPI,1)面向緩衝區和通道,2)包含Buffer、Channel和Selector核心組件,3)支持非阻塞模式,4)相比傳統IO更高效處理並發連接。其優勢體現在:1)非阻塞IO減少線程開銷,2)Buffer提升數據傳輸效率,3)Selector實現多路復用,4)內存映射加快文件讀寫。使用時需注意:1)Buffer的flip/clear操作易混淆,2)非阻塞下需手動處理不完整數據,3)Selector註冊需及時取消,4)NIO並非適用於所有場景。

在Java中,枚舉(enum)適合表示固定常量集合,最佳實踐包括:1.用enum表示固定狀態或選項,提升類型安全和可讀性;2.為枚舉添加屬性和方法以增強靈活性,如定義字段、構造函數、輔助方法等;3.使用EnumMap和EnumSet提高性能和類型安全性,因其基於數組實現更高效;4.避免濫用enum,如動態值、頻繁變更或複雜邏輯場景應使用其他方式替代。正確使用enum能提升代碼質量並減少錯誤,但需注意其適用邊界。

Java的類加載機制通過ClassLoader實現,其核心工作流程分為加載、鏈接和初始化三個階段。加載階段由ClassLoader動態讀取類的字節碼並創建Class對象;鏈接包括驗證類的正確性、為靜態變量分配內存及解析符號引用;初始化則執行靜態代碼塊和靜態變量賦值。類加載採用雙親委派模型,優先委託父類加載器查找類,依次嘗試Bootstrap、Extension和ApplicationClassLoader,確保核心類庫安全且避免重複加載。開發者可自定義ClassLoader,如URLClassL

Javaprovidesmultiplesynchronizationtoolsforthreadsafety.1.synchronizedblocksensuremutualexclusionbylockingmethodsorspecificcodesections.2.ReentrantLockoffersadvancedcontrol,includingtryLockandfairnesspolicies.3.Conditionvariablesallowthreadstowaitfor

Java異常處理的關鍵在於區分checked和unchecked異常並合理使用try-catch、finally及日誌記錄。 1.checked異常如IOException需強制處理,適用於可預期的外部問題;2.unchecked異常如NullPointerException通常由程序邏輯錯誤引起,屬於運行時錯誤;3.捕獲異常時應具體明確,避免籠統捕獲Exception;4.推薦使用try-with-resources自動關閉資源,減少手動清理代碼;5.異常處理中應結合日誌框架記錄詳細信息,便於後

HashMap在Java中通過哈希表實現鍵值對存儲,其核心在於快速定位數據位置。 1.首先使用鍵的hashCode()方法生成哈希值,並通過位運算轉換為數組索引;2.不同對象可能產生相同哈希值,導致衝突,此時以鍊錶形式掛載節點,JDK8後鍊錶過長(默認長度8)則轉為紅黑樹提升效率;3.使用自定義類作鍵時必須重寫equals()和hashCode()方法;4.HashMap動態擴容,當元素數超過容量乘以負載因子(默認0.75)時,擴容並重新哈希;5.HashMap非線程安全,多線程下應使用Concu
