經典設計原則 - SOLID

来源:https://www.cnblogs.com/fatedeity/archive/2022/08/18/16598137.html
-Advertisement-
Play Games

SOLID 原則是面向對象編程和麵向對象設計的五個基本原則。當這些原則被一起應用時,它們使得一個程式員開發一個容易進行軟體維護和擴展的系統變得更加可能。 ...


SOLID 設計原則包含以下 5 種原則:

  • 單一職責原則(Single Responsibility Principle, SRP)
  • 開閉原則(Open Closed Principle, OCP)
  • 里式替換原則(Liskov Substitution Principle, LSP)
  • 介面隔離原則(Interface Segregation Principle, ISP)
  • 依賴反轉原則(Dependency Inversion Principle, DIP)

單一職責原則

理解

單一職責原則的描述是,一個類或者模塊只負責完成一個職責(或功能)。當然,單一職責原則不止是可以針對於模塊或類,對於很多粒度都有效果,如函數、類、介面、模塊等等,模塊通常由多個類組成。

職責可以指模塊變化的原因,從這個角度理解,單一職責原則表示不要存在超過一個導致模塊變更的原因。

需要註意的是,不同的應用場景、不同階段的需求背景、不同的業務層面,對同一個類的職責是否單一,可能會有不同的判定結果。

優點

遵循單一職責原則,將會有以下的優點:

  • 提高代碼的可維護性:職責越少,複雜度越低,可讀性更好,可維護性就更高
  • 降低代碼變更的風險:職責越多,代碼變更的可能性就越高,變更帶來的風險也就越大

最佳實踐

在實際開發中,出現以下現象有可能違反了單一職責原則:

  • 模塊的變數、屬性或代碼行數過多
  • 模塊的內部對外部依賴過多
  • 模塊的私有方法過多
  • 難以給模塊取一個合理的名稱
  • 模塊的大部分操作只針對幾個屬性

如出現上述情況,則需要判斷是否對代碼做職責分離,以遵循單一職責原則,最終應以提高內聚、降低耦合、保證代碼的可維護性為主。

開閉原則

理解

開閉原則的描述是,軟體實體(模塊、類、方法等)應該“對擴展開放、對修改關閉”。

詳細的解釋就是,添加一個新的功能時,在已有代碼基礎上擴展代碼(新增模塊、類、方法等),而非修改已有代碼(修改模塊、類、方法等)。更寬鬆的理解是以最小的修改代碼的代價來完成新功能的開發。

優點

遵循開閉原則,將會有以下的優點:

  • 減少測試範圍:修改的代碼範圍越小,涉及的測試範圍越小,未改動的測試代碼仍能正常運行
  • 降低維護成本:軟體規模越大、壽命越長,則軟體的維護成本越高

最佳實踐

若要做到“對擴展開發、對修改關閉”,有以下幾點需要註意:

  • 時刻具備擴展意識、抽象意識、封裝意識,多花時間設計代碼結構,事先留好擴展點
  • 大部分經典設計模式都是為瞭解決代碼的擴展性問題而總結出來的,開閉原則是它們一個重要的評價依據

里式替換原則

理解

里式替換原則的描述是,子類對象能夠替換程式中父類對象出現的任何地方,並且保證原來程式的邏輯行為不變及正確性不被破壞。

從代碼實現上看,面向對象的多態和里式替換原則有點類似,但是它們的關註點不一樣:里式替換原則是用來指導繼承關係中子類該如何設計,子類的設計要保證在替換父類的時候,不改變原有程式的邏輯以及不破壞原有程式的正確性。

優點

遵循里式替換原則,將會有以下的優點:

  • 實現有意義的繼承:保證了父類的復用性,也降低了系統出錯誤的故障,防止誤操作,同時也不會破壞繼承的機制
  • 增強程式的健壯性:不同的子類可以完成不同的業務邏輯,即使增加子類也能保持非常好的相容性

最佳實踐

通常,需要註意以下違反里式替換原則的代碼:

  • 子類違背父類聲明要實現的功能,如將加法改成減法
  • 子類違背父類對輸入、輸出、異常的約定,如同一情況拋出的異常不同等
  • 子類違背父類註釋中所羅列的任何特殊聲明

介面隔離原則

理解

介面隔離原則的描述是,介面的調用者或使用者不應該被強迫依賴它不需要的介面。

通過對介面的理解不同,介面隔離原則有以下三種理解:

1、如果把“介面”理解成一組介面集合,可以是某個微服務的介面,也可以是某個類庫的介面等。如果存在部分介面只被部分調用者使用,就需要將這部分介面隔離出來,單獨給這部分調用者使用,而不強迫其他調用者也依賴其他不會用到的介面。

2、如果把“介面”理解成單個 API 介面或函數,部分調用者只需要其中的部分功能,則需要將這個函數拆分成更細粒度的多個函數,讓調用者只依賴它需要的那個細粒度函數。

3、如果把“介面”理解成 OOP 中的介面,也可以理解成為面向對象編程語言中的介面語法,那介面的設計要儘量單一,不要讓介面的實現類和調用者依賴不需要的介面函數。

介面隔離原則和單一職責原則有點類似,但介面隔離原則更側重於介面的設計,通常是通過調用者如何使用介面來定義這個介面的設計是否足夠職責單一。

優點

遵循介面隔離原則,將會有以下的優點:

  • 高內聚,低耦合:拆分成更小粒度的介面,減少對外的交互,預防外來的變更,提高系統的靈活性和可維護性
  • 可讀性高,易於維護:合理的介面拆分粒度能保證系統的穩定性,減少項目工程的代碼冗餘

最佳實踐

採用介面隔離原則對介面進行約束時,要註意以下幾點:

  • 介面儘量小,但是要有限度。定義過小,則會造成介面數量過多,使設計複雜化;定義多大,靈活性降低
  • 每個項目和產品都有選定的環境因素,環境不同,介面拆分的標準就不同,深入瞭解業務邏輯

依賴反轉原則

理解

依賴反轉原則也被叫作依賴倒置原則,其含義是:高層模塊不要依賴底層模塊,高層模塊和底層模塊應該通過抽象來互相依賴;抽象不要依賴具體實現細節,具體實現細節依賴抽象。

Tomcat 是運行 Java Web 應用程式的容器,編寫的 Web 應用程式代碼只需要部署在 Tomcat 容器中下,便可被 Tomcat 容器調用執行。在這裡,Tomcat 容器就是高層模塊,Web 應用程式就是底層模塊。Tomcat 容器和 Web 應用程式沒有直接的依賴關係,而是通過 Servlet 規範實現互相依賴,而 Servlet 規範也不會依賴具體的實現細節,而是 Tomcat 和 Web 應用程式依賴 Servlet 規範。

控制反轉

控制反轉(Inversion Of Control, IoC)指的是將程式員自己對程式執行流程的控制反轉成通過框架控制。控制反轉並不是一種具體的設計技巧,而是一種籠統的設計思想,一般用來指導框架層面的設計。

實現控制反轉主要有兩種方式:依賴註入和依賴查找。兩者的區別在於,前者是被動的接收對象,在類 A 的實例創建過程中即創建了依賴的 B 對象,通過類型或名稱來判斷將不同的對象註入到不同的屬性中,而後者是主動索取相應類型的對象,獲得依賴對象的時間也可以在代碼中自由控制。

依賴註入

依賴註入(Dependency Injection, DI)是一種具體的編碼技巧。

其詳細概括就是:不通過 new 的方式在類的內部創建依賴對象,而是將依賴的類對象在外部創建好之後,通過構造函數、函數參數等方式傳遞(或註入)給類使用。

一個簡單的依賴註入代碼例子如下:

package cn.fatedeity.designpattern.philosophy;

/**
 * 依賴註入案例
 */
public class DependencyInjectionCase {
    private MessageSender messageSender;

    public DependencyInjectionCase(MessageSender messageSender) {
        this.messageSender = messageSender;
    }

    public void sendMessage(String phone, String message) {
        this.messageSender.send(phone, message);
    }

    public static void main(String[] args) {
        MessageSender smsSender = new SmsSender();
        DependencyInjectionCase dependencyInjectionCase0 = new DependencyInjectionCase(smsSender);
        // SmsSender sms send sms message
        dependencyInjectionCase0.sendMessage("sms", "sms message");

        MessageSender inboxSender = new InboxSender();
        DependencyInjectionCase dependencyInjectionCase1 = new DependencyInjectionCase(smsSender);
        // SmsSender inbox send inbox message
        dependencyInjectionCase1.sendMessage("inbox", "inbox message");
    }
}

class InboxSender implements MessageSender {
    @Override
    public void send(String phone, String message) {
        System.out.println("InboxSender " + phone + " send "+ message);
    }
}

class SmsSender implements MessageSender {
    @Override
    public void send(String phone, String message) {
        System.out.println("SmsSender " + phone + " send "+ message);
    }
}

interface MessageSender {
    void send(String phone, String message);
}

優點

遵循依賴反轉原則,將會有以下的優點:

  • 查詢依賴和應用代碼分離,大量降低工廠類和單例類的數量,代碼層次更加清晰
  • 沒有侵入性,無須依賴容器的 API,也無須實現一些特殊介面

最佳實踐

通過依賴註入提供的擴展點,簡單配置一下所有需要的類及其類之間依賴關係,就可以實現由框架來自動創建對象、管理對象的生命周期、依賴註入等功能。

現成的依賴註入創建有很多,比如 Google Guide、Java Spring、Pico Container、Butterfly Container 等。

首發於翔仔的個人博客,點擊查看更多。


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

-Advertisement-
Play Games
更多相關文章
  • 大學的時候學過web前端的課,但是主要是講大框架,對於具體的一些概念沒有講那麼細。最後交大作業項目就是用到啥了現場百度啥。 現在工作了,雖然也寫了一些頁面,接觸了echarts,axios,也用過vue+elementui等等,但是發現很多前端的概念我並不懂,還是一知半解的狀態。再加上最近是有考慮這 ...
  • provide和inject依賴註入 點擊打開視頻講解更詳細 在此之前,在我們描述訪問父級組件實例的時候,展示過一個類似這樣的例子: <google-map> <google-map-region v-bind:shape="cityBoundaries"> <google-map-markers ...
  • 在 Vue 開發中,實現一個功能有很多種方式可以選擇,這依賴於 Vue 強大的功能(指令、混合、過濾、插件等),本文介紹一下插件的開發使用。 ...
  • 一、為什麼會出現跨域問題 點擊打開視頻講解更詳細 出於瀏覽器的同源策略限制。同源策略(Sameoriginpolicy)是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,則瀏覽器的正常功能可能都會受到影響。可以說Web是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實現。同 ...
  • 1.日期轉時間戳 this.timeChou('2022-10-20') // 輸入日期返回時間戳 1666224000000 timeChou(time) { const timeData = new Date(time).getTime() return timeData } 2.時間戳轉日期 ...
  • 在B/S系統開發中,前後端分離開發設計已成為一種標準,而VUE作為前端三大主流框架之一,越來越受到大家的青睞,Antdv是Antd在Vue中的實現。本系列文章主要通過Antdv和Asp.net WebApi開發學生信息管理系統,簡述前後端分離開發的主要相關內容,僅供學習分享使用,如有不足之處,還請指... ...
  • 大概是在18年的時候,當時還沒有疫情。當時工作中同時負責多個項目,有 PC 端運營管理後臺的,有移動端 M 站的,有微信小程式的,每天 git 分支切到頭昏眼花,每個需求提測需要發送郵件,而且周五要寫煩人的周報,我就萌生了做一個任務管理系統的想法。其實不管是日常需求還是處理線上 bug,都可以看作一 ...
  • 使用ref訪問子組件實例或子元素 點擊打開視頻講解更加詳細 儘管存在 prop 和事件,有的時候你仍可能需要在 JavaScript 里直接訪問一個子組件。為了達到這個目的,你可以通過 ref 這個 attribute 為子組件賦予一個 ID 引用。 <template> <div id="app" ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...