1. 程式人生 > >小話設計模式原則之:單一職責原則SRP(C#篇)

小話設計模式原則之:單一職責原則SRP(C#篇)

正文

前言:上篇C#軟體設計——小話設計模式原則之:依賴倒置原則DIP簡單介紹了下依賴倒置的由來以及使用,中間插了兩篇WebApi的文章,這篇還是迴歸正題,繼續來寫寫設計模式另一個重要的原則:單一職責原則。

軟體設計原則系列文章索引

回到頂部

一、原理介紹

回到頂部

1、官方定義

單一職責原則,英文縮寫SRP,全稱Single Responsibility Principle。

原始定義:There should never be more than one reason for a class to change。

官方翻譯:應該有且僅有一個原因引起類的變更。簡單點說,一個類,最好只負責一件事,只有一個引起它變化的原因。

回到頂部

2、自己理解

2.1、原理解釋

上面的定義不難理解,引起類變化的原因不能多於一個。也就是說每一個類只負責自己的事情,此所謂單一職責

我們知道,在OOP裡面,高內聚、低耦合是軟體設計追求的目標,而單一職責原則可以看做是高內聚、低耦合的引申,將職責定義為引起變化的原因,以提高內聚性,以此來減少引起變化的原因。職責過多,可能引起變化的原因就越多,這將是導致職責依賴,相互之間就產生影響,從而極大的損傷其內聚性和耦合度。單一職責通常意味著單一的功能,因此不要為類實現過多的功能點,以保證實體只有一個引起它變化的原因。

不管是從官方定義,還是對“單一職責”名稱的解釋,都能很好的理解單一職責原則的意義。其實在軟體設計中,要真正用好單一職責原則並不簡單,因為遵循這一原則最關鍵的地方在於職責的劃分,博主的理解是職責的劃分是根據需求定的,同一個類(介面)的設計,在不同的需求裡面,可能職責的劃分並不一樣,為什麼這麼說呢?我們來看下面的例子。

回到頂部

二、場景示例

關於單一職責原則的原理,我們就不做過多的解釋了。重點是職責的劃分!重點是職責的劃分!重點是職責的劃分!重要的事情說三遍。下面根據一個示例場景來看看如何劃分職責。

假定現在有如下場景:國際手機運營商那裡定義了生產手機必須要實現的介面,接口裡面定義了一些手機的屬性和行為,手機生產商如果要生成手機,必須要實現這些介面。

回到頂部

1、初始設計——初稿

我們首先以手機作為單一職責去設計介面,方案如下。

複製程式碼
   /// <summary>
    /// 充電電源
    /// </summary>
    public class ElectricSource
    {    }
複製程式碼 複製程式碼
  public interface IMobilePhone
    {
        //執行記憶體
        string RAM { get; set; }

        //手機儲存記憶體
        string ROM { get; set; }

        //CPU主頻
        string CPU { get; set; }

        //螢幕大小
        int Size { get; set; }

        //手機充電介面
        void Charging(ElectricSource oElectricsource);

        //打電話
        void RingUp();

        //接電話
        void ReceiveUp();

        //上網
        void SurfInternet();
    }
複製程式碼

然後我們的手機生產商去實現這些介面

複製程式碼
//具體的手機示例
    public class MobilePhone:IMobilePhone
    {
        public string RAM
        {
            get {throw new NotImplementedException();}
            set{ throw new NotImplementedException();}
        }

        public string ROM
        {
            get{throw new NotImplementedException();}
            set{ throw new NotImplementedException();}
        }

        public string CPU
        {
            get{ throw new NotImplementedException();}
            set{ throw new NotImplementedException();}
        }

        public int Size
        {
            get{throw new NotImplementedException();}
            set{throw new NotImplementedException();}
        }

        public void Charging(ElectricSource oElectricsource)
       {
            throw new NotImplementedException();
        }

        public void RingUp()
        {
            throw new NotImplementedException();
        }

        public void ReceiveUp()
        {
            throw new NotImplementedException();
        }

        public void SurfInternet()
        {
            throw new NotImplementedException();
        }
    }
複製程式碼

這種設計有沒有問題呢?這是一個很有爭議的話題。單一職責原則要求一個介面或類只有一個原因引起變化,也就是一個介面或類只有一個職責,它就負責一件事情,原則上來說,我們以手機作為單一職責去設計,也是有一定的道理的,因為我們接口裡面都是定義的手機相關屬性和行為,引起介面變化的原因只可能是手機的屬性或者行為發生變化,從這方面考慮,這種設計是有它的合理性的,如果你能保證需求不會變化或者變化的可能性比較小,那麼這種設計就是合理的。但實際情況我們知道,現代科技日新月異,科技的進步促使著人們不斷在手機原有基礎上增加新的屬性和功能。比如有一天,我們給手機增加了攝像頭,那麼需要新增一個畫素的屬性,我們的介面和實現就得改吧,又有一天,我們增加移動辦公的功能,那麼我們的介面實現是不是也得改。由於上面的設計沒有細化到一定的粒度,導致任何一個細小的改動都會引起從上到下的變化,有一種“牽一髮而動全身”的感覺。所以需要細化粒度,下面來看看我們如何變更設計。

回到頂部

2、二次設計——變更

 我們將介面細化

複製程式碼
  //手機屬性介面
    public interface IMobilePhoneProperty
    {
        //執行記憶體
        string RAM { get; set; }

        //手機儲存記憶體
        string ROM { get; set; }

        //CPU主頻
        string CPU { get; set; }

        //螢幕大小
        int Size { get; set; }

        //攝像頭畫素
        string Pixel { get; set; }
    }

    //手機功能介面
    public interface IMobilePhoneFunction
    {
        //手機充電介面
        void Charging(ElectricSource oElectricsource);

        //打電話
        void RingUp();

        //接電話
        void ReceiveUp();

        //上網
        void SurfInternet();

        //移動辦公
        void MobileOA();
    }
複製程式碼

實現類

複製程式碼
    //手機屬性實現類
    public class MobileProperty:IMobilePhoneProperty
    {

        public string RAM
        {
            get{ throw new NotImplementedException();}
            set{ throw new NotImplementedException();}
        }

        public string ROM
        {
            get{ throw new NotImplementedException();}
            set{ throw new NotImplementedException();}
        }

        public string CPU
        {
            get{ throw new NotImplementedException();}
            set{throw new NotImplementedException();}
        }

        public int Size
        {
            get{throw new NotImplementedException();}
            set{throw new NotImplementedException();}
        }

        public string Pixel
        {
            get{throw new NotImplementedException();}
            set{throw new NotImplementedException();}
        }
    }

    //手機功能實現類
    public class MobileFunction:IMobilePhoneFunction
    {

        public void Charging(ElectricSource oElectricsource)
        {
            throw new NotImplementedException();
        }

        public void RingUp()
        {
            throw new NotImplementedException();
        }

        public void ReceiveUp()
        {
            throw new NotImplementedException();
        }

        public void SurfInternet()
        {
            throw new NotImplementedException();
        }

        public void MobileOA()
        {
            throw new NotImplementedException();
        }
    }

    //具體的手機例項
    public class HuaweiMobile
    {
        private IMobilePhoneProperty m_Property;
        private IMobilePhoneFunction m_Func;
        public HuaweiMobile(IMobilePhoneProperty oProperty, IMobilePhoneFunction oFunc)
        {
            m_Property = oProperty;
            m_Func = oFunc;
        }
    }
複製程式碼

對於上面題的問題,這種設計能夠比較方便的解決,如果是增加屬性,只需要修改IMobilePhoneProperty和MobileProperty即可;如果是增加功能,只需要修改IMobilePhoneFunction和MobileFunction即可。貌似完勝第一種解決方案。那麼是否這種解決方案就完美了呢?答案還是看情況。原則上,我們將手機的屬性和功能分開了,使得職責更加明確,所有的屬性都由IMobilePhoneProperty介面負責,所有的功能都由IMobilePhoneFunction介面負責,如果是需求的粒度僅僅到了屬性和功能這一級,這種設計確實是比較好的。反之,如果粒度再細小一些呢,那我們這種職責劃分是否完美呢?比如我們普通的老人機只需要一些最基礎的功能,比如它只需要充電、打電話、接電話的功能,但是按照上面的設計,它也要實現IMobilePhoneFunction介面,某一天,我們增加了一個新的功能玩遊戲,那麼我們就需要在介面上面增加一個方法PlayGame()。可是我們老人機根本用不著實現這個功能,可是由於它實現了該介面,它的內部實現也得重新去寫。從這點來說,以上的設計還是存在它的問題。那麼,我們如何繼續細化介面粒度呢?

回到頂部

3、最終設計——成型

 介面細化粒度設計如下

複製程式碼
  //手機基礎屬性介面
    public interface IMobilePhoneBaseProperty
    {
        //執行記憶體
        string RAM { get; set; }

        //手機儲存記憶體
        string ROM { get; set; }

        //CPU主頻
        string CPU { get; set; }

        //螢幕大小
        int Size { get; set; }
    }

    //手機擴充套件屬性介面
    public interface IMobilePhoneExtentionProperty
    {
        //攝像頭畫素
        string Pixel { get; set; }
    }

    //手機基礎功能介面
    public interface IMobilePhoneBaseFunc
    {
        //手機充電介面
        void Charging(ElectricSource oElectricsource);

        //打電話
        void RingUp();

        //接電話
        void ReceiveUp();
    }

    //手機擴充套件功能介面
    public interface IMobilePhoneExtentionFunc
    {
        //上網
        void SurfInternet();

        //移動辦公
        void MobileOA();

        //玩遊戲
        void PlayGame();
    }
複製程式碼

實現類和上面類似

複製程式碼
//手機基礎屬性實現
    public class MobilePhoneBaseProperty : IMobilePhoneBaseProperty
    {

        public string RAM
        {
            get{throw new NotImplementedException();}
            set{throw new NotImplementedException();}
        }

        public string ROM
        {
            get{throw new NotImplementedException();}
            set {throw new NotImplementedException();}
        }

        public string CPU
        {
            get{throw new NotImplementedException();}
            set{ throw new NotImplementedException();}
        }

        public int Size
        {
            get{ throw new NotImplementedException();}
            set{ throw new NotImplementedException();}
        }
    }

    //手機擴充套件屬性實現
    public class MobilePhoneExtentionProperty : IMobilePhoneExtentionProperty
    {

        public string Pixel
        {
            get{ throw new NotImplementedException();}
            set{ throw new NotImplementedException();}
        }
    }

    //手機基礎功能實現
    public class MobilePhoneBaseFunc : IMobilePhoneBaseFunc
    {
        public void Charging(ElectricSource oElectricsource)
        {
            throw new NotImplementedException();
        }

        public void RingUp()
        {
            throw new NotImplementedException();
        }

        public void ReceiveUp()
        {
            throw new NotImplementedException();
        }
    }

    //手機擴充套件功能實現
    public class MobilePhoneExtentionFunc : IMobilePhoneExtentionFunc
    {

        public void SurfInternet()
        {
            throw new NotImplementedException();
        }

        public void MobileOA()
        {
            throw new NotImplementedException();
        }

        public void PlayGame()
        {
            throw new NotImplementedException();
        }
    }
複製程式碼

此種設計能解決上述問題,細分到此粒度,這種方案基本算比較完善了。能不能算完美?這個得另說。介面的粒度要設計到哪一步,取決於需求的變更程度,或者說取決於需求的複雜度。

回到頂部

三、總結

以上通過一個應用場景簡單介紹了下單一職責原則的使用,上面三種設計,沒有最合理,只有最合適。理解單一職責原則,最重要的就是理解職責的劃分,職責劃分的粒度取決於需求的粒度,最後又回到了那句話:沒有最好的設計,只有最適合的設計。歡迎園友拍磚斧正。如果園友們覺得本文對你有幫助,請幫忙推薦,博主將繼續努力~~


來自:http://www.cnblogs.com/landeanfen/p/5247366.html

相關推薦

C#軟體設計——設計模式原則單一職責原則SRP

前言:上篇C#軟體設計——小話設計模式原則之:依賴倒置原則DIP簡單介紹了下依賴倒置的由來以及使用,中間插了兩篇WebApi的文章,這篇還是迴歸正題,繼續來寫寫設計模式另一個重要的原則:單一職責原則。 軟體設計原則系列文章索引 一、原理介紹 1、官方定義 單一職責原則,英文縮寫SRP,全稱Sing

設計模式原則單一職責原則SRPC#

正文 前言:上篇C#軟體設計——小話設計模式原則之:依賴倒置原則DIP簡單介紹了下依賴倒置的由來以及使用,中間插了兩篇WebApi的文章,這篇還是迴歸正題,繼續來寫寫設計模式另一個重要的原則:單一職責原則。 軟體設計原則系列文章索引 回到頂部 一、原理介紹 回到頂部

設計模式學習筆記 設計基本原則單一職責原則

code 分享 開發者 實際應用 需要 ret ext file類 tor 單一職責原則(SRP: Single Responsibility Principle) 名詞解釋: 1) 職責:是指類變化的原因。 2) 職責擴散:就是因為某種原因,職責P被分化為粒度更細的職責P

設計模式六大原則(一)單一職責原則

控制 line 避免 多人 由來 pan 兩個類 思想 功能 單一職責定義: 不要存在多於一個導致類變更的原因,通俗的說,即一個類只負責一項職責。 問題由來: 類T負責兩個不同的職責:職責P1,職責P2。當由於職責P1需求發生改變而需要修改類T時,有可能會導致原

面向對象設計原則單一職責原則SRP

能夠 實現 update 之間 關註 linq 好處 相互 並且 單一職責原則(SRP) 定義:系統中的每一個類都應該只有一個職責。 好處:高內聚、低耦合。 解釋說明: 單一職責也就是說我們應該讓一個類或一個對象只做一件事情,每個類所要關註的就是自己要完成的

大話設計模式-第三章 單一職責原則

1.概念相關 <1>單一職責原則:就一個類而言,應該僅有一個引起它變化的原因; 2.OOP <1>如果一個類承擔的職責過多,就等於把這些職責耦合在一起,一個職責的變化可能會消弱或者抑制這個類完成其他職責的能力. 這種耦合會導致脆弱的設計,當變化發生時

六大原則之一單一職責原則

ret 延遲函數 data resolve image wfq 成功 appdata scn 解決回調地獄的方法: 1.保持代碼的簡潔性 2.模塊化 綜合上述,可使用六大原則之一單一職責原則 例: //延遲函數 六大原則 // var df =

C#軟體設計——設計模式原則開閉原則OCP

前言:這篇繼續來看看開閉原則。廢話少說,直接入正題。 軟體設計原則系列文章索引 一、原理介紹 1、官方定義 開閉原則,英文縮寫OCP,全稱Open Closed Principle。 原始定義:Software entities (classes, modules, functions) sho

C#軟體設計——設計模式原則介面隔離原則ISP

前言:有朋友問我,設計模式原則這些東西在園子裡都討論爛了,一搜一大把的資料,還花這麼大力氣去整這個幹嘛。博主不得不承認,園子裡確實很多這方面的文章,並且不乏出色的博文。博主的想法是,既然要完善知識體系,就不能半途而廢。今天就來看看設計模式原則的另一個:介面隔離原則。 軟體設計原則系列文章索引 一、原理

C#軟體設計——設計模式原則依賴倒置原則DIP

前言:很久之前就想動筆總結下關於軟體設計的一些原則,或者說是設計模式的一些原則,奈何被各種bootstrap元件所吸引,一直抽不開身。群裡面有朋友問博主是否改行做前端了,呵呵,其實博主是想做“全戰”,即各方便都有戰鬥力。關於設計模式,作為程式猿的我們肯定都不陌生。博主的理解,所謂設計模式就是前人總結下來的一些

設計模式學習設計模式原則單一職責原則和里氏替換原則

學習設計模式,以《設計模式之禪》為藍本進行總結與學習,今天先記錄設計模式六大原則的兩個原則:單一職責原則(SRP)和里氏替換原則(LSP)。 單一職責原則 Single Responsibilit

設計模式單一職責原則、開放-封閉原則以及依賴倒置原則

在設計程式碼中,我們有許多可以依照的設計模式,讓我把整個專案的邏輯結構變得清晰易於維護。當然,在設計模式中我們不只有各種模式,還有許多設計的原則,雖然他們不是程式碼架構的模板,但是這些原則卻時刻提醒我們提高程式碼質量和防止未來麻煩。這次我就將單一職責原則、開放-封閉原則以及依賴倒轉原則進行解釋。

設計模式六大原則1單一職責原則

定義:不要存在多於一個導致類變更的原因。通俗的說,即一個類只負責一項職責。 問題由來:類T負責兩個不同的職責:職責P1,職責P2。當由於職責P1需求發生改變而需要修改類T時,有可能會導致原本執行正常的職責P2功能發生故障。 解決方案:遵循單一職責原則。分別建立兩個類T1

設計模式單一職責原則

最近在看<<設計模式之禪>>感覺這本書很是不錯的,demo雖然簡單但是確實很明瞭,感覺很有必要自己再敲一遍 單一職責原則 demo: https://github.com/sanyinchen/UMLDemo 如果一個類有多於一個的動機被改變,那麼

設計模式原則1單一職責原則

定義:不要存在多於一個導致類變更的原因。通俗的說,即一個類只負責一項職責。 問題由來:類T負責兩個不同的職責:職責P1,職責P2。當由於職責P1需求發生改變而需要修改類T時,有可能會導致原本執行正常的職責P2功能發生故障。 解決方案:遵循單一職責原則。分別建立兩個類T1、T2,使T1完成職責P1功能,T2完成

【學習筆記】慕課網—Java設計模式精講 第3章 軟體設計七大原則-3-4 單一職責原則

/** * 軟體設計七大原則-單一職責原則 學習筆記 * @author cnRicky * @date 2018.11.10 */ 單一職責原則 定義:不要存在多於一個導致類變更的原因 一個類只負責一個職責,如果分別有兩個職責,那就建立兩個類分別負責職責1和職責2 一個類/介面/方法只負

設計模式學習裝飾器模式

  最近在總結學習Java I/O相關知識點,I/O應用的場景比較多,不僅存在各種I/O源端和想要與之通訊的接收端(檔案、控制檯、網路連結等),而且還需要支援多種不同方式的通訊(順序、隨機存取、緩衝、二進位制、按字元、按行、按字等)。   Java類庫的設計者通過建立大量的類來解決這個難題,這裡面用到了裝飾器

JavaScript設計模式基礎面向對象的JavaScript

UNC 靈活 我們 靜態 type n) object 模式 string 動態語言類型與鴨子類型 此內容取自JavaScript設計模式與開發實踐一書 編程語言按照數據類型大體可以分為2類,一類就是靜態類型語言,另一類則是動態類型語言 靜態類型語言也可以稱之為編譯語言,而

設計模式】中介者模式設計模式中的解耦神器!!附上demo

偶然間看到了一個之前完全沒有關注過的設計模式——中介者模式,在看過該設計模式的應用場景後,便有了相見恨晚的感覺啊!!! 這麼屌的設計模式應該應用很廣泛呀!!可怎麼之前都沒怎麼聽過?難道是我之前以為『中介者模式』==『代理模式』嗎????不過話說回來,只看名字的

六大設計原則之一_單一職責原則SRP

但是反過來,我們定義很多類或者介面,一個類裡面只有簡單的一兩個方法,這樣滿足單一職責原則了嗎?表面上看是滿足了SRP,但是其實這種設計也是不符合SRP的初衷的。因為有些可能出現的變化在對應實際情況看來,很可能兩年,三年內不會有變化,那麼這種變化單獨提出來是沒有多大意義的,反而耗費更多資源,也使得系統結構變得異