依賴注入和控制反轉

依賴注入和控制反轉到底是什麼意思?

控制反轉(Ioc):調用者不再創建被調用者的實例,由IOC容器框架創建(C#中常用的IOC框架有Autofac/Unity等等),這種方式稱爲控制反轉

依賴注入(DI):容器框架將創建好的實例注入到調用者稱爲依賴注入

依賴倒置原則(DIP)

高層模塊不應依賴於低層模塊,兩者應該依賴於抽象。

抽象不不應該依賴於實現,實現應該依賴於抽象。

使用Ioc的好處是什麼?

控制反轉是面向對象編程中的一種設計原則,可以用來降低代碼之間的耦合度,Ioc把創建和查找依賴對象的控制權交給容器,由容器進行注入,所以對象與對象之間是鬆散耦合,傳統的應用程序都是由我們在類內部主動創建依賴對象,從而導致類與類之間是緊耦合

舉例

class SqlDal
{
    public void Add()
    {
        Console.WriteLine("向SQLServer數據庫添加一條新訂單");
    }
}
class Order
{
    SqlDal sqlDal = new SqlDal();

    public void Add()
    {
        sqlDal.Add();
    }
}

首先定義了SqlDal類用於SqlServer數據庫的讀寫操作,再定義Order類負責訂單的邏輯處理,因爲訂單數據是要插入到數據庫的,所以在Order類中定了SqlDal類的變量並初始化.這裏Order是依賴於SqlDal類的

控制檯調用

static void Main(string[] args)
{
    Order order = new Order();
    order.Add();
    Console.ReadKey();
}

這是傳統開發中比較常見的方式,如果這時候數據庫改爲了mysql,那麼SqlDal是無法繼續使用了,需要重新定義mysqlDal,負責mysql數據庫的讀寫操作

class MySqlDal
{
    public void Add()
    {
        Console.WriteLine("向MySql數據庫添加一條新訂單");
    }
}

由於Order類中直接引用了SqlDal類的對象,所以也需要同步修改爲MySqlDal

class Order
{        
    MySqlDal mySqlDal = new MySqlDal();

    public void Add()
    {
        mySqlDal.Add();
    }
}

OK,可以滿足當前需求了,但是如果後期類似的情況再次出現,就意味着需要將剛纔的操作重新做一次,顯然這不是一個良好的設計,類與類之間是高度耦合的,可擴展性較差,它違背了DIP原則,高層模塊Order不應依賴於底層模塊SqlDal、MySqlDal,兩者應該依賴於抽象

改寫代碼

public interface IDal
{
    void Add();
}
class SqlDal : IDal
{
    public void Add()
    {
        Console.WriteLine("向SQLServer數據庫添加一條新訂單");
    }
}
class Order
{
    private IDal _dal;

    /// <summary>
    /// 構造函數注入
    /// </summary>
    /// <param name="dal"></param>
    public Order(IDal dal)
    {
        _dal = dal;
    }
    public void Add()
    {
        _dal.Add();
    }
}

第一步:定義接口IDal,添加方法Add()

第二步:定義SqlDal繼承IDal接口並實現接口中的Add()方法

第三步:在Order類中添加私有變量用於存儲抽象並在構造函數中傳遞具體依賴對象

控制檯調用

static void Main(string[] args)
{
    Order order = new Order(new SqlDal());
    order.Add();
    Console.ReadKey();
}
class MySqlDal : IDal
{
    public void Add()
    {
        Console.WriteLine("向MySql數據庫添加一條新訂單");
    }
}
Order order = new Order(new MySqlDal());
order.Add();

輸出結果是一樣的,但是這裏將依賴對象SqlDal對象的創建和綁定轉移到了Order外部來實現,這就解除了Order與SqlDal類的耦合關係,如果現在出現需要更改數據庫爲MySql的需求,只需要定義MySqlDal類繼承IDal並實現Add()方法,然後外部綁定依賴,不需要修改Order類內部的代碼即可實現替換,很明顯這種方式程序的可擴展性更高

屬性注入

上面所示的方式是在Order類的構造函數中注入依賴對象,還可以使用屬性注入的方式,顧名思義,屬性注入是通過屬性來傳遞依賴,因此在Order類中定義一個屬性

class Order
{
    private IDal _dal;

    /// <summary>
    /// 屬性注入
    /// </summary>
    public IDal Dal
    {
        get { return _dal; }
        set { _dal = value; }
    }

public void Add() { _dal.Add(); } }

在控制檯調用時給屬性賦值從而傳遞依賴

static void Main(string[] args)
{          
    Order order = new Order();
    order.Dal = new SqlDal();
    order.Add();            
    Console.ReadKey();
}

接口注入

相比構造函數注入和屬性注入,接口注入顯得有些複雜,使用也不常見。具體思路是先定義一個接口,包含一個設置依賴的方法。然後依賴類,繼承並實現這個接口

public interface IDependent
{
    void SetDepend(IDal idal);
}
class Order: IDependent
{
    private IDal _dal;

    public void Add()
    {
        _dal.Add();
    }

    public void SetDepend(IDal idal)
    {
        _dal = idal;
    }
}

 通過SetDepend方法傳遞依賴

 static void Main(string[] args)
 {
     Order order = new Order();
     order.SetDepend(new SqlDal());
     order.Add();
     Console.ReadKey();
 }

 Ioc容器

前面所有的例子中,我們都是通過手動的方式來創建依賴對象,並將引用傳遞給被依賴模塊,對於大型項目來說,相互依賴的組件比較多。如果還用手動的方式自己來創建和注入依賴的話,顯然效率很低,而且往往還會出現不可控的場面。正因如此,Ioc容器誕生了。Ioc容器實際上是一個DI框架,它能簡化我們的工作量。它包含以下幾個功能 

  • 動態創建、注入依賴對象
  • 管理對象生命週期
  • 映射依賴關係

總結:DIP是軟件設計的一種思想,Ioc則是基於DIP衍生出的一種軟件設計模式。DI是Ioc的具體實現方式之一,使用最爲廣泛。Ioc容器是DI構造函注入的框架,它管理着依賴項的生命週期以及映射關係

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章