設計六大原則總結

来源:https://www.cnblogs.com/fomin/archive/2019/08/30/11434300.html
-Advertisement-
Play Games

1、單一職責原則(SRP) 定義 :就一個類而言,應該僅有一個引起它變化的原因 為什麼需要單一職責呢?如果一個類承擔的職責過多,就等於把這些職責耦合在一起了,一個職責的變化可能會引起其它職責的變化,當變化發生時,設計會遭到意想不到的變化。 我們看看下麵簡單的類圖,UserDiscount類具有兩個方 ...


1、單一職責原則(SRP)

定義:就一個類而言,應該僅有一個引起它變化的原因
為什麼需要單一職責呢?如果一個類承擔的職責過多,就等於把這些職責耦合在一起了,一個職責的變化可能會引起其它職責的變化,當變化發生時,設計會遭到意想不到的變化。
我們看看下麵簡單的類圖,UserDiscount類具有兩個方法,一個是獲取等級類型,一個是計算折扣價格。

有兩個不同的類在使用UserDiscount,Order需要獲取用戶等級和計算價格;User只需要獲取用戶等級,但不需要計算價格,這個設計違反類SRP,如果其中一個使用類的改變導致UserDiscount改變,這樣會導致其它使用類也需要變更、測試、部署等問題。我們需要拆分兩個職責類,如下圖:

但是,如果類的變化總是導致這兩個職責的同時變化,那麼就不必分離它們,實際上,分離它們可能會導致複雜性增加。或者說,變化的軸線僅當變化實際發生時才具有真正意義。如果沒有徵兆,那麼去應用SRP或者其它原則都是不可取的。
結論:SRP是最簡單的職責之一,但是也比較難正確運用的職責,在開發中,會自然地把職責結合在一起,畢竟有些職責需要耦合在一起的,而難以拆分並增加複雜性。

2、開放封閉原則(OCP)

定義:軟體實體(類、模塊、函數等等)應該是可以擴展的,但是不可以修改的

  • 對於擴展是開放的:模塊行為是可以擴展的,當應用需求改變時,我們可以對模塊進行擴展,使其滿足那些改變的行為。
  • 對於修改是封閉的:對模塊擴展時,不必改動模塊的源代碼

下麵來看個播放MP3的例子,MP3和Player都是具體類,MP3直接使用Player播放音樂,但是如果需要播放音頻,那麼就需要重新修改Player而導致MP3也需要修改。

下麵我們修改下例子而遵循OCP原則


這個設計中,IPlayer是一個介面,MP3和Video繼承該介面,今後想增加其它類型的播放只需要繼承IPlayer就行,無需修改MP3或Video類。
但實際開放中,無論模塊多麼封閉,都會存在一些無法對之封閉的現象,那就需要有策略的去對待這個問題,模塊應該對哪種變化封閉而做出選型,必須先猜測最有可能發生變化的情況,然後構造出抽象來隔離。
結論:遵循OOP可以帶來靈活性、可重用性、以及可維護性。然而,對於應用程式中每個部分都肆意的進行抽象同樣是不行的,這樣屬於不成熟抽象,我們只需要把頻繁變化的部分進行抽象就行。

3、Liskov替換原則(LSP)

定義:子類型必須能夠替換掉它們的基類型
舉個例子,函數a使用的參數是基類B,但是C類繼承基類B,但把C做為參數傳給了函數a而導致其發生錯誤,這樣就是違反了LSP原則。主要體現在下麵四個方面:

  • 子類必須實現父類的抽象方法,但不得重寫(覆蓋)父類的非抽象(已實現)方法。
  • 子類中可以增加自己特有的方法。
  • 當子類覆蓋或實現父類的方法時,方法的前置條件(即方法的形參)要比父類方法的輸入參數更寬鬆。
  • 當子類的方法實現父類的抽象方法時,方法的後置條件(即方法的返回值)要比父類更嚴格。

下麵來看下簡單類圖,違反來SRP原則,定義了一個Rectangle和一個繼承自Rectangle的Square,看著是非常符合邏輯的,但是我們重新設置Rectangle的寬度,會導致Square的寬度也會變動,導致Square出錯。

改變一下不符合SRP,我們再定義一個他們共同的父類Graphics,然後讓Rectangle和Square都繼承自這個父類。在基類Graphics類中沒有賦值方法,因此重設寬高不可能適用於Graphics類型,而只能適用於不同的具體子類Rectangle和Aquare,因此里氏替換原則不可能被破壞。並且下麵的設計也符合OCP原則。

結論:使用LSP,使得程式具有更多的可維護性、可重用性以及健壯性。而LSP是使OCP成為可能的主要原則之一,子類型的可替換性才使得基類類型的模塊在無需修改的情況下可以擴展。

4、依賴倒置原則(DIP)

定義:高層模塊不應該依賴於低層模塊,二者應該依賴於抽象;抽象不應該依賴於細節,細節應該依賴於抽象。
下麵來看下簡單例子,用戶有多個用戶等級類UseOrdinary和UserDiamond,而UserTypeService使用等級類進行相關的邏輯處理,今後如果增強其它用戶等級,就需要修改UserTypeService,這樣違反類DIP,高層策略沒有和低層實現分離,抽象沒有和具體細節分離,沒有這種分離,高層策略就自動地依賴於低層策略,抽象就自動地依賴於具體細節。

我們變更下具體的實現方式,抽象出UseType介面,UseOrdinary和UserDiamond繼承該介面,而UserTypeService使用了UseType,不管今後增加什麼用戶等級都無需修改UserTypeService,並對於具體的實現類我們是不管的,只要介面的行為不發生變化,增加新的用戶等級後,上層服務不用做任何的修改。這樣設計降低了層與層之間的耦合,能很好地適應需求的變化,大大提高了代碼的可維護性。

結論:設置倒置的依賴關係結構,使得細節和策略都依賴於抽象,屬於面向對象設計;如果依賴關係不倒置,屬於過程化設計。

5、介面隔離原則(ISP)

定義:不應該強迫繼承類依賴於它們不使用的介面方法,類間的依賴關係應該建立在最小的介面上
使用者依賴了那些它們不使用的方法,就面臨著這些未使用的方法改變而帶來的變更,無意中導致了它們之間的耦合,下麵來看下簡單示例,MatchingHandler是一個匹配介面,包含匹配系統ID(handleSystemId)和處理聯賽ID(detectLeagueId),MatchMatching和LeagueMatching繼承了該介面,但MatchMatching不需要處理處理聯賽ID,也繼承了該方法,這樣方法改變而帶來的變更。

我們在來看下變更後的簡單類圖,新增了LeagueMatchingHandler(detectLeagueId),LeagueMatching繼承了該介面,detectLeagueId方法的變更不會導致MatchMatching也需要變更,只會影響到LeagueMatching。

結論:胖類是這個類過於臃腫,可能會導致使用者產生不正常的耦合關係,該類的修改也會導致使用者的修改。使用介面分解,使用者只需要使用特定的介面,並解除了和胖類的耦合關係。

6、迪米特原則(LOD)

定義:類之間儘可能少與其他實體發生相互作用
在開發中,我們經常提到高內聚低耦合,使各個模塊之間的耦合儘量的低,才能提高代碼的復用率,耦合的方式很多,依賴、關聯、組合、聚合等。其中,我們稱出現成員變數、方法參數、方法返回值中的類為低耦合,而出現在局部變數中的類則高耦合。也就是說,陌生的類最好不要作為局部變數的形式出現在類的內部。下麵我們來看下例子,定義了Match,Team和Player,Match都引用了Team和Player,Team又引用了Player,這樣違反了LOD,導致了Match跟Player耦合增加。

下麵我們來變更下引用,Match只需要引用了Team就行,無需在引用Palyer,因為Team已經引用了Player。這樣Match可以列印出相關選手了。

結論:LOD的初衷是降低類之間的耦合,由於每個類都減少了不必要的依賴,因此的確可以降低耦合關係,但這樣必須會產生一個中介類,由這個中介類來處理類之間的通信,過多的中介類會導致系統複雜度增大而難以維護。設計的時候需要權衡,保持結構清晰和高內聚低耦合


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、網路伺服器 1、http狀態碼 1xx: 表示普通請求,沒有特殊含義 2xx:請求成功 + 200:請求成功 3xx:表示重定向 + 301 永久重定向 + 302 臨時重定向 + 303 使用緩存(伺服器沒有更新過) 4xx:無法訪問 + 403:無法訪問 + 404:資源找不到 5xx:服務 ...
  • 01. Object構造函數模式 方式一: Object構造函數模式 * 套路: 先創建空Object對象, 再動態添加屬性/方法 * 適用場景: 起始時不確定對象內部數據 * 問題: 語句太多 02. 對象字面量 方式二: 對象字面量模式 * 套路: 使用{}創建對象, 同時指定屬性/方法 * 適 ...
  • js註釋 JavaScript註釋與Java註釋相同 js五大基本類型:   number(數值型)、string(字元串性)、boolean(布爾型)、undefined類型、null類型 number 包括:數值類型、包含整型、浮點型、NaN和Infinity(無窮大)。 關 ...
  • 註釋標簽:對代碼進行說明 常用格式標簽 標題標簽 列表標簽 無序列表: 有序列表: 自定義列表:圖文混排 CSS樣式   內嵌樣式:放在``標簽之間 語法: 所有標簽( ) 文本屬性 字體標簽及屬性 背景相關的屬性 超鏈接: 郵件超鏈接 圖片標簽 錨點鏈接 圖片滾動 分隔視窗 塊 ...
  • v3傳送門 v3文檔其實思考了很久, 還是放棄了v3, 因為開發v3的時候很粗暴, 按照編程角度來看就是硬擼, 也怪自己實力有限, 所以反思了一下, 嘗試著繼續前行... 2、交流學習 QQ號: 707200833 QQ群: 769620939 重要的事情說三遍, 看文檔, 看文檔, 看文檔, 文檔 ...
  • 大多數野生程式員最棘手的問題就是如何依靠技術解決溫飽,通俗來講就是技術折現的問題。 如果是單純出於興趣,或者只是為了突擊某一階段或者某一項目技術壁壘,不跟就業掛鉤的自學倒也是無關痛癢。但是當上崗成為自學的終極目標和結果時,一切都就另當別論了。 前端自學者存在的學習誤區: 1、所學東西可能已過時 奉為 ...
  • 摘要: 玩轉JS字元串。 原文: "JS 前20個常用字元串方法及使用方式" 譯者:前端小智 "Fundebug" 經授權轉載,版權歸原作者所有。 本文主要介紹一些最常用的 字元串函數。 1. charAt(x) 返回字元串中 位置的字元,下標從 開始。 2. charCodeAt(x) js 返回 ...
  • 0830總結 Vue CLI 項目中相關操作 一.前臺路由的基本工作流程 目錄結構 App.vue:根組件 Nav.vue:小組件 PageFirst.vue:頁面組件 PageSecond.vue:頁面組件 router.js:路由配置 二.配置全局樣式文件 目錄結構 global.css mai ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...