面向對象設計五個基本原則之簡單理解

来源:http://www.cnblogs.com/zanpen2000/archive/2017/07/24/7228994.html
-Advertisement-
Play Games

引言 本文以簡單的示例輔助闡述面向對象設計的五個基本原則。 概念 在程式設計領域, SOLID(單一功能、開閉原則、里氏替換、介面隔離以及依賴反轉)是由羅伯特·C·馬丁在21世紀早期引入的記憶術首字母縮略字, 指代了面向對象編程和麵向對象設計的五個基本原則。當這些原則被一起應用時,它們使得一個程式員 ...


引言

本文以簡單的示例輔助闡述面向對象設計的五個基本原則。

概念

在程式設計領域, SOLID(單一功能、開閉原則、里氏替換、介面隔離以及依賴反轉)是由羅伯特·C·馬丁在21世紀早期引入的記憶術首字母縮略字,
指代了面向對象編程和麵向對象設計的五個基本原則。當這些原則被一起應用時,它們使得一個程式員開發一個容易進行軟體維護和擴展的系統變得
更加可能。SOLID所包含的原則是通過引發編程者進行軟體源代碼的代碼重構進行軟體的代碼異味清掃,從而使得軟體清晰可讀以及可擴展時可以應用
的指南。SOLID被典型的應用在測試驅動開發上,並且是敏捷開發以及自適應軟體開發的基本原則的重要組成部分。

首字母 指代 概念
S 單一功能原則 認為對象應該僅具有一種單一功能的概念
O 開閉原則 認為軟體體應該是對於擴展開放的,但是對於修改封閉的概念
L 里氏替換原則 認為“程式中的對象應該是可以在不改變程式正確性的前提下被它的子類所替換的”概念
I 介面隔離原則 認為“多個特定客戶端介面要好於一個寬泛用途的介面”
D 依賴反轉原則 認為一個方法應該遵從“依賴於抽象而不是一個實例”的概念
依賴註入所以該原則的一種實現方式

以上源自Wiki百科:SOLID(面向對象設計)

在我們的實際工作中,這五個原則互相關聯和支持,可能實現一個業務功能把五個原則都用到了,也可能只用到了其中的三個

單一功能原則(S)

按照字面意思不難理解,一個對象有且僅有一種功能,同時也僅有一種功能需求的變更能夠引發該對象實現的修改。

  • 試想以下場景,如果我們有一個對象,裡面大而全的實現了某業務領域內的全部功能,那麼,當該領域內的需求發生變更時,我們是不是要對該對象內的代碼進行修改?
  • 再試想一下,如果我們將該業務領域內的需求進行細分,針對細分後的功能實現對應的功能(業務)對象,那麼當該領域內的需求發生變更時,我們僅僅需要修改有變更需求對應的功能(業務)對象就可以?

可能有人會說,可是兩種方式都是修改代碼了呀?!是的,兩種方式都對我們的實現進行了代碼修改,這是不可避免的。
但換個角度想,此時我們修改的代碼,對其他需求沒有變更的功能(業務)對象來說,是不是已經沒有了太大的關聯?

因為實現了“單一功能原則(S)”,順帶也滿足了“介面隔離(I)”原則,當然嚴格來說是面向介面,但面向實現也未嘗不可。而此時,我們對特定業務/功能的修改不會影響到該領域內的其他實現,是不是也捎帶手實現了“開閉原則(O)”?

其實,在開發過程中,有一個百試不爽的竅門就是“拆”,大拆小,直到不能再拆(原子級),遇到複雜的業務如是,遇到複雜的功能同樣適用。不但有利於實現,同時也有利於測試。

延伸一下思路,無論是當下流行的微服務,還是一個方法只完成一個功能的普遍認知,是不是和單一功能原則有著相似的理念?

理論講完,來點具體的代碼實現驗證一下

    // C# 示例
    // 做飯介面
    public interface ICooking
    {
        //做飯介面方法
        bool Cook();
    }

    //做中餐
    public class ChineseCook: ICooking
    {
        public bool Cook()
        {
            Console.WriteLine("開始做魚香肉絲")
            Console.WriteLine("魚香肉絲做好了,快來米西吧")
        }
    }

    //做西餐
    public class WesternCook: ICooking
    {
        public bool Cook()
        {
            Console.WriteLine("開始做蔬菜沙拉")
            Console.WriteLine("蔬菜沙拉做好了, 快來eat吧")
        }
    }

例子寫的可能不太恰當,但足夠說明吃貨腦子裡不想別的。

如果哪天魚香肉絲吃膩味了換成宮保雞丁,是不是就只要修改ChineseCook的Cook實現就可以了?
又或者說如果哪天新增加了墨西哥菜,泰國菜,日本料理等,是不是只需要實現ICooking介面就可以了?

可能這個例子還不能對“拆”這個行為提供足夠的支持,因為好處不夠明顯,但別忘了五個原則是互相關聯和支持的,個原則是互相關聯和支持的,原則是互相關聯和支持的,是互相關聯和支持的,互相關聯和支持的,相關聯和支持的,關聯和支持的,聯和支持的,相和支持的,支持的,持的,的...

上面的示例中,實現了單一功能原則(S)的同時,也實現了開閉原則(O),介面隔離原則(I)。

依賴反轉原則(D)

請看如下代碼示例:

    // 廚師 - 依賴反轉示例
    public class Cooker
    {
        //這裡只需要ICooking介面對象,不關心介面實現
        public void Cook(ICooking cook)
        {
            cook.Cook();
        }
    }

    // 食客 - 消費者
    public class Diner
    {
        public static void main(string[] args)
        {
            Cooker cooker = new Cooker();
            ICooking chineseCook = new ChineseCook(); //做中餐實例
            ICooking westernCook = new WesternCook(); //做西餐實例
            // 或許哪天增加日本料理
            // ICooking japaneseCook = new JapaneseCook(); //日本料理實例

            cooker.Cook(chineseCook); // 做中餐
            cooker.Cook(westernCook); // 做西餐
            cooker.Cook(japaneseCook); // 日本料理
        }
    }

代碼說明:
以上示例,在ICooking介面的兩個實現的基礎上,實現了依賴反轉/倒置(DIP)

我們來看一下定義:

  • 高層次的模塊不應該依賴於低層次的模塊,兩者都應該依賴於抽象介面
  • 抽象介面不應該依賴於具體實現。而具體實現則應該依賴於抽象介面

在此示例中:

  • 高層次的模塊 Cooker 不依賴於 ChineseCook 或 WesternCook, 而是依賴於 ICooking 介面
  • ICooking介面不依賴於具體的實現。而 ChineseCook 和 WesternCook 則依賴於 ICooking 介面
  • 如果新增加了墨西哥菜,日本料理,只需要實現介面ICooking即可(開閉原則:面向修改關閉,面向擴展開放)

依賴倒置原則基於這樣一個事實:
相對於實現的多變性,抽象的東西(介面)要穩定的多。以抽象為基礎搭建起來的架構要比以實現為基礎搭建起來的架構要穩定的多。
在不同的語言領域中,抽象和實現可能有著不同的實現。不一而論。在C#或者Java中,抽象指的是介面或者抽象類,實現則是具體的實現類。
依賴倒置原則的核心就是要我們面向介面編程,理解了面向介面編程,也就理解了依賴倒置。

出自設計模式六大原則(3):依賴倒置原則

里氏替換原則(L)

定義:

派生類(子類)對象能夠替換其基類(超類)對象被使用。

出自里氏替換原則

這個概念比較好理解,結合上面的示例,如果我們把介面替換成抽象類,該抽象類實現了預設的Cook方法(預設做一份老北京雞肉捲):

// 父類,實現了一個預設的Cook方法
public class Cooking
{
  public virtual Cook()
  {
    Console.WriteLine("開始做老北京雞肉捲");
    Console.WriteLine("老北京雞肉捲做完了,快來啃吧!");
  }
}

//做中餐(重載Cook方法)
public class ChineseCook: Cooking
{
    public override bool Cook()
    {
        Console.WriteLine("開始做魚香肉絲")
        Console.WriteLine("魚香肉絲做好了,快來米西吧")
    }
}

//做西餐(重載Cook方法)
public class WesternCook: Cooking
{
    public override bool Cook()
    {
        Console.WriteLine("開始做蔬菜沙拉")
        Console.WriteLine("蔬菜沙拉做好了, 快來eat吧")
    }
}

代碼說明:

上例中,父類Cooking實現了Cook的預設方法(做一份老北京雞肉捲),所以如果不繼承該父類的話,服務員端上來的就是雞肉捲,很顯然,該類可以被子類替換。

本文完。


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

-Advertisement-
Play Games
更多相關文章
  • 1、函數嵌套 1.1函數的嵌套調用 在調用一個函數的過程中,又調用了其他函數 1.2函數的嵌套定義 在一個函數的內部,又定義另外一個函數 2、名稱空間 2.1名稱空間 名稱空間:存放名字的地方,準確的說名稱空間是存放名字與變數值綁定關係的地方 內置名稱空間:在python解釋器啟動時產生,存放一些p ...
  • 一 概述 1.整合目的 有了Spring以後,所有對象的創建任務都應該交給Spring容器來完成,這樣做不僅是為了降低代碼的耦合度,而且可以利用Spring容器作為代理工廠實現代理。 2.整合目標 將Spring容器中的bean註入Action中,將Action的創建與管理工作交給Spring容器。 ...
  • 一 概述 1.整合目的 將所有對象的創建與管理任務交給Spring容器,降低程式的耦合度。 2.整合途徑 將Spring容器註入到Web容器中。 3.具體實現 使用ServletContextListener監聽ServletContext,當ServletContexxt創建時同時創建Spring ...
  • 經典數據集CIFAR-10,60000張32x32彩色圖像,訓練集50000張,測試集10000張。標註10類,每類圖片6000張。airplance、automobile、bird、cat、deer、dog、frog、horse、ship、truck。沒有任何重疊。CIFAR-100,100類標註 ...
  • 載入MNIST數據集。創建預設Interactive Session。 初始化函數,權重製造隨機雜訊打破完全對稱。截斷正態分佈雜訊,標準差設0.1。ReLU,偏置加小正值(0.1),避免死亡節點(dead neurons)。 捲積層函數,tf.nn.conv2d,TensorFlow 2 維捲積函數 ...
  • 題目原文: An inversion in an array a[] is a pair of entries a[i] and a[j] such that i<j but a[i]>a[j]. Given an array, design a linearithmic algorithm to ...
  • 一 概述 1.整合目的 在Hibernate中,SessionFactory是一個重量級對象,創建與初始化會耗費大量的資源,應該減少對象的創建次數,並且SessionFactory線程安全,可以採用單例模式,如果將對象的創建任務交給Spring容器就解決了這個問題。 二 實現 1.配置Session ...
  • 第一步:註冊微信支付賬戶,開通掃碼支付 具體流程請參照官方說明 第二步:創建Maven項目 1. 添加微信支付SDK依賴、二維碼工具依賴(微信支付需要自己通過二維碼工具生成支付二維碼) <!-- 微信支付 --> <dependency> <groupId>com.github.wxpay</gro ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...