面向對象的SOLID原則白話篇

来源:http://www.cnblogs.com/wuyuegb2312/archive/2017/06/15/7011708.html
-Advertisement-
Play Games

用通俗易懂、並且儘量短小精悍的正反例,介紹面向對象SOLID原則。大約15分鐘左右可以消化完成。 ...


面向對象的SOLID原則

簡介

縮寫 全稱 中文
S The Single Responsibility Principle 單一責任原則
O The Open Closed Principle 開放封閉原則
L Liskov Substitution Principle 里氏替換原則
I The Interface Segregation Principle 介面分離原則
D The Dependency Inversion Principle 依賴倒置原則

單一職責原則

一個類只應承擔一種責任。換句話說,讓一個類只做一件事。如果需要承擔更多的工作,那麼分解這個類。

舉例

訂單和賬單上都有流水號、業務時間等欄位。如果只用一個類表達,賦予其雙重職責,後果:

  1. 特有屬性和共有屬性相互摻雜,難以理解;
  2. 修改一個場景可能會影響另一個場景。

正確的做法是拆成兩個獨立的類。

開放封閉原則

實體應該對擴展是開放的,對修改是封閉的。即,可擴展(extension),不可修改(modification)。

舉例

一個商戶接入了多個付款方式,支付寶和微信支付,如果將調用支付API的類寫成:

public class PayHandler {

    public Result<T> pay(Param param) {
        if(param.getType() == "ALIPAY") {
            // 支付寶付款調用
            ...
        } else if(param.getType() == "WeChatPay") {
           // 微信支付付款調用
           ...
        }
    }
}

那麼每次新加一種支付方式,或者修改原有的其中一種支付方式,都要修改PayHandler這個類,可能會影響現有代碼。

比較好的做法是將不同的行為(支付方式)抽象,如下:

public class PayHandler {

    private Map<String, PayProcessor> processors;

    public Result<T> pay(Param param) {
        PayProcessor payProcessor = processors.get(param.getType());
        // 異常處理略
        return payProcessor.handle(param);
    }
}

interface PayProcessor {
    Result<T> handle(Param param);
}

public class AlipayProcessor implements PayProcessor {
    ...
}

public class WeChatPayProcessor implements PayProcessor {
    ...
}

這樣,新增支付方式只需要新增類,如果使用的是spring等容器,在xml配置對應key-value關係即可;修改已有的支付方式只需要修改對應的類。最大化地避免了對已有實體的修改。

里式替換原則

一個對象在其出現的任何地方,都可以用子類實例做替換,並且不會導致程式的錯誤。換句話說,當子類可以在任意地方替換基類且軟體功能不受影響時,這種繼承關係的建模才是合理的。

舉例

經典的例子: 正方形不是長方形的子類。原因是正方形多了一個屬性“長 == 寬”。這時,對正方形類設置不同的長和寬,計算面積的結果是最後設置那項的平方,而不是長*寬,從而發生了與長方形不一致的行為。如果程式依賴了長方形的面積計算方式,並使用正方形替換了長方形,實際表現與預期不符。

擴展

不能用繼承關係(is-a),但可以用委派關係(has-a)表達。上例中,可以使用正方形類包裝一個長方形類。或者,將正方形和長方形作進一步抽象,使用共有的抽象類。

逸聞

“里氏”指的是芭芭拉·利斯科夫(Barbara Liskov,1939年-),是美國第一個電腦科學女博士,圖靈獎、馮諾依曼獎得主,參與設計並實現了OOP語言CLU,而CLU語言對現代主流語言C++/Java/Python/Ruby/C#都有深遠影響。其項目中提煉出來的數據抽象思想,已成為軟體工程中最重要的精髓之一。(來源: 互動百科

介面分離原則

客戶(client)不應被強迫依賴它不使用的方法。即,一個類實現的介面中,包含了它不需要的方法。將介面拆分成更小和更具體的介面,有助於解耦,從而更容易重構、更改。

舉例

仍以商家接入移動支付API的場景舉例,支付寶支持收費和退費;微信介面只支持收費。

interface PayChannel {
    void charge();
    void refund();
}

class AlipayChannel implements PayChannel {
    public void charge() {
        ...
    }
    
    public void refund() {
        ...
    }
}

class WeChatChannel implements payChannel {
    public void charge() {
        ...
    }
    
    public void refund() {
        // 沒有任何代碼
    }
}

第二種支付渠道,根本沒有退款的功能,但是由於實現了PayChannel,又不得不將refund()實現成了空方法。那麼,在調用中,這個方法是可以調用的,實際上什麼都沒有做!

改進

將PayChannel拆成各包含一個方法的兩個介面PayableChannel和RefundableChannel。

依賴倒置原則

  1. 高層次的模塊不應依賴低層次的模塊,他們都應該依賴於抽象。
  2. 抽象不應依賴於具體實現,具體實現應依賴抽象。

實際上,依賴倒置是實現開閉原則的方法。

舉例

開閉原則的場景仍然可以說明這個問題。以下換一種表現形式。

public class PayHandler {

    public Result<T> pay(Param param) {
        if(param.getType() == "ALIPAY") {
            AlipayProcessor processor = new AlipayProcessor();
            processor.hander(param);
            ...
        } else if(param.getType() == "WeChatPay") {
            WeChatPayProcessor processor = new WeChatPayProcessor();
            processor.hander(param);
           ...
        }
    }
}

public class AlipayProcessor { ... }

public class WeChatPayProcessor { ... }

這種實現方式,PayHandler的功能(高層次模塊)依賴了兩個支付Processor(低層次模塊)的實現。

擴展:IOC和DI

控制反轉(IOC)和依賴註入(DI)是Spring中最重要的核心概念之一,而兩者實際上是一體兩面的。

  • 依賴註入
    • 一個類依賴另一個類的功能,那麼就通過註入,如構造器、setter方法等,將這個類的實例引入。
    • 側重於實現。
  • 控制反轉
    • 創建實例的控制權由一個實例的代碼剝離到IOC容器控制,如xml配置中。
    • 側重於原理。
    • 反轉了什麼:原先是由類本身去創建另一個類,控制反轉後變成了被動等待這個類的註入。

後記

網路上很多文章中關於SOLID的介紹,語句都不通順,徒增理解難度。如果對基本釋義仍不能領會,可以參考 英文WIKI


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

-Advertisement-
Play Games
更多相關文章
  • JAVA WEB 亂碼問題解析 亂碼原因 在Java Web開發過程中,經常遇到亂碼的問題,造成亂碼的原因,概括起來就是對字元編碼和解碼的方式不匹配。 既然亂碼的原因是字元編碼與解碼的方式不匹配,那麼為什麼我們一定要對字元進行編碼,不編碼可不可以呢?這是因為在電腦中存儲數據的基本單位是1個位元組,即 ...
  • 介面 (1)當抽象類中的方法都是抽象的時候,java就提供了一種新的表現形式:介面 ,介面是功能的集合 介面不能創建對象 (2)格式 父介面:public interface Tnter{ } 子類:public class interImp implements Itner{ } 介面的使用 1. ...
  • 1. os.path.driname(path):返迴路徑的上一級路徑字元串。 >>> os.path.dirname('D:\Games') 'D:\\' >>> 2. os.path.basename(path):返迴路徑的最後一級目錄名(文件夾名)或文件名(全稱)。 >>> os.path.b ...
  • 早在去年八月份PowerShell就開始開源跨平臺了,但是一直沒有去嘗試,叫做PowerShell Core。 這裡打算簡單介紹一下如何安裝和簡單使用,為還不知道PowerShell Core on Ubuntu的同學們提供一點小小的入門幫助,謝謝大家支持~ PowerShell Core是由Mic ...
  • 匿名對象:就是沒有名字的對象 new student(); 匿名對象的應用場景: 1.僅僅調用一次對象中的方法 註意:調用多次的時候,不合適 匿名對象調用方法有什麼方法? 匿名對象調用完畢就是垃圾,可以被垃圾回收期回收。 2.匿名對象可以作為實際參數傳遞。 ...
  • 開發人員使用Jersey和CXF等工具可以方便地編寫java client,從而提供REST或SOAP服務;開發人員也可以基於Apache HC等http傳輸工具包編寫自己的java http client;而Feign的關註點在於簡化開發人員使用工具包的複雜度,以最少的代碼編寫代碼從而提供java... ...
  • 這一章中作者簡要的介紹了python數據模型,主要是python的一些特殊方法。比如__len__, __getitem__. 並用一個紙牌的程式來講解了這些方法 首先介紹下Tuple和nametuple的區別: Nametuple是類似於元組的數據類型。除了能夠用索引來訪問數據,還支持用方便的屬性 ...
  • 成員變數和局部變數的區別: 1.在類中的位置不同 成員變數:在類中,方法外 局部變數:在方法定義中或者方法聲明上 2.在記憶體中的位置不同: 成員變數:在堆記憶體中 局部變數:在棧記憶體中 3.生命周期不同: 成員變數:隨著對象的創建而存在,隨著對象的消失而消失 局部變數:隨著方法的調用而存在,隨著方法的 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...