解構控制反轉(IoC)和依賴注入(DI)

1.控制反轉

 

控制反轉(Inversion of Control,IoC),簡言之就是代碼的控制器交由系統控制,而不是在代碼內部,經過IoC,消除組件或者模塊間的直接依賴,使得軟件系統的開發更具柔性和擴展性。控制反轉的典型應用體如今框架系統的設計上,是框架系統的基本特徵,無論是.NET Framework抑或是Java Framework都是創建在控制反轉的思想基礎之上。html

 

控制反轉不少時候被看作是依賴倒置原則的一個同義詞,其概念產生的背景大概來源於框架系統的設計,例如.NET Framework就是一個龐大的框架(Framework)系統。在.NET Framework大平臺上能夠很容易地構建ASP.NET Web應用、Silverlight應用、Windows Phone應用或者Window Azure Cloud應用。不少時候,基於.NET Framework構建自定義系統的方式就是對.NET Framework自己的擴展,調用框架提供的基礎API,擴展自定義的系統功能和行爲。然而,無論如何新建或者擴展自定義功能,代碼執行的最終控制權仍是回到框架中執行,再交回應用程序。黃忠誠先生曾經在Object Builder Application Block一文中給出一個較爲貼切的舉例,就是在Window From應用程序中,當Application.Run調用以後,程序的控制權交由Windows Froms Framework上。因此,控制反轉更強調控制權的反轉,體現了控制流程的依賴倒置,因此從這個意義上來講,控制反轉是依賴倒置的特例。服務器

 

2.依賴注入

 

依賴注入(Dependency Injection,DI),早見於Martin Flower的Inversion of Control Containers and the Dependency Injection pattern一文,其定義可歸納爲:框架

 

客戶類依賴於服務類的抽象接口,並在運行時根據上下文環境,由其餘組件(例如DI容器)實例化具體的服務類實例,將其注入到客戶類的運行時環境,實現客戶類與服務類實例之間鬆散的耦合關係。ide

 

(1)常見的三種注入方式函數

 

簡單而言,依賴注入的方式被總結爲如下三種。ui

 

  • 接口注入(Interface Injection),將對象間的關係轉移到一個接口,以接口注入控制。

 

首先定義注入的接口:this

 

public interface IRunnerProviderspa

{.net

    void Run(Action action);線程

}

爲注入的接口實現不一樣環境下的注入提供器,本例的系統是一個後臺處理程序提供了運行環境的多種可能,默認狀況下將運行於單獨的線程,或者經過獨立的Windows Service進程運行,那麼須要爲不一樣的狀況實現不一樣的提供器,例如:

 

public class DefaultRunnerProvider : IRunnerProvider

{

    #region IRunnerProvider Members

 

    public void Run(Action action)

    {

        var thread = new Thread(() => action());

        thread.Start();

    }

 

    #endregion

}

對於後臺服務的Host類,經過配置獲取注入的接口實例,而Run方法的執行過程則被注入了接口所定義的邏輯,該邏輯由上下文配置所定義:

 

public class RunnerHost : IDisposable

{

    IRunnerProvider provider = null;

 

    public RunnerHost()

    {

        // Get Provider by configuration

        provider = GetProvider(config.Host.Provider.Name);

    }

 

    public void Run()

    {

        if (provider != null)

        {

            provider.Run(() => 

            {

               // exceute logic in this provider, if provider is DefualtRunnerProvider,

               // then this logic will run in a new thread context.

            });

        }

    }

}

 

接口注入,爲無須從新編譯便可修改注入邏輯提供了可能,GetProvider方法徹底能夠經過讀取配置文件的config.Host.Provider.Name內容,來動態地建立對應的Provider,從而動態地改變BackgroundHost的Run()行爲。

 

  • 構造器注入(Constructor Injection),客戶類在類型構造時,將服務類實例以構造函數參數的形式傳遞給客戶端,所以服務類實例一旦注入將不可修改。

 

public class PicWorker

{

}

 

public class PicClient

{

    private PicWorker worker;

 

    public PicClient(PicWorker worker)

    {

        // 經過構造器注入

        this.worker = worker;

    }

}

  • 屬性注入(Setter Injection),經過客戶類屬性設置的方式,將服務器類實例在運行時設定爲客戶類屬性,相較構造器注入方式,屬性注入提供了改寫服務器類實例的可能。

 

public class PicClient

{

    private PicWorker worker;

 

   // 經過屬性注入

    public PicWorker Woker

    {

        get { return this.worker; }

        set { this.worker = value; }

    }

}

另外,在.NET平臺下,除了Martin Flower大師提出的三種注入方式以外,還有一種更優雅的選擇,那就是依靠.NET特有的Attribute實現,以ASP .NET MVC中的Action Filter爲例:

 

[HttpPost]

public ActionResult Register(RegisterModel model)

{

    // 省略註冊過程

    return View(model);

}

其中,HttpPostAttribute就是經過Attribute方式爲Register Action注入了自動檢查Post請求的邏輯,一樣的注入方式普遍存在於ASP .NET MVC的不少Filter邏輯中。

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]

public sealed class HttpPostAttribute : ActionMethodSelectorAttribute

{

    // Fields

    private static readonly AcceptVerbsAttribute _innerAttribute = new AcceptVerbsAttribute(HttpVerbs.Post);

 

    // Methods

    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)

    {

        return _innerAttribute.IsValidForRequest(controllerContext, methodInfo);

    }

}

 

關於Attribute的詳細內容,請參考8.3節「歷史糾葛:特性和屬性」,其中的TrimAttribute特性正是應用Attribute注入進行屬性Trim過濾處理的典型應用。

 

轉自:http://www.cnblogs.com/jyshis/archive/2011/09/15/2177279.html

相關文章
相關標籤/搜索