1. 程式人生 > >依賴倒置,控制翻轉,依賴注入

依賴倒置,控制翻轉,依賴注入

  前面描述的是應用程式和類庫之間的依賴關係。如果我們開發的不是類庫,而是框架系統,依賴關係就會更強烈一點。那麼,該如何消解框架和應用程式之間的依賴關係呢?
  《道法自然》第5章描述了框架和類庫之間的區別:
  “框架和類庫最重要的區別是:框架是一個‘半成品’的應用程式,而類庫只包含一系列可被應用程式呼叫的類。
  “類庫給使用者提供了一系列可複用的類,這些類的設計都符合面向物件原則和模式。使用者使用時,可以建立這些類的例項,或從這些類中繼承出新的派生類,然後呼叫類中相應的功能。在這一過程中,類庫總是被動地響應使用者的呼叫請求。
  “框架則會為某一特定目的實現一個基本的、可執行的架構。框架中已經包含了應用程式從啟動到執行的主要流程,流程中那些無法預先確定的步驟留給使用者來實現。程式執行時,框架系統自動呼叫使用者實現的功能元件。這時,框架系統的行為是主動的。
  “我們可以說,類庫是死的,而框架是活的。應用程式通過呼叫類庫來完成特定的功能,而框架則通過呼叫應用程式來實現整個操作流程。框架是控制倒置原則的完美體現。”
  框架系統的一個最好的例子就是圖形使用者介面(GUI)系統。一個簡單的,使用面向過程的設計方法開發的GUI系統如圖 5所示。 

 從圖 5中可以看出,應用程式呼叫GUI框架中的CreateWindow()函式來建立視窗,在這裡,我們可以說應用程式依賴於GUI框架。但GUI框架並不瞭解該視窗接收到視窗訊息後應該如何處理,這一點只有應用程式最為清楚。因此,當GUI框架需要傳送視窗訊息時,又必須呼叫應用程式定義的某個特定的視窗函式(如上圖中的MyWindowProc)。這時,GUI框架又必須依賴於應用程式。這是一個典型的雙向依賴關係。這種雙向依賴關係有一個非常嚴重的缺陷:由於GUI框架呼叫了應用程式中的某個特定函式(MyWindowProc), GUI框架根本無法獨立存在;換一個新的應用程式,GUI框架多半就要做相應的修改。因此,如何消解框架系統對應用程式的依賴關係是實現框架系統的關鍵。
  並非只有面向物件的方法才能解決這一問題。WIN32 API早就為我們提供了在面向過程的設計思路下解決類似問題的範例。類WIN32 的架構模型如圖 6所示。 
在圖 6中,應用程式呼叫CreateWindow()函式時,要傳遞一個訊息處理函式的指標給GUI框架(對WIN32而言,我們在註冊視窗類時傳遞這一指標),GUI框架把該指標記錄在視窗資訊結構中。需要傳送視窗訊息時,GUI框架就通過該指標呼叫視窗函式。和圖 5 相比,GUI框架仍然需要呼叫應用程式,但這一呼叫從一個硬編碼的函式呼叫變成了一個由應用程式事先註冊被呼叫物件的動態呼叫。圖 6用一條虛線表示這種動態呼叫。可以看出,這種動態的呼叫關係有一個非常大的好處:當應用程式發生變化時,它可以自行改變框架系統的呼叫目標,GUI框架無需隨之發生變化。現在,我們可以說,雖然還存在著從GUI框架到應用程式的呼叫關係,但GUI框架已經完全不再依賴於應用程式了。這種動態呼叫機制通常也被稱為“回撥函式”。
  在面向物件領域,“回撥函式”的替代物就是“模板方法模式”,也就是“好萊塢原則(不要呼叫我們,讓我們呼叫你)”。GUI框架的一個面向物件的實現如圖 7所示。 
 圖 7中,“GUI框架抽象介面”是GUI框架系統提供給應用程式使用的介面。抽象出該介面的動機是根據“依賴倒置”的原則,消解從應用程式到GUI框架之間的直接依賴關係,以使得GUI框架實現的變化對應用程式的影響最小化。Window介面類則是“模板方法模式”的核心。應用程式呼叫CreateWindow()函式時,GUI框架會把該視窗的引用儲存在視窗連結串列中。需要傳送視窗訊息時,GUI框架就呼叫視窗物件的SendMessage()函式,該函式是實現在Window類中的非虛成員函式。SendMessage()函式又呼叫WindowProc()虛擬函式,這裡實際執行的是應用程式MyWindow類中實現的WindowProc()函式。在圖 7中,我們已經看不到從GUI框架到應用程式之間的直接依賴關係了。因此,模板方法模式完全實現了回撥函式的動態呼叫機制,消解了從框架到應用程式之間的依賴關係。
  從上面的分析可以看出,模板方法模式是框架系統的基礎,任何框架系統都離不開模板方法模式。Martin Fowler也說 [Folwer 2004],“幾位輕量級容器的作者曾驕傲地對我說:這些容器非常有用,因為它們實現了‘控制反轉’。這樣的說辭讓我深感迷惑:控制反轉是框架所共有的特徵,如果僅僅因為使用了控制反轉就認為這些輕量級容器與眾不同,就好像在說‘我的轎車是與眾不同的,因為它有四個輪子’。問題的關鍵在於:它們反轉了哪方面的控制?我第一次接觸到的控制反轉針對的是使用者介面的主控權。早期的使用者介面是完全由應用程式來控制的,你預先設計一系列命令,例如‘輸入姓名’、‘輸入地址’等,應用程式逐條輸出提示資訊,並取回使用者的響應。而在圖形使用者介面環境下,UI 框架將負責執行一個主迴圈,你的應用程式只需為螢幕的各個區域提供事件處理函式即可。在這裡,程式的主控權發生了反轉:從應用程式移到了框架。”
  確實:對比圖 3和圖 7可以看出,使用普通類庫時,程式的主迴圈位於應用程式中,而使用框架系統的應用程式不再包括一個主迴圈,只是實現某些框架定義的介面,框架系統負責實現系統執行的主迴圈,並在必要的時候通過模板方法模式呼叫應用程式。
  也就是說,雖然“依賴倒置”和“控制反轉”在設計層面上都是消解模組耦合的有效方法,也都是試圖令具體的、易變的模組依賴於抽象的、穩定的模組的基本原則,但二者在使用語境和關注點上存在差異:“依賴倒置”強調的是對於傳統的、源於面向過程設計思想的層次概念的“倒置”,而“控制反轉”強調的是對程式流程控制權的反轉;“依賴倒置”的使用範圍更為寬泛,既可用於對程式流程的描述(如流程的主從和層次關係),也可用於描述其他擁有概念層次的設計模型(如服務元件與客戶元件、核心模組與外圍應用等),而“控制反轉”則僅適用於描述流程控制權的場合(如演算法流程或業務流程的控制權)。
  從某種意義上說,我們也可以把“控制反轉”看作是“依賴倒置”的一個特例。例如,用模板方法模式實現的“控制反轉”機制其實就是在框架系統和應用程式之間抽象出了一個描述所有演算法步驟原型的介面類,框架系統依賴於該介面類定義並實現程式流程,應用程式依賴於該介面類提供具體演算法步驟的實現,應用程式對框架系統的依賴被“倒置”為二者對抽象介面的依賴。
  總地說來,應用程式和框架系統之間的依賴關係有以下特點:
  1. 應用程式和框架系統之間實際上是雙向呼叫,雙向依賴的關係。
  2. 依賴倒置原則可以減弱應用程式到框架之間的依賴關係。
  3. “控制反轉”及具體的模板方法模式可以消解框架到應用程式之間的依賴關係,這也是所有框架系統的基礎。
  4. 框架系統可以獨立重用。

依賴注入(Dependency Injection) 

  在前面的例子裡,我們通過“依賴倒置”原則,最大限度地減弱了應用程式Copy類和類庫提供的服務Read,Write之間的依賴關係。但是,如果需要把Copy()函式也實現在類庫中,又會發生什麼情況呢?假設在類庫中實現一個“服務類”,“服務類”提供Copy()方法供應用程式使用。應用程式使用時,首先建立“服務類”的例項,呼叫其中的Copy()函式。“服務類”的例項初始化時會建立KeyboardReader 和PrinterWriter類的例項物件。如圖 8所示。 

從圖 8中可以看出,雖然Reader和Writer介面隔離了“服務類”和具體的Reader和Writer類,使它們之間的耦合降到了最小。但當 “服務類”建立具體的Reader和Writer物件時,“服務類”還是和具體的Reader和Writer物件發生了依賴關係——圖 8中用藍色的虛線描述了這種依賴關係。
  在這種情況下,如何例項化具體的Reader和Writer類,同時又儘量減少服務類對它們的依賴,就是一個非常關鍵的問題了。如果服務類位於應用程式中,這一依賴關係對我們造成的影響還不算大。但當“服務類”位於需要獨立釋出的類庫中,它的程式碼就不能隨著應用程式的變化而改變了。這也意味著,如果“服務類”過度依賴於具體的Reader和Writer類,使用者就無法自行新增新的Reader和Writer 的實現了。
  解決這一問題的方法是“依賴注入”,即切斷“服務類”到具體的Reader和Writer類之間的依賴關係,而由應用程式來注入這一依賴關係。如圖 9所示。 
在圖 9中,“服務類”並不負責建立具體的Reader和Writer類的例項物件,而是由應用程式來建立。應用程式建立“服務類”的例項物件時,把具體的Reader和Write物件的引用注入“服務類”內部。這樣,“服務類”中的程式碼就只和抽象介面相關的了。具體實現程式碼發生變化時,“服務類”不會發生任何變化。新增新的實現時,也只需要改變應用程式的程式碼,就可以定義並使用新的Reader和Writer類,這種依賴注入方式通常也被稱為“構造器注入”。
  如果專門為Copy類抽象出一個注入介面,應用程式通過介面注入依賴關係,這種注入方式通常被稱為“介面注入”。如果為Copy類提供一個設值函式,應用程式通過呼叫設值函式來注入依賴關係,這種依賴注入的方法被稱為“設值注入”。具體的“介面注入”和“設值注入”請參考[Martin 2004]。
  PicoContainer和Spring輕量級容器框架都提供了相應的機制來幫助使用者實現各種不同的“依賴注入”。並且,通過不同的方式,他們也都支援在XML檔案中定義依賴關係,然後由應用程式呼叫框架來注入依賴關係,當依賴關係需要發生變化時,只要修改相應的 XML檔案即可。
  因此,依賴注入的核心思想是:
  1. 抽象介面隔離了使用者和實現之間的依賴關係,但建立具體實現類的例項物件仍會造成對於具體實現的依賴。
  2. 採用依賴注入可以消除這種建立依賴性。使用依賴注入後,某些類完全是基於抽象介面編寫而成的,這可以最大限度地適應需求的變化。

結論 

  分離介面和實現是人們有效地控制依賴關係的最初嘗試,而純粹的抽象介面更好地隔離了相互依賴的兩個模組,“依賴倒置”和 “控制反轉”原則從不同的角度描述了利用抽象介面消解耦合的動機,GoF的設計模式正是這一動機的完美體現。具體類的建立過程是另一種常見的依賴關係,“依賴注入”模式可以把具體類的建立過程集中到合適的位置,這一動機和GoF的建立型模式有相似之處。
  這些原則對我們的實踐有很好的指導作用,但它們不是聖經,在不同的場合可能會有不同的變化,我們應該在開發過程中根據需求變化的可能性靈活運用。

相關推薦

依賴倒置控制翻轉依賴注入

  前面描述的是應用程式和類庫之間的依賴關係。如果我們開發的不是類庫,而是框架系統,依賴關係就會更強烈一點。那麼,該如何消解框架和應用程式之間的依賴關係呢?   《道法自然》第5章描述了框架和類庫之間的區別:   “框架和類庫最重要的區別是:框架是一個‘半成品’的應用程式,而類庫只包含一系列可被應用程式呼叫

我曾想深入瞭解的:依賴倒置控制反轉、依賴注入

大道至簡 我們在軟體工程中進行的架構設計、模組實現、編碼等工作,很多時候說到底就是圍繞一件事進行:解耦。 三層架構,MVC,微服務,DDD.我們分析問題,抽象問題,然後劃分邊界,劃分層次。 也是為了讓我們的類、模組、系統有更強的複用能力,提高生產效率。 這一次,我想深入瞭解和探討我曾經很迷糊,也沒有一直仔細瞭

PHP:依賴注入控制反轉依賴倒置原則

判斷程式碼的好壞,我們有自己的標準:高內聚,低耦合。為了解決這一問題,php中有許多優秀的設計模式,比如工廠模式,單例模式。而在程式碼中體現出來的設計模式,就如依賴注入和控制反轉。那什麼是依賴注入?簡單來說,就是把A類所依賴的B類C類等以屬性或者建構函式等方式注入A類而不是直

對spring IOC(控制翻轉) DI(依賴注入)AOP(面向切面)理解

 (1) 控制反轉(Inversion of Confrol):  物件的建立交給外部容器完成,這個就叫做控制反轉,  Ioc—Inversion of Control,即“控制反轉”,不是什麼技術,而是一種設計思想。在Java開發中,Ioc意味著將你設計好的物件交給容器控

.Net中的控制翻轉依賴注入

簡介其實依賴性注入(Dependency Injection)和控制反轉(Inversion of Control)二者是同一個概念。具體含義是:當某個角色(可能是一個c#例項,呼叫者)需要另一個角色(另一個c#例項,被呼叫者)的協助時,在傳統的程式設計過程中,通常由呼叫者

64管道符控制命令變量

名詞 bash命令 mgr rsyslog ftp hist 命令歷史 hostname rep 管道符 註:管道符 就是 把上一個命令 的結果 丟給下一個命令來執行顯示出來如下: 案例一 [root@localhost /]# cat 1.txt | wc -l1

大資料之scala(三) --- 類的檢查、轉換、繼承檔案特質trait操作符applyupdateunapply高階函式柯里化控制抽象集合

一、類的檢查和轉換 -------------------------------------------------------- 1.類的檢查 isInstanceOf -- 包括子類 if( p.isInstanceOf[Employee]) {

rac遷移ocr,votedisk,引數檔案資料檔案控制檔案密碼檔案redoundotempasm磁碟

概述:資料庫沒有開啟歸檔,當前環境12.1。votedg(normal)遷移到dg_vote(external),把votedg下的磁碟新增到dg_vote中,並幹掉votedg 因為只建立了votedg磁碟組,cdb,pdb也建立在上面,過程還是比較複雜,因為資料庫很多檔案都放在voted

常用的Emgu CV程式碼(主要有圖片格式轉換圖片裁剪圖片翻轉圖片旋轉和圖片平移等功能)

轉載自部落格 using System; using System.Drawing; using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Structure; namespace ZNLGIS { public class Im

嵌入式Linux併發程式設計程序間通訊方式System V IPC物件ftok()共享記憶體使用步驟建立shmget()對映shmat()撤銷對映shmdt()控制shmctl()注意

文章目錄 1,System V IPC 2,使用IPC物件的大致流程 3,生成KEY值ftok() ftok示例 4,共享記憶體 4.1,共享記憶體使用步驟 4.2,共享記憶體建立 shmget()

面向物件元類控制物件的建立

""" call 呼叫的意思 ​ 在在物件被呼叫時 執行 函式 類 自定義元類 的目的 ​ 1.可以通過call 來控制物件的建立過程 ​ 2.可用控制類的建立過程 """ 自定義一個元類 元類也是一個類 但是需要繼承type class MyMeta(type):​self 表示要建立物

Stemwin軟鍵盤小專案控制元件回撥函式

視窗回撥函式很重要 下面是我做的一個簡單的快遞收發系統框架,也可以算個計算器吧,有些功能不太全哦,再改進一下就可以了 下面是原始碼,圖片流太大了,沒有新增進來,自己可以用軟體做一個圖片 #include "GUI.h" static

OpenCV計算機視覺學習(11)——影象空間幾何變換(影象縮放影象旋轉影象翻轉影象平移仿射變換映象變換)

如果需要處理的原圖及程式碼,請移步小編的GitHub地址   傳送門:請點選我   如果點選有誤:https://github.com/LeBron-Jian/ComputerVisionPractice   影象的幾何變換是在不改變影象內容的前提下對影象畫素進行空間幾何變換,主要包括了影象的平移變換,縮放,

依賴注入控制反轉總結

DI(dependency injection)依賴注入模式;依賴注入是指將元件的依賴通過外部以引數或其他形式注入; 再看看 IOC(inversion of control)控制反轉模式;控制反轉是將元件間的依賴關係從程式內部提到外部來管理; 其實兩個說法本質上是一個意思。 不管是依賴

我的控制反轉依賴注入和麵向切面程式設計的理解

1.什麼是控制? 如下圖所示,我們看到了 軟體系統中 物件的 高耦合現象。全體齒輪的轉動由一個物件來控制,如類B。 2.什麼是 控制反轉? 是用來對物件進行解耦。藉助第三方實現具有依賴關係的

搞定.NET MVC IOC控制反轉依賴注入

一直聽說IOC,但是一直沒接觸過,只看例子好像很高達上的樣子,今天抽了點時間實現了下,當然也是藉助部落格園裡面很多前輩的文章來搞的!現在做個筆記,防止自己以後忘記! 1、首先建立MVC專案 2、然後新建一個介面IServiceA public interface IS

Spring中什麼是控制反轉依賴注入

Spring 核心思想之一IOC/DI:建立控制物件的主權反轉,獲取物件的方式反轉 IOC:負責建立物件及其生命週期控和關係控制。以單例模式來管理建立物件的生命週期,由原先的應用程式主動建立物件變為被動接手注入的物件,由IOC進行物件的例項化 、裝載、銷燬。 IOC控制物件

控制反轉依賴註入

per rec contex 單例 分銷 cfb 5% sse 合並 最近在學習Spring框架,它的核心就是IoC容器。要掌握Spring框架,就必須要理解控制反轉的思想以及依賴註入的實現方式。那麽出現了以下問題 什麽是控制反轉? 什麽是依賴註入? 它們之間有什麽關系?

依賴倒置原則(Dependence Inversion PrincipleDIP)

穩定性 提高 一個 pre inversion href 問題 簡單的 依賴關系 依賴倒置原則: A.高層次的模塊不應該依賴於低層次的模塊,他們都應該依賴於抽象。 B.抽象不應該依賴於具體,具體應該依賴於抽象。 定義:高層模塊不應該依賴低層模塊,二者都應該依賴其抽象

軟體架構與模式(依賴注入 控制反轉 依賴倒置原則 開閉原則 單一職責原則 介面隔離原則 里氏代換原則)

名詞解釋:   依賴:  一個獨立元素的變化會影響到相關的元素   派生:  一個類是由其他類衍生出的,子類繼承了基類的結構(屬性的名詞和型別)方法   抽象:  去掉每個不重要的細節,專