設計六大原則總結

来源: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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...