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