SOLID 面對象設計的五大基本原則

SOLID 原則的價值

原則核心價值解決的問題
SRP職責分離,提高內聚性代碼臃腫、牽一發而動全身
OCP通過擴展而非修改實現變化頻繁修改現有代碼導致的風險
LSP確保子類行為的一致性繼承濫用導致的系統不穩定
ISP定制化接口,避免依賴冗余接口過大導致的實現負擔
DIP解耦高層與低層模塊模塊間強依賴導致的可維護性差

SOLID原則是一組面向對象編程和設計的五個基本原則,由羅伯特·C·馬丁在21世紀早期引入。這五個原則的首字母縮寫為SOLID,它們分別是:單一職責原則(Single Responsibility Principle)、開閉原則(Open-Closed Principle)、里氏替換原則(Liskov Substitution Principle)、接口隔離原則(Interface Segregation Principle)和依賴反轉原則(Dependency Inversion Principle)。

單一職責原則 (SRP)

單一職責原則 (SRP) 英文全稱為 Single Responsibility Principle,是最簡單,但也是最難用好的原則之一。它的定義也很簡單:對于一個類而言,應該僅有一個引起它變化的原因。其中變化的原因就表示了這個類的職責,它可能是某個特定領域的功能,可能是某個需求的解決方案。

單一職責原則用于控制類的粒度大小,減少類中不相關功能的代碼耦合,使得類更加的健壯;另外,單一職責原則也適用于模塊之間解耦,對于模塊的功能劃分有很大的指導意義。

這個原則表達的是不要讓一個類承擔過多的責任,一旦有了多個職責,那么它就越容易因為某個職責而被更改,這樣的狀態是不穩定的,不經意的修改很有可能影響到這個類的其他功能。因此,我們需要將不同的職責封裝在不同的類中,即將不同的變化原因封裝在不同的類中,不同類之間的變化互不影響。

面對違背單一職責原則的程序代碼,我們可以利用外觀模式,代理模式,橋接模式,適配器模式,命令模式 等對已有設計進行重構,實現多職責的分離。

我們在設計一個類的時候,可以先從粗粒度的類開始設計,等到業務發展到一定規模,我們發現這個粗粒度的類方法和屬性太多,且經常修改的時候,我們就可以對這個類進行重構了,將這個類拆分成粒度更細的類,這就是所謂的持續重構

開閉原則(OCP)

開閉原則 (OCP) 英文全稱為 Open-Closed Principle,基本定義是軟件中的對象(類,模塊,函數等)應該對于擴展是開放的,但是對于修改是封閉的。這里的對擴展開放表示這添加新的代碼,就可以讓程序行為擴展來滿足需求的變化;對修改封閉表示在擴展程序行為時不要修改已有的代碼,進而避免影響原有的功能。

要實現不改代碼的情況下,仍要去改變系統行為的關鍵就是抽象和多態,通過接口或者抽象類定義系統的抽象層,再通過具體類來進行擴展。這樣一來,無須對抽象層進行任何改動,只需要增加新的具體類來實現新的業務功能即可,達到開閉原則的要求

同樣,舉個例子來更深刻地理解開閉原則:有一個用于圖表顯示的 Display 類,它能繪制各種類型的圖表,比如餅狀圖,柱狀圖等;而需要繪制特定圖表時,都強依賴了對應類型的圖表,Display 類的內部實現如下:

public void display(String type) {if (type.equals("pie")) {  PieChart chart = new PieChart();  chart.display();  }  else if (type.equals("bar")) {  BarChart chart = new BarChart();  chart.display();  } 
}

基于上述的代碼,如果需要新增一個圖表,比如折線圖 LineChart ,就要修改 Display 類的 display() 方法,增加新增的判斷邏輯,很顯然這樣的做法違反開閉原則。而讓類的實現符合開閉原則的方式就是引入抽象圖表類 AbstractChart,作為其他圖表的基類,讓 Display 依賴這個抽象圖表類 AbstractChart,然后通過 Display 決定使用哪種具體的圖表類,實現代碼變成了這樣:

private Abstractchart chart;public void display() {chart.display();  
}

現在我們新增一個 LineChart 對象只需要繼承Abstractchart 實現其方法,無須修改之前的 BarChart 的代碼。

這個重構后的設計符合開閉原則,因為我們通過擴展子類來實現新的功能,而不需要修改父類的代碼和其他子類的代碼。這樣做的好處是,已有的代碼保持不變,不會引入新的錯誤,同時也增加了系統的可擴展性和可維護性,但會增加一定的復雜性。

總結起來,開閉原則鼓勵我們在設計軟件時,采用抽象、封裝和多態等方式,使得系統能夠以最小的修改來適應變化。這種設計思想能夠提高代碼的可復用性、可擴展性和可維護性,是良好的軟件設計實踐之一。

里式替換原則 (LSP)

里式替換原則 (LSP) 英文全稱為 Liskov Substitution Principle,基本定義為:在不影響程序正確性的基礎上,所有使用基類的地方都能使用其子類的對象來替換。這里提到的基類和子類說的就是具有繼承關系的兩類對象,當我們傳遞一個子類型對象時,需要保證程序不會改變任何原基類的行為和狀態,程序能正常運作。 一句話概括為: 能夠使用父類的地方,一定可以使用其子類,并且預期結果是一致的。

讓我們舉一個簡單的例子比方說,我們有一個動物超類,它有一個叫做 "咆哮 "的方法。現在我們有了我們的子類,例如狗和熊擴展了超類Animal。現在我們知道狗不會吼叫,但熊會,所以我們必須拋出一個狗不能吼叫的異常。所以子類(狗)實際上不能使用超類的這個咆哮方法,那么我們就違反了這個原則。

所以,我們需要確保我們的子類能夠實現超類所有暴露的方法并且預期結果是一致的,這樣才不會違反原則。

另一方面,里式替換原則也是對開閉原則的補充,不僅適用于繼承關系,還適用于實現關系的設計,常提到的 IS-A (父子繼承關系) 是針對行為方式來說的,如果兩個類的行為方式是不相容,那么就不應該使用繼承,更好的方式是提取公共部分的方法來代替繼承

里氏替換主要解決的問題:

  • 里氏替換解決了繼承中重寫父類造成的可復用性變差的問題
  • 是動作正確性的保證,即類的擴展不會給已有系統引入新的錯誤,降低了代碼出錯的可能性
  • 加強程序的健壯性,同時變更時可以做到非常好的兼容性,提高程序的維護性、可擴展性,降低需求變更時引入的風險。

接口隔離原則 (ISP)

接接口隔離原則(Interface Segregation Principle)的定義是:類間的依賴關系應該建立在最小的接口上。簡單地說:接口的內容一定要盡可能地小,能有多小就多小。

舉個例子來說,我們經常會給別人提供服務,而服務調用方可能有很多個。很多時候我們會提供一個統一的接口給不同的調用方,但有些時候調用方 A 只使用 1、2、3 這三個方法,其他方法根本不用。調用方 B 只使用 4、5 兩個方法,其他都不用。接口隔離原則的意思是,你應該把 1、2、3 抽離出來作為一個接口,4、5 抽離出來作為一個接口,這樣接口之間就隔離開來了。

那么為什么要這么做呢?我想這是為了隔離變化吧! 想想看,如果我們把 1、2、3、4、5 放在一起,那么當我們修改了 A 調用方才用到 的 1 方法,此時雖然 B 調用方根本沒用到 1 方法,但是調用方 B 也會有發生問題的風險。而如果我們把 1、2、3 和 4、5 隔離成兩個接口了,我修改 1 方法,絕對不會影響到 4、5 方法。

除了改動導致的變化風險之外,其實還會有其他問題,例如:調用方 A 抱怨,為什么我只用 1、2、3 方法,你還要寫上 4、5 方法,增加我的理解成本。調用方 B 同樣會有這樣的困惑。

依賴倒置原則(DIP)

依賴倒置原則 (DIP) 英文全稱 Dependency Inversion Principle, DIP),基本定義是:

  • 高層模塊不應該依賴低層模塊,應該共同依賴抽象;
  • 抽象不應該依賴細節,細節應該依賴抽象。

這里的抽象就是接口和抽象類,而細節就是實現接口或繼承抽象類而產生的類。

如何理解“高層模塊不應該依賴低層模塊,應該共同依賴抽象”呢?如果高層模塊依賴于低層模塊,那么低層模塊的改動很有可能影響到高層模塊,從而導致高層模塊被迫改動,這樣一來讓高層模塊的重用變得非常困難。

而最佳的做法就如上圖一樣,在高層模塊構建一個穩定的抽象層,并且只依賴這個抽象層;而由底層模塊完成抽象層的實現細節。這樣一來,高層類都通過該抽象接口使用下一層,移除了高層對底層實現細節的依賴。

依賴倒置原則可以減少類間的耦合性,提高系統的穩定性,降低并行開發引起的風險,提高代碼的可讀性和可維護性。同時依賴倒置原則也是框架設計的核心原則,善于創建可重用的框架和富有擴展性的代碼。

我們來看看一個非常簡單的寄信案例幫你理解依賴倒置,寄信這個業務,主要存在兩種角色:寄信人和郵遞員。 最開始寄信人強依賴于郵遞員,寄信需要送到郵遞員家。這種模式缺點比較明顯,郵遞員換了很麻煩。直到后面增加了郵筒,寄信人不再直接依賴郵遞員,而是依賴一個站著不會動的郵筒。

上面的郵筒,可以讓郵遞員再怎么變化,都不會影響到寄信人。這種將寄信人直接依賴郵遞員,改為寄信人和郵遞員互不依賴,兩者都依賴于郵筒的過程,正是“依賴倒置”

下面來看看系統中模塊的設計使用依賴倒置的場景:

比如 Tomcat 容器的 Servlet 規范實現,Spring Ioc 容器實現就是第一層境界的產品, 我們熟悉的 RPC 模式就是,第二層境界的產品 。 那么我們是不是可以總結為第一層境界是在系統內部模塊和模塊的協作,第二層境界是跨系統協作。

點贊 -收藏 -關注
有問題在評論區或者私信我-收到會在第一時間回復

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/81564.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/81564.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/81564.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python 裝飾器詳解

裝飾器是 Python 中一種強大的語法特性,它允許在不修改原函數代碼的情況下動態地擴展函數的功能。裝飾器本質上是一個高階函數,它接受一個函數作為參數并返回一個新的函數。 基本裝飾器 1. 簡單裝飾器示例 def my_decorator(func):def wrapper():prin…

無損耗協議:PROFINET和EtherNet IP網關的高效安裝指南

作為風力發電機組監控系統的重要組成部分,PROFINET和EtherNet/IP協議轉換網關倍訊BX-606-EIP的安裝至關重要。作為安裝工,我們要確保網關安裝的高效順利,保證風力發電機組的穩定運行。 首先,我們需要仔細檢查網關的硬件接口,確保所有連接線纜與設備端口相匹配。網關…

Axure元件動作四:設置選中

親愛的小伙伴,在您瀏覽之前,煩請關注一下,在此深表感謝!如有幫助請訂閱專欄! Axure產品經理精品視頻課已登錄CSDN可點擊學習https://edu.csdn.net/course/detail/40420 課程主題:設置選中 主要內容:選中效果全面解析 應用場景:元件、元件組合需要被選中場景 案例展…

大模型為什么學新忘舊(大模型為什么會有災難性遺忘)?

字數:2500字 一、前言:當學霸變成“金魚” 假設你班上有個學霸,數學考滿分,英語拿第一,物理稱霸全校。某天,他突然宣布:“我要全面發展!從今天起學打籃球!” 一周后&am…

通過SMTP協議實現Linux郵件發送配置指南

一、環境準備與基礎配置 1. SMTP服務開通(以qq郵箱為例) 登錄qq郵箱網頁端,進入「設置」-「POP3/SMTP/IMAP」 開啟「SMTP服務」并獲取16位授權碼(替代郵箱密碼使用) 記錄關鍵參數: SMTP服務器地址&#…

react中安裝依賴時的問題 【集合】

目錄 依賴升級/更新 1、 npm install --save-dev 與 npm install 的區別 1. ?安裝位置(依賴類型)? 2. ?package.json 中的區別? 3. ?示例 4. ?何時使用哪種方式? 2、npm install 和 yarn add 有什么不一樣嗎 ?命令語法?: …

Coze 實戰教程 | 10 分鐘打造你的AI 助手

> 文章中的 xxx 自行替換,文章被屏蔽了。 📱 想讓你的xxx具備 AI 對話能力?本篇將手把手教你,如何用 Coze 平臺快速構建一個能與用戶自然交流、自動回復提問的 xxx助手,零代碼、超高效! 📌…

【Spring Cloud Gateway】Nacos整合遇坑記:503 Service Unavailable

一、場景重現 最近在公司進行微服務架構升級,將原有的 Spring Cloud Hoxton 版本升級到最新的 2021.x 版本,同時使用 Nacos 作為服務注冊中心和配置中心。在完成基礎框架搭建后,我使用 Spring Cloud Gateway 作為API 網關,通過 N…

寶塔面板屏蔽垃圾搜索引擎蜘蛛和掃描工具的辦法

首先進入寶塔面板,文件管理進入/www/server/nginx/conf目錄,新建空白文件kill_bot.conf。然后將以下代碼保存到當前文件中。 #禁止垃圾搜索引擎蜘蛛抓取if ($http_user_agent ~* "CheckMarkNetwork|Synapse|Nimbostratus-Bot|Dark|scraper|LMAO|Ha…

Docker拉取鏡像報錯Error response from daemon: Get “https://registry-1.docker.io/v2/“

記一次Docker拉取鏡像的報錯 使用docker拉取鏡像時,出現報錯 [rootcentos8 ~]# sudo docker pull mysql:8.0 Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.T…

Ansible模塊——文件內容修改

修改文件單行內容 ansible.builtin.lineinfile 可以按行修改文件內容,一次修改一行,支持正則表達式。 選項名 類型 默認值 描述 attributesstrnull 設置目標文件的 Linux 文件系統屬性(attribute bits),作用類似于…

如何用PDO實現安全的數據庫操作:避免SQL注入

如何用PDO實現安全的數據庫操作:避免SQL注入 在現代Web應用程序中,數據庫操作是核心功能之一。然而,SQL注入是一種常見的安全漏洞,攻擊者可以通過惡意輸入來操控數據庫,從而獲取敏感信息或破壞數據。使用PHP的PDO&…

使用大語言模型從零構建知識圖譜(中)

從零到一:大語言模型在知識圖譜構建中的實操指南 ©作者|Ninja Geek 來源|神州問學 還沒有看過上篇的讀者可以閱讀《使用大語言模型從零構建知識圖譜(上)》了解整個系列的內容 通過創建一個自定義流程來自動上傳業務數據 在這一節&#…

pycharm連接github(詳細步驟)

【前提:菜鳥學習的記錄過程,如果有不足之處,還請各位大佬大神們指教(感謝)】 1.先安裝git 沒有安裝git的小伙伴,看上一篇安裝git的文章。 安裝git,2.49.0版本-CSDN博客 打開cmd(…

uniapp在APP上如何使用websocket--詳解

UniApp 在 APP 端如何使用 WebSocket以及常見問題 一、WebSocket 基礎概念 WebSocket 是一種在單個TCP連接上進行全雙工通信的協議,適用于實時數據傳輸場景(如聊天室、實時游戲、股票行情等)。 與傳統HTTP對比 特性WebSocketHTTP連接方式…

物聯網賦能7×24H無人值守共享自習室系統設計與實踐!

隨著"全民學習"浪潮的興起,共享自習室市場也欣欣向榮,今天就帶大家了解下在物聯網的加持下,無人共享自習室系統的設計與實際方法。 一、物聯網系統整體架構 1.1 系統分層設計 層級技術組成核心功能用戶端微信小程序/H5預約選座、…

【Linux】ELF與動靜態庫的“暗黑兵法”:程序是如何跑起來的?

目錄 一、什么是庫? 1. C標準庫(libc) 2. C標準庫(libstdc) 二、靜態庫 1. 靜態庫的生成 2. 靜態庫的使用 三、動態庫 1. 動態庫的生成 2. 動態庫的使用 3. 庫運行的搜索路徑。 (1)原因…

滲透測試流程-中篇

#作者:允砸兒 #日期:乙巳青蛇年 四月廿一(2025年5月18日) 今天筆者帶大家繼續學習,網安的知識比較雜且知識面很廣,這一部分會介紹很多需要使用的工具。會用各種工具是做網安的基礎,ok咱們繼續…

[創業之路-358]:從歷史輪回到制度躍遷:中國共產黨創業模式的超越性密碼

人類文明的演進如同一條螺旋上升的階梯,從原始社會的公有制到資本主義私有制的巔峰,再到社會主義對公有制的重構,每一次制度迭代都伴隨著對前序文明的揚棄。中國共產黨自誕生之日起,便以“為人類求解放”為使命,在革命…

NLP基礎

目錄 一、NLP 概述和應用 (一)NLP 的定義與演進歷程 (二)NLP 的多元應用領域 二、文本預處理技術 (一)文本獲取與編碼轉換 (二)文本清洗:去除雜質的精細打磨 &…