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

broadviewbj發表於2011-08-10

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

1.控制反轉

控制反轉(Inversion of ControlIoC),簡言之就是程式碼的控制器交由系統控制,而不是在程式碼內部,透過IoC,消除元件或者模組間的直接依賴,使得軟體系統的開發更具柔性和擴充套件性。控制反轉的典型應用體現在框架系統的設計上,是框架系統的基本特徵,不管是.NET Framework抑或是Java Framework都是建立在控制反轉的思想基礎之上。

控制反轉很多時候被看做是依賴倒置原則的一個同義詞,其概念產生的背景大概來源於框架系統的設計,例如.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 InjectionDI),早見於Martin FlowerInversion of Control Containers and the Dependency Injection pattern一文,其定義可概括為:

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

1)常見的三種注入方式

簡單而言,依賴注入的方式被總結為以下三種。

·  介面注入(Interface Injection),將物件間的關係轉移到一個介面,以介面注入控制。

首先定義注入的介面:

public interface IRunnerProvider

{

    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,從而動態地改變BackgroundHostRun()行為。

·  構造器注入(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過濾處理的典型應用。

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

本文節選自《你必須知道的.NET(第2版)》一書

圖書詳細資訊:http://space.itpub.net/13164110/viewspace-704514

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/13164110/viewspace-704622/,如需轉載,請註明出處,否則將追究法律責任。

相關文章