SOLID 原则的可靠指南
山姆·米靈頓
讀完需要
7
分鐘速讀僅需 3 分鐘
山姆是牛津大學的一名軟件開發人員,目前在生物信息學領域工作。山姆的專長領域是 Java,他每天都在為生物學研究編寫多線程分析工具。他是一位熱情的程序員、作家,并且喜歡學習新技術。當他不在辦公桌前時,您可能在攀巖墻上找到他,或者他在看 Netflix 或嘗試健身。
原文鏈接:https://www.baeldung.com/solid-principles
作者:山姆
編輯:大白?
翻譯:大白
1
? ?
概述
在本教程中,我們將討論面向對象設計的 SOLID 原則。
首先,我們將首先探討它們出現的原因以及為什么我們在設計軟件時應該考慮它們。然后,我們將概述每個原則以及一些示例代碼。
2
? ?
SOLID 原則的原因
SOLID 原則是由 Robert C. Martin 在他 2000 年的論文“設計原則和設計模式”中引入的。這些概念后來由 Michael Feathers 建立,他向我們介紹了 SOLID 首字母縮略詞。在過去的 20 年里,這五項原則徹底改變了面向對象編程的世界,改變了我們編寫軟件的方式。
那么,什么是 SOLID,它如何幫助我們編寫更好的代碼呢?簡而言之,Martin and Feathers 的設計原則鼓勵我們創建更易于維護、更易理解和更靈活的軟件。因此,隨著應用程序規模的增長,我們可以降低其復雜性,并在未來的道路上節省很多麻煩!
以下五個概念構成了我們的 SOLID 原則:
Single Responsibility 單一職責
Open/Closed 開放閉合
Liskov Substitution 里氏替代
Interface Segregation 接口分離
Dependency Inversion 依賴倒置
雖然這些概念可能看起來令人生畏,但可以通過一些簡單的代碼示例輕松理解它們。在以下各節中,我們將深入研究這些原則,并舉一個快速的 Java 示例來說明每個原則。
3
? ?
單一職責
讓我們從單一職責原則開始。正如我們所料,這個原則規定一個類應該只有一個職責。此外,它應該只有一個改變的理由。
這個原則如何幫助我們構建更好的軟件?讓我們看看它的一些好處:
測試 – 一個職責單一的類將有更少的測試用例。
低耦合 – 單個類中的功能越少,依賴就越少。
組織 – 較小的、組織良好的類比單一的類更容易搜索。
例如,讓我們看一個類來表示一本簡單的書:
public class Book {private String name;private String author;private String text;//constructor, getters and setters }在這段代碼中,我們存儲與 Book 實例關聯的名稱、作者和文本。
現在,讓我們添加幾個方法來查詢文本:
public class Book {private String name;private String author;private String text;//constructor, getters and setters// methods that directly relate to the book propertiespublic String replaceWordInText(String word){return text.replaceAll(word, text);}public boolean isWordInText(String word){return text.contains(word);} }現在,我們的 Book 類運行良好,我們可以在應用程序中存儲任意數量的書籍。
但是,如果我們不能將文本輸出到控制臺并閱讀它,那么存儲信息又有什么用呢?
讓我們謹慎行事,并添加一個打印方法:
public class Book {//...void printTextToConsole(){// our code for formatting and printing the text} }但是,此代碼違反了我們之前概述的單一職責原則。
為了解決我們的問題,我們應該實現一個單獨的類,只處理打印我們的文本:
public class BookPrinter {// methods for outputting textvoid printTextToConsole(String text){//our code for formatting and printing the text}void printTextToAnotherMedium(String text){// code for writing to any other location..} }棒極了!我們不僅開發了一個類來減輕 Book 的打印任務,而且我們還可以利用 BookPrinter 類將我們的文本發送到其他媒體。
無論是電子郵件、日志記錄還是其他任何內容,我們都有一個單獨的課程專門針對這一問題。
4
? ?
開放擴展,關閉修改
現在是 SOLID 中的 O 的時候了,也就是所謂的開閉原則。簡而言之,類應該對擴展開放,對修改關閉。通過這樣做,我們可以 阻止自己修改現有代碼并在原本令人滿意的應用程序中造成潛在的新錯誤。
當然,該規則的一個例外是在修復現有代碼中的錯誤時。
讓我們通過一個快速的代碼示例來探索這個概念。作為一個新項目的一部分,假設我們已經實現了一個 Guitar 類。
它完全成熟,甚至還有一個音量旋鈕:
public class Guitar {private String make;private String model;private int volume;//Constructors, getters & setters }我們啟動了應用程序,每個人都喜歡它。但幾個月后,我們認為吉他 有點乏味,可以使用酷炫的火焰圖案讓它看起來更搖滾。
此時,打開 Guitar 類并添加火焰模式可能很誘人——但誰知道我們的應用程序中可能會拋出什么錯誤。
相反,讓我們堅持開閉原則,簡單地擴展我們的 Guitar 類:
public class SuperCoolGuitarWithFlames extends Guitar {private String flameColor;//constructor, getters + setters }通過擴展 Guitar 類,我們可以確保我們現有的應用程序不會受到影響。
5
? ?
里氏替換
這個原則由 Barbara Liskov 定義。他說程序里的對象都應該可以被它的子類實例替換而不用更改系統的正常工作.
它可以說是五項原則中最復雜的。簡單地說,如果類 A 是類 B 的子類型,我們應該能夠用 A 替換 B 而不會破壞程序的行為。
讓我們直接跳到代碼來幫助我們理解這個概念:
public interface Car {void turnOnEngine();void accelerate(); }上面,我們定義了一個簡單的 Car 接口,其中包含所有汽車都應該能夠實現的幾個方法:打開引擎并加速前進。
讓我們實現我們的接口并為方法提供一些代碼:
public class MotorCar implements Car {private Engine engine;//Constructors, getters + setterspublic void turnOnEngine() {//turn on the engine!engine.on();}public void accelerate() {//move forward!engine.powerOn(1000);} }正如我們的代碼所描述的,我們有一個可以打開的引擎,我們可以增加功率。
但是等等——我們現在生活在電動汽車時代:
public class ElectricCar implements Car {public void turnOnEngine() {throw new AssertionError("I don't have an engine!");}public void accelerate() {//this acceleration is crazy!} }通過將沒有引擎的汽車加入其中,我們本質上改變了程序的行為。這是對 Liskov 替換的公然違反,并且比我們之前的兩個原則更難修復。
一種可能的解決方案是將我們的模型重新設計為考慮到 Car 的無引擎狀態的接口。
6
? ?
接口隔離
SOLID 中的 I 代表接口隔離,它只是意味著應該將較大的接口拆分為較小的接口。通過這樣做,我們可以確保實現類只需要關注它們感興趣的方法。
對于這個例子,我們將嘗試作為動物園管理員。更具體地說,我們將在熊圈工作。
讓我們從一個概述我們作為養熊人角色的界面開始:
public interface BearKeeper {void washTheBear();void feedTheBear();void petTheBear(); }作為狂熱的動物園管理員,我們非常樂意為我們心愛的熊清洗和喂食。但我們都非常清楚撫摸它們的危險。不幸的是,我們的接口比較大,我們只能實現代碼來寵熊。
讓我們通過將我們的大界面分成三個獨立的界面來解決這個問題:
public interface BearCleaner {void washTheBear(); }public interface BearFeeder {void feedTheBear(); }public interface BearPetter {void petTheBear(); }現在,由于接口隔離,我們可以自由地只實現對我們重要的方法:
public class BearCarer implements BearCleaner, BearFeeder {public void washTheBear() {//I think we missed a spot...}public void feedTheBear() {//Tuna Tuesdays...} }最后,我們可以把危險的東西留給魯莽的人:
public class CrazyPerson implements BearPetter {public void petTheBear() {//Good luck with that!} }更進一步,我們甚至可以將 BookPrinter 類從我們之前的示例中拆分出來,以同樣的方式使用接口隔離。通過使用單個 打印 方法實現 Printer 接口,我們可以實例化單獨的 ConsoleBookPrinter 和 OtherMediaBookPrinter 類。
7
? ?
依賴倒置
依賴倒置原理是指軟件模塊的解耦。這樣,高級模塊不再依賴于低級模塊,而是都依賴于抽象。
為了證明這一點,讓我們回到老派,用代碼讓 Windows 98 計算機栩栩如生:
public class Windows98Machine {}但是沒有顯示器和鍵盤的電腦有什么用呢?讓我們將每個添加到我們的構造函數中,以便我們實例化的每臺 Windows98Computer 都預裝了一個 Monitor 和一個 StandardKeyboard:
public class Windows98Machine {private final StandardKeyboard keyboard;private final Monitor monitor;public Windows98Machine() {monitor = new Monitor();keyboard = new StandardKeyboard();}}這段代碼可以工作,我們將能夠在我們的 Windows98Computer 類中自由 使用 StandardKeyboard 和 Monitor 。
問題解決了?不完全的。通過使用 new 關鍵字聲明 StandardKeyboard 和 Monitor ,我們將這三個類緊密耦合在一起。
這不僅使我們的 Windows98Computer 難以測試,而且我們也失去了在需要時將 StandardKeyboard 類換成不同的類的能力。我們也被我們的 Monitor 類所困。
讓我們通過添加一個更通用的鍵盤接口并在我們的類中使用它來將我們的機器與 StandardKeyboard 分離:
public interface Keyboard { }public class Windows98Machine{private final Keyboard keyboard;private final Monitor monitor;public Windows98Machine(Keyboard keyboard, Monitor monitor) {this.keyboard = keyboard;this.monitor = monitor;} }在這里,我們使用依賴注入模式來幫助將鍵盤依賴添加到 Windows98Machine 類中。
讓我們也修改我們的 StandardKeyboard 類來實現 Keyboard 接口,使其適合注入到 Windows98Machine 類中:
public class StandardKeyboard implements Keyboard { }現在我們的類被解耦并通過鍵盤抽象進行通信。如果需要,我們可以使用不同的接口實現輕松地切換機器中的鍵盤類型。我們可以對 Monitor 類遵循相同的原則。
優秀!我們已經解耦了依賴關系,并且可以 使用我們選擇的任何測試框架自由地測試我們的 Windows98Machine 。
8
? ?
結論
在本文中,我們深入探討了面向對象設計的 SOLID 原則。
我們首先簡要介紹了 SOLID 的歷史以及這些原則存在的原因。
我們用一個違反它的快速代碼示例分解了每個原則的含義。然后我們看到了如何修復我們的代碼 并使其符合 SOLID 原則。
與往常一樣,代碼可在 GitHub 上獲得。(點擊閱讀原文)
往期推薦
萬字長文 | 淘寶 10年架構演進
今天我要批判中臺!
今天我要批判技術管理者
今天我要批判架構師
Apache架構師的30條設計原則!
成為優秀軟件工程師的三條路徑
好代碼和壞代碼
圖勝千言:電商支付架構設計
一年之計:如何構建知識體系?
入行二十年的一些認知
總結
以上是生活随笔為你收集整理的SOLID 原则的可靠指南的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 慌的一批!新手妹子一个命令把公司服务器数
- 下一篇: NYOJ 692 Chinese che