S.O.L.I.D原則的重要性

> S.O.L.I.D principles
介紹
作為開發人員,我們一直在處理遺留代碼庫。 大多數傳統代碼庫具有緊密耦合的類,冗余代碼和較少的測試范圍。 快速瀏覽代碼時,這會使開發人員難以理解代碼庫的功能。
想象一下在類中遍歷無數行代碼以僅修復錯誤的痛苦。 開發人員最終可能會閱讀更多的代碼行,而不是編寫更多的代碼行。 此外,固定一個流程可能會導致另一個流程中斷。 這使我想起以下著名的模因。

> Fix one bug & you have 10 more ready
由于在舊版軟件中沒有進行積極的開發,因此使開發人員和管理人員陷入了困境。 然后,團隊考慮重新編寫整個服務并棄用舊的服務。

為什么采用軟件設計原則?
在當今不斷發展的世界中,客戶需求以前所未有的速度不斷變化。 對于軟件團隊來說,適應新的需求并迅速發布變更變得至關重要。 為此,必須減少軟件開發和測試時間。
同時,每隔一年就會引入新技術。 通常,通過替換現有技術來嘗試更優化,更高效的技術。 因此,編寫的代碼必須靈活且松散地耦合以引入任何更改。
編寫良好的代碼很容易掌握。 新開發人員不必花更多的時間來閱讀代碼,而只需修改部分代碼即可。 維護良好的軟件可以提高開發人員和團隊的生產力。 此外,高測試覆蓋率增加了部署新變更的信心。
S.O.L.I.D是Michael Feathers創造的首字母縮寫詞,也是Robert Martin(鮑勃叔叔)發表的原理的子集。 我們將介紹這五個原則,并為每個原則進行說明。
S-單一責任原則
這是最簡單的原理之一。 它指出"一個班級只有一個改變的理由"。 很多時候,您可能會發現一類執行的功能比預期的要多。
假設您正在編寫銀行軟件的代碼。 該功能是顯示給定用戶的聲明。 該代碼從數據庫中獲取數據,并以用戶選擇的格式顯示數據。 您最終編寫了以下代碼。

> BankStatementManager
從上面的代碼片段中可以看到," BankStatementMgr"類一次執行多項操作。 它正在從數據庫中獲取數據,解析結果,然后以用戶指定的格式顯示它。 您可以觀察到以下缺陷:
· 沒有責任隔離。 如果引入了新格式或添加了新的數據庫列,則該類將需要更改
· 該類與數據庫驅動程序緊密耦合。 數據庫驅動程序或SQL查詢中的任何更改都將導致對該類的修改
· 交易格式無法單獨測試,因為BankStatementMgr不會公開交易格式
· 該代碼不是模塊化的,因為多個功能相互交織
以上缺點可以通過以下方法克服:
· 定義一個單獨的格式化程序,其職責是格式化事務
· 添加一個數據庫訪問對象或DAO,它將封裝數據庫驅動程序并完成所有查詢任務
· BankStatementMgr會將請求委托給DAO以獲取數據,然后將響應傳遞給格式化程序以進行美化
· 通過這種方式,我們可以隔離地測試DAO和Formatter并實現松散耦合。 因此,它將通過分離職責來使代碼模塊化
以下是我們的修改代碼:

> BankStatementManager

> StatementFormatter

> TransactionDAO
仍有很多改進的余地,我們將在下一部分中看到如何重構和使事情變得更好。
O — 開閉原理
該原則指出,代碼應打開以進行擴展,并應關閉以進行修改。 如果需要添加新功能,則必須擴展該類。 此外,為使系統可擴展,應將其行為分開。
我們將通過一個例子來理解這一點。 假設您是接受不同方式付款的電子商務商人。 您已經集成了Paypal,Wepay,google Pay等不同的模式,并開發了付款處理器。 您提出以下代碼。

> PaymentProcessor

> PaymentHandler
PaymentHandler處理付款請求。 PaymentProcessor確定模式并將其委托給正確的操作。 此代碼違反了開閉原則,因為任何功能都需要在PaymentProcessor和PaymentHandler中進行修改。 此設計不可擴展,因為每種新的付款方式都會在switch語句中引入一個新的case塊。
為了使代碼可擴展,我們可以使PaymentHandler抽象,并定義一種處理Payment的方法。 為了處理新的付款方式,我們可以擴展此基類并覆蓋其handlePayment方法。 下面是新代碼。

> abstract PaymentHandler

> GooglePayHandler

> CardPaymentHandler
現在,我們將創建一個工廠類,該類將負責存儲特定的處理程序并根據模式返回它。

> PaymentHandlerFactory

> PaymentProcessor
我們的新代碼現在符合開閉原則。 要添加新的行為,我們只需要擴展抽象類PaymentHandler并在工廠中對其進行配置。 無需修改PaymentProcessor。
L-Liskov替代原理
乍一看,這個名字聽起來很嚇人。 該原理指出,同一超類的對象應該能夠彼此替代而不破壞現有代碼。
我們將以為電影開發剪貼簿為例。 抓取工具提供了一個界面,可以按電影名稱或演員搜索電影。

> MovieSearch

> IMDB Search

> Rotten Tomatoes Search

> Client code using the MovieSearch interface
我們有兩種不同的實現。 一個用于爛番茄,另一個用于IMDB。 它們都是可替換的,并且可以使用相同的接口進行訪問。
如果未實現派生類中的方法,則會違反該原理。 以下是違反Liskov原理的示例。

> AllMoviesSearch
在這種情況下,我們無法將其他派生類(例如IMDB和爛番茄)替換為"所有電影"。 該方法未實現searchByMovieName方法,并且不會在客戶端代碼中導致一致的行為。
I —接口隔離
根據此原則,不應讓客戶實施不需要的方法。 如果您定義了客戶端不使用的方法,則接口將變得過于龐大且受到污染。
如果某個接口因混合功能而變得太大,則可以將其隔離為多個較小的接口。 讓我們看一下投資組合服務的示例,該服務允許客戶訂購股票,ETF,期權等

> Interface Portfolio
我們定義了一個接口Portfolio,該接口允許客戶訂購股票,ETF,以及兩者的組合。

> ETFOrderService

> StockOrderService
如果我們決定在訂購股票時增加價格作為參數怎么辦? 它將需要更改orderStocks方法以接受價格作為參數。 此外,此更改必須由ETFOrderService合并,即使它不支持orderStocks方法。
為了克服這個問題,我們可以將接口分成兩個部分-a)StockPortfolio b)ETFPortfolio

> StockPortfolio

> ETFPortfolio
使用新的接口,StockOrderService無需處理訂購ETF。 這同樣適用于ETFOrderService。

> ETFOrderService

> StockOrderService
界面隔離與"單一責任"和" Liskov替代原則"有一些相似之處。
在上面帶有龐大接口的示例中,我們在StockOrderService中引發了Exception。 這違反了《里斯科夫換人原則》。 在這種情況下,派生類不會擴展功能。
如果在接口中定義了不相關的方法,則該類將有多個更改原因。 這違反了單一責任原則。
D —依賴倒置
根據依賴倒置,程序中的高級模塊一定不能與低級模塊緊密耦合。 兩個模塊都必須依賴抽象。 該原理提供了一種構建松耦合軟件模塊的機制。
讓我們看看以下示例。 在此示例中,類OrderHistory從PostgreSQL數據存儲中獲取數據。

> OrderHistory
OrderHistory類必須知道PostgresDB依賴項的實現細節。 如果我們決定使用其他數據庫驅動程序,則需要用新的依賴項替換所有PostgresDB實例。
此外,DB驅動程序更改的一項功能是什么? 它還需要在OrderHistory類中進行更改,以調用數據庫驅動程序的方法。
可以通過聲明接口DataStore除去此耦合。 此接口將公開使用者將調用的API。 我們可以有DataStore的多種實現-a)Postgres DataStore b)MySQL DataStore c)S3,等等

> DataStore

> PostgresDataStore

> OrderHistory
我們的消費者類別現在不必處理正在使用什么數據存儲的底層細節。 高級模塊OrderHistory依賴于接口DataStore來訪問數據。 較低級別的DataStore實現的任何更改都不會對OrderHistory產生任何影響。
此外,由于模塊松散耦合,因此可以獨立測試它們。 使用依賴注入,可以輕松地將新的實現注入到高級模塊中。
結論
以上五項原則構成了軟件工程遵循的最佳實踐的基石。 在日常工作中實踐上述原理有助于提高軟件的可讀性,模塊化,可擴展性和可測試性。
最終,它有助于構建易于維護的,易于維護的軟件。 遵循上述做法有助于提高開發人員的生產率和工程團隊的敏捷性。
參考資料
· Android中的扎實原則
· 簡化基本原則
· 里斯科夫替代原則
· 通過應用SOLID原則成為更好的開發人員
· 五分鐘的SOLID原理
· 封面照片
編碼面試題 Skilled.dev
一個完整的平臺,在該平臺上,我可以教您找到下一份工作所需的一切,以及可以實現以下目標的技術:
(本文翻譯自Viktors Telle的文章《SOLID Principles — Simplified with Illustrations》,參考:https://levelup.gitconnected.com/solid-principles-simplified-with-illustrations-fe5265f68ec6)