依賴註入(DI)和控制反轉(IoC)其實很好理解 —— 如果沒理解,興許是角度沒選對。 很多時候我們會將問題複雜化,而問題的答案卻一如既往的簡單。 ...
依賴註入
沒那麼複雜
很多時候我們會將問題複雜化,而問題的答案卻一如既往的簡單。
public class Stupid
{
public IService Service { get; private set; }
public Stupid()
{
Service = new ConcreteServiceA();
}
}
public class Smart
{
public IService Service { get; private set; }
public Smart(IService service)
{
Service = service;
}
}
上面是兩段看上去極其相似的類型代碼,區別在於 Service
屬性的初始化。前者在創建類型實例的時候直接創建Service的實例,而後者則不同:
// 已經有了一個 IService 的實例引用:concrteServiceA
var smart = new Smart(concreteServiceA);
這就是 依賴註入(Dependency Injection,DI)。
當然,這隻是依賴註入的其中一種形式,因為我們至少聽說過以下三種形式的依賴註入: 構造函數註入、 屬性註入、 方法註入。沒錯,按照前面的思路,屬性註入就是給屬性賦值,方法註入就是用指定的參數調用方法 —— 他們都是傳遞一些東西(一般是另一個對象的引用)給某個對象。
是什麼在誤導我們
站在過高的角度上去看問題,難免會看不透問題的本質。
我們接觸 依賴註入,往往起源於某個 依賴註入框架(NInject,Autofac,Castle,Unity......)。從那時起我們開始在 依賴註入框架 劃定的高度上入手 依賴註入。容器?註冊?解析?......依賴註入框架為我們提供了豐富強大的功能,同時也帶來了一大堆概念。然之後,我們還是沒搞清楚依賴註入是什麼,有什麼優勢,甚至開始懷疑它的可用性。
可是,整個圈子都在誇贊和使用的東西,怎麼可能沒有利用價值呢? —— 把 依賴註入框架 當成 依賴註入,誤導我們的,不是別人,是我們自己。
控制反轉
說到依賴註入,就不得不提 控制反轉(Inversion of Control,IoC):作為設計模式六大原則之一,它不是具體技術,而是一種設計思想。
前文提到的 Stupid
類型,看上去也無傷大雅,很是美觀易讀。可是,IService
介面的設計初衷是什麼呢? Service
屬性值的創建已經由 Stupid
類型的構造器完成了,IService
介面為我們帶來的 多態性 在這個類裡面儼然不復存在;屬性值將隨著 Stupid
類型實例的消逝而被回收,也不可能再被重覆利用。在設計上,我們將這種情況稱為 緊耦合 —— IService
介面成了擺設,ConcreteServiceA
與 Stupid
二者已是密不可分的存在。
顯然,這是不合理的,我們需要改變 —— 將 Service
屬性值的創建和生命周期管理交由 Stupid
類以外的地方去控制,就像 Smart
類型那樣。
對的,這就是 控制反轉。跟依賴註入一樣,沒那麼複雜。
控制反轉(IoC) VS 依賴註入(DI)
事實上這兩者並沒有什麼可比性。記住一句話即可:控制反轉 是一種設計思想,而 依賴註入 是 控制反轉 的實現方式之一。沒錯,之一,因為 IoC 的實現方式不只是 DI。