咱們在軟件工程中進行的架構設計、模塊實現、編碼等工做,不少時候說到底就是圍繞一件事進行:解耦。編程
三層架構,MVC,微服務,DDD.咱們分析問題,抽象問題,而後劃分邊界,劃分層次。設計模式
也是爲了讓咱們的類、模塊、系統有更強的複用能力,提升生產效率。架構
這一次,我想深刻了解和探討我曾經很迷糊,也沒有一直仔細瞭解的:依賴倒置、控制反轉、依賴注入 這些概念。mvc
一般能夠理解爲一種須要,需求。須要協助才能完成一件事情。
例如,咱們依賴日誌服務寫日誌:框架
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就肯定好依賴。
缺點:後期沒法更改依賴。
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個接口,只是簡單提供了調用能力,對於接口的編排所有寄但願於業務系統。
這個時候會有兩種聲音:
一、業務系統不想這麼繁瑣地重複着編排這些接口
二、中臺也想把流程控制權掌握在本身手中,這樣遇到業務流程的總體性變動,業務系統是不須要調整的
業務流程引擎的加入,就像是咱們的接口同樣,它負責接口的編排,而後成爲業務流程。
此時,控制權實際在接口提供方。
咱們想使用微軟提供的MVC框架,只要是遵循MVC框架的約定就能擁有MVC的能力。
MVC的控制權在框架,應用想經過框架提供的MVC能力就必須按照框架的定義去作。
若是框架僅僅是給i咱們提供相似於類庫同樣的MVC實現,
那麼整個流程是應用系統本身根據文檔,調用各類類庫文件,編排這些實現知足業務系統的MVC需求
因此,如今的Asp.Net Core 給咱們提供的MVC,只要是咱們遵循mvc約定,引擎就會推進整個信息的流動,最終反饋給應用。
這種比較普適的流程或者方案,咱們能夠成爲模式,相似於設計模式,MVC模式.
原來算落在業務系統中的控制權,反向轉到模式中。
依賴倒置能夠很小也能夠很大,
控制反轉也能夠很小也能夠很大。
這種思想咱們無時無刻能夠碰到。