天天看點

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

前言:很久之前就想動筆總結下關于軟體設計的一些原則,或者說是設計模式的一些原則,奈何被各種bootstrap元件所吸引,一直抽不開身。群裡面有朋友問部落客是否改行做前端了,呵呵,其實部落客是想做“全戰”,即各友善都有戰鬥力。關于設計模式,作為程式猿的我們肯定都不陌生。部落客的了解,所謂設計模式就是前人總結下來的一些對于某些特定使用場景非常适用的優秀的設計思路,“前人栽樹,後人乘涼”,作為後來者的我們就有福了,當我們遇到類似的應用場景的時候就可以直接使用了。關于設計模式的原則,部落客将會在接下來的幾篇裡面根據自己的了解一一介紹,此篇就先來看看設計模式的設計原則之——依賴倒置原則。

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

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

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

C#軟體設計——小話設計模式原則之:接口隔離原則ISP

C#軟體設計——小話設計模式原則之:開閉原則OCP

依賴倒置原則,英文縮寫DIP,全稱Dependence Inversion Principle。

原始定義:High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions。

官方翻譯:高層子產品不應該依賴低層子產品,兩者都應該依賴其抽象;抽象不應該依賴細節,細節應該依賴抽象。

上面的定義不難了解,主要包含兩次意思:

1)高層子產品不應該直接依賴于底層子產品的具體實作,而應該依賴于底層的抽象。換言之,子產品間的依賴是通過抽象發生,實作類之間不發生直接的依賴關系,其依賴關系是通過接口或抽象類産生的。

2)接口和抽象類不應該依賴于實作類,而實作類依賴接口或抽象類。這一點其實不用多說,很好了解,“面向接口程式設計”思想正是這點的最好展現。

相比傳統的軟體設計架構,比如我們常說的經典的三層架構,UI層依賴于BLL層,BLL層依賴于DAL層。由于每一層都是依賴于下層的實作,這樣當某一層的結構發生變化時,它的上層就不得不也要發生改變,比如我們DAL裡面邏輯發生了變化,可能會導緻BLL和UI層都随之發生變化,這種架構是非常荒謬的!好,這個時候如果我們換一種設計思路,高層子產品不直接依賴低層的實作,而是依賴于低層子產品的抽象,具體表現為我們增加一個IBLL層,裡面定義業務邏輯的接口,UI層依賴于IBLL層,BLL層實作IBLL裡面的接口,是以具體的業務邏輯則定義在BLL裡面,這個時候如果我們BLL裡面的邏輯發生變化,隻要接口的行為不變,上層UI裡面就不用發生任何變化。

在經典的三層裡面,高層子產品直接依賴低層子產品的實作,當我們将高層子產品依賴于底層子產品的抽象時,就好像依賴“倒置”了。這就是依賴倒置的由來。通過依賴倒置,可以使得架構更加穩定、更加靈活、更好應對需求變化。

上面說了,在三層架構裡面增加一個接口層能實作依賴倒置,它的目的就是降低層與層之間的耦合,使得設計更加靈活。從這點上來說,依賴倒置原則也是“松耦合”設計的很好展現。

 文章最開始的時候說了,依賴倒置是設計模式的設計原則之一,那麼在我們那麼多的設計模式中,哪些設計模式遵循了依賴倒置的原則呢?這個就多了,比如我們常見的工廠方法模式。下面部落客就結合一個使用場景來說說依賴倒置原則如何能夠使得設計更加靈活。

場景描述:還記得在C#基礎系列——一場風花雪月的邂逅:接口和抽象類這篇裡面介紹過裝置的采集的例子,這篇繼續以這個使用場景來說明。裝置有很多類型,每種裝置都有登入和采集兩個方法,通過DeviceService這個服務去啟動裝置的采集,最開始我們隻有MML和TL2這兩種類型的裝置,那麼來看看我們的設計代碼。

代碼示例:

在Main函數裡面調用

上述代碼經過開發、調試、部署、上線。可以正常運作,貌似一切都OK。

日複一日、年複一年。後來公司又來兩種新的裝置TELNET和TL5類型裝置。于是程式猿們又有得忙了,加班,趕進度!于是代碼變成了這樣:

比如我們想啟動TL5類型裝置的采集,這樣調用可以實作:

花了九年二虎之力,總算是可以實作了。可是又過了段時間,又有新的裝置類型呢?是不是又要加班,又要改。這樣下去,感覺這就是一個無底洞,再加上時間越久,項目所經曆的開發人員越容易發生變化,這個時候再改,那維護的成本堪比開發一個新的項目。并且,随着裝置類型的增多,代碼裡面充斥着大量的if...else,這樣的爛代碼簡直讓人無法直視。

基于這種情況,如果我們當初設計這個系統的時候考慮了依賴倒置,那麼效果可能截然不同。我們來看看依賴倒置如何解決以上問題的呢?

調用

代碼說明:上述解決方案中,我們定義了一個IDevice接口,用于上層服務的依賴,也就是說,上層服務(這裡指DeviceService)僅僅依賴IDevice接口,對于具體的實作類我們是不管的,隻要接口的行為不發生變化,增加新的裝置類型後,上層服務不用做任何的修改。這樣設計降低了層與層之間的耦合,能很好地适應需求的變化,大大提高了代碼的可維護性。呵呵,看着是不是有點眼熟?是不是有點像某個設計模式?其實設計模式的設計原理正是基于此。

上面說了那麼多,都是在講依賴倒置的好處,那麼在我們的項目中究竟如何具體實作和使用呢?

在介紹依賴倒置具體如何使用之前,我們需要引入IOC容器相關的概念,我們先來看看它們之間的關系。

依賴倒置原則(DIP):一種軟體架構設計的原則(抽象概念)。

控制反轉(IoC):一種反轉流、依賴和接口的方式(DIP的具體實作方式)。這是一個有點不太好了解和解釋的概念,通俗地說,就是應用程式本身不負責依賴對象的建立和維護,而是将它交給一個外部容器(比如Unity)來負責,這樣控制權就由應用程式轉移到了外部IoC 容器,即控制權實作了所謂的反轉。例如在類型A中需要使用類型B的執行個體,而B 執行個體的建立并不由A 來負責,而是通過外部容器來建立。

依賴注入(DI):IoC的一種實作方式,用來反轉依賴(IoC的具體實作方式)。園子裡面很多博文裡面說IOC也叫DI,其實根據部落客的了解,DI應該是IOC的具體實作方式,比如我們如何實作控制反轉,答案就是通過依賴注入去實作。

IoC容器:依賴注入的架構,用來映射依賴,管理對象建立和生存周期(DI架構),自動建立、維護依賴對象。

這些名詞是不是有點熟呢?部落客之前介紹過MEF,之前使用MEF做過依賴注入,詳見C#進階系列——MEF實作設計上的“松耦合”(一)。其實嚴格來講,MEF不能算一種正式的IOC容器,因為它的主要作用還是用于應用程式擴充,避免生成脆弱的硬依賴項,而不是依賴注入。根據部落客的了解以及使用經曆,常用的IOC容器有:

Spring.NET: http://www.springframework.net/

Unity:  http://unity.codeplex.com/

Autofac:  http://code.google.com/p/autofac/

Ninject:  http://www.ninject.org/

當然,還有其他的IOC容器這裡就不一一列舉。Spring.net是從Java的Spring架構移植過來的,功能之強大我們就不多說了,可是自從它宣布不再更新,部落客在使用它的時候就非常慎重了。下面部落客還是就Unity這種IOC容器來看看依賴倒置的具體實作。

Unity如何引入?我們神奇的Nuget又派上用場了。最新的Unity版本已經到了4.0.1。

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

安裝成功後主要引入了三個dll。

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

仍然以上面的場景為例說明,我們注入DeviceMML這個實作類。

運作結果

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

上面預設注入的方式中,我們隻能注入一種具體的執行個體,如果我們需要同時注入多個類型的執行個體呢?看看我們的 RegisterType() 方法有多個重載。

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

在App.config或者Web.config裡面加入如下配置:

在代碼裡面注冊配置檔案:

運作結果:

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

代碼說明

(1)

節點裡面,type對象抽象,mapTo對象具體執行個體對象,name對象執行個體的别名。

(2)在app.config裡面可以配置多個 <container name="Spider"> 節點,不同的name配置不同的依賴對象。

(3)配置檔案注入的靈活之處在于解耦。為什麼這麼說呢?試想,如果我們的IDevice接口對應着一個接口層,而DeviceMML、DeviceTELNET、DeviceTL2、DeviceTL5等實作類在另外一個實作層裡面,我們的UI層(這裡對應控制台程式這一層)隻需要添加IDevice接口層的引用,不必添加實作層的引用,通過配置檔案注入,在運作的時候動态将實作類注入到UI層裡面來。這樣UI層就對實作層實作了解耦,實作層裡面的具體邏輯變化時,UI層裡面不必做任何更改。

 到此,依賴倒置原則的講解基本結束了。根據部落客的了解,設計模式的這些原則是設計模式的理論指導,而設計模式則是這些理論的具體運用。說一千道一萬,要想搞懂設計模式,必須先了解設計模式遵循的原則,無論是哪種設計模式都會遵循一種或者多種原則。當然文章可能有了解不當的地方,歡迎大牛們指出。部落客将會在接下來的幾章繼續總結下其他設計原則,如果園友們覺得本文對你有幫助,請幫忙推薦,部落客将繼續努力~~

繼續閱讀