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


大道至簡

我們在軟件工程中進行的架構設計、模塊實現、編碼等工作,很多時候說到底就是圍繞一件事進行:解耦。

三層架構,MVC,微服務,DDD.我們分析問題,抽象問題,然后划分邊界,划分層次。

也是為了讓我們的類、模塊、系統有更強的復用能力,提高生產效率。

這一次,我想深入了解和探討我曾經很迷糊,也沒有一直仔細了解的:依賴倒置、控制反轉、依賴注入 這些概念。

什么是依賴?

通常可以理解為一種需要,需求。需要協助才能完成一件事情。

例如,我們依賴日志服務寫日志:

    public class Contract
    {
       public void Successed()
        {
            string msg = "save, successed!";
            Log log = new Log();
            log.Write(msg);
        }
    }

Contract類正依賴Log類,協助完成整個業務流程。這就產生了依賴。

什么是抽象? 什么是細節?

我們經常會聽說,面向接口編程,依賴於抽象不能依賴於具體實現細節。

我們每次修改接口時候,一定會去修改具體實現。但是我們修改具體實現卻很少修改接口。

所以接口比具體實現更穩定。

此時,我們在中間加入一層接口,看看如何。

    public interface ILog
    {
        void Write();
    }
     public void Successed()
     {
         string msg = "save, successed!";
         
         ILog log = new Log();
         log.Write(msg );
     }

關系變化如圖:

什么是上層模塊? 什么是底層模塊?

此時,Contract類可以看做上層。Log看做底層。

上層模塊:指揮控制

底層模塊:策略實現

依賴倒置

理清楚了 上層、底層、細節、抽象、依賴概念,

我們不難發現,上面的依賴箭頭發生了改變。

所以依賴倒置也由此而來:

上層模塊不應該依賴底層模塊,它們都應該依賴於抽象。
抽象不應該依賴於細節,細節應該依賴於抽象。

依賴倒置,使得我們的擴展性增強。

public class Log:ILog
public class NLog : ILog
public class Log4 : ILog
// ILog log = new Log();
// ILog log = new Log4();
ILog log = new NLog();
log.Write(msg);

以上代碼我們也可以看出,我們需要不斷注釋修改Contract類,以至於引用不同的Log組件來應對需求。

每次都要修改這個類來滿足需求(修改關閉,擴展開放原則),顯然是我們所不希望的。造成這種現象的原因是:

因為對於上層的Contract類,不僅僅負責業務邏輯的實現,第二職責還要負責日志實例的構造。

對於Program類,有日志服務類直接拿來使用即可,不需要關心這些實例的構造。

有沒有一種機制能夠將構造和使用進行分離?使得Contract的職責更加單一,耦合更低?(單一職責原則)

控制反轉

怎么算是控制反轉了呢?

我們改一下上面的代碼將日志類的實例化控制權,轉移到類的外部:

    public class Contract
    {
      public Contract(ILog log)
    }

調用


    class Program
    {
        static void Main(string[] args)
        {
            ILog log = new NLog();
            Contract contract = new Contract(log);
            contract.Successed();
        }
    }

這樣,無論外部日志組件如何變化,都不用會影響現有的Contract類。

Contract只專注於屬於自己的職責。上層Contract類和日志類解耦更加徹底。互不影響。

如果從職責角度來看,我們是不是可以有一個類專門來管理創建日志類呢?

就像倉庫管理員一樣,根據單子出貨,不需要關心這些貨物到底如何被使用的。

     public static class Ioc
    {
        public static ILog GetLogInstance(int type)
        {
            switch (type)
            {
                case 1: return new Log();
                case 2: return new Log4();
                case 3: return new NLog();
                default: return new Log();
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {           
            ILog log = Ioc.GetLogInstance(1);
            Contract contract = new Contract(log);
            contract.Successed();
        }
    }

依賴注入

什么是依賴注入呢?

其實我們剛剛已經實現過了,全文先是依賴倒置,然后控制反轉,而現在說的依賴注入是控制反轉的具體實現方式。

依賴注入是解開依賴並實現反轉的一種手段。

大約分為三種方式:

  • 構造函數方式
    public class Contract
    {
        private ILog _log { get; set; }
        public Contract(ILog log)
        {
            _log = log;
        }
    }

優點: 構造Contract就確定好依賴。
缺點:后期無法更改依賴。

  • Set方式注入
     public class Contract
    {
        private ILog _log { get; set; }
        public Contract(ILog log)
        {
            _log = log;
        }
        public void Successed()
        {
            string msg = "save, successed!";
            _log.Write(msg);
        }

        public void SetLogInstance(ILog log)
        {
            _log = log;
        }
    }

優點: 將Log實例化延遲,Contract類可以靈活變動依賴。
缺點:使用_log前需要判斷null情況

  • 接口方式注入
     public interface ILogSetter
    {
        ILog Setter(ILog log);
    }
    public class Contract: ILogSetter
    {
        ... ...
        public ILog Setter(ILog log)
        {
            _log = log;
            return _log;
        }
    }

接口方式和方式二有點類似,這里將依賴注入提升為一種能力,可以支配依賴關系的能力。

探討 控制反轉

從這個圖中,可以看到依賴的倒置,這里低層定義接口並繼承實現,高層引用低層定義的接口進行調用使用。

那么現在的控制權是在低層,那么定義接口這個權力到底屬於誰?

比如我們有一個流程對應着5個步驟,這5個步驟又對應着5個接口:A1,A2,A3,A4,A5

業務系統a,需要使用這個流程就需要依次調用這5個接口:A1 -> A2 -> A3 -> A4 -> A5

現在這個流程非常公用,已經上升成為企業級中台的一個流程,越來越多的業務系統給都在對接。

此時,很多的業務系統需要重新用代碼組織一套這樣的調用流程,當然現在的控制權還是在業務系統這里。

所以此時我們對外公開的5個接口,只是簡單提供了調用能力,對於接口的編排全部寄希望於業務系統。

這個時候會有兩種聲音:
1、業務系統不想這么繁瑣地重復着編排這些接口
2、中台也想把流程控制權掌握在自己手中,這樣遇到業務流程的整體性變更,業務系統是不需要調整的

業務流程引擎的加入,就像是我們的接口一樣,它負責接口的編排,然后成為業務流程。

此時,控制權實際在接口提供方。

模式

我們想使用微軟提供的MVC框架,只要是遵循MVC框架的約定就能擁有MVC的能力。

MVC的控制權在框架,應用想通過框架提供的MVC能力就必須按照框架的定義去做。

如果框架僅僅是給i我們提供類似於類庫一樣的MVC實現,

那么整個流程是應用系統自己根據文檔,調用各種類庫文件,編排這些實現滿足業務系統的MVC需求

所以,現在的Asp.Net Core 給我們提供的MVC,只要是我們遵循mvc約定,引擎就會推動整個信息的流動,最終反饋給應用。

這種比較普適的流程或者方案,我們可以成為模式,類似於設計模式,MVC模式.

原來散落在業務系統中的控制權,反向轉到模式中。

總結

依賴倒置可以很小也可以很大,

控制反轉也可以很小也可以很大。

這種思想我們無時無刻可以碰到。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM