ASP.NET Core的框架的設計從頭到尾都是模塊化的,並且遵循良好的軟件工程實踐。在面向對象的程序設計中,SOLID經受住了時間的考驗。ASP.NET Core已經把依賴注入融入了核心框架。不管你自己是否想把依賴注入運用到自己的代碼中,ASP.NET Core框架已經把依賴注入當成了一個基本概念。
SOLID是 Single responsibility, Open-closed, Liskov substitution, Interface segregation, Dependeny inversion的簡寫:https://zh.wikipedia.org/zh-hans/SOLID_(面向對象設計)
依賴注入的介紹參考鏈接:https://www.martinfowler.com/articles/injection.html
1.明白依賴注入的好處
在簡單應用程序中我們使用不到依賴注入,但當應用程序變得復雜得時候,依賴注入(DI)可以作為一個偉大的工具來控制代碼的復雜性。
讓我們看下面一個例子,當不使用DI時,一個用戶在你的Web應用程序上注冊后,你想給它發送一封郵件,下面的代碼展示了你可能會在最初的時候這樣處理問題。
在這個例子中,當一個新用戶注冊你的Web應用程序時,在UserControllor中RegeditUser
將執行。首先創建一個EmailSender類的實例,並且調用SendEmail()方法去發送郵件。在這個例子中,你可以設想將看到以下的內容:
Console.WriteLine將會執行發送email。
如果EmailSender類像這個例子一樣簡單並且沒有依賴項,你可能不會考慮使用其它方法來創建對象。在某種程度上,你可能時對的。但是如果你稍后更新EmailSender的實現邏輯,發現它並不能實現自身郵件發送的全部邏輯。
實際上,EmailSender在發送郵件時需要做大量的事情:
1.創建一個email消息
2.配置email服務的設置
3.將郵件發送給emial服務器
如果在一個類中實現以上功能,將會違背單位職責原則(SRP),所以不能讓EmialSender依賴其它服務。下圖將會展示Web應用程序的依賴將會是怎樣的。UserController想要使用EmailSender去發送郵件,但是如果要這樣做,首先需要創建一個MessageFactory,NetworkClient,EmailServerSettings對象給EmailSender提供依賴。
每一個類都需要大量的依賴,在這個例子中UserController作為“底部”的類,需要知道如何創建它所依賴的每一個類,以及每一個類的依賴關系。
多依賴服務的例子如下:
NetworkClient依賴的實例代碼如下:
用這樣的方式可能覺得很麻煩,但這樣的依賴鏈卻是很常見的。實際上,你的代碼中並沒有這樣寫,那你的類將會很龐大,並且違背了單一職責原則。當你手動創建依賴時,發送郵件時是沒有DI的。代碼如下所示:
上述代碼示例變成了一些粗糙的代碼。當在UserController調用時,為提高EmailSender的設計性,以分離出不同的職責,這成為了一個麻煩事。上述代碼有以下問題。
1.沒有遵循單一職責原則-我們的代碼同時負責創建EmailSender實例並且使用它發送郵件。
2.相當大的方法代碼-上面的RegeditUser方法,只有最后兩行代碼才有用。很難讀懂這個方法的真正目的是什么。
3.與實現綁定-如果你決定去重構EmialSender並且去添加另一個依賴,你需要去更新它每一個使用的地方。
UserController已經明確的依賴了EmailSender類,並且手動創建了這個對象的實例作為RegeditUser方法的一部門。在閱讀源代碼時僅僅知道UserController使用了EmailSender。相比之下,EmialSender已經明確依賴了NetworkClient和MessageFactory,並且在構造函數中進行了初始化。同樣的,NetworkClient已經明確的依賴了EmailServerSetting類。
依賴注入旨在決絕反轉依賴鏈。以代替UserController去手動的創建依賴,深入代碼實現的細節中,已經創建了通過構造函數注入EmailSender創建的實例。
現在,很明顯了,需要一些事情去創建對象,所以這些代碼對象需要在某處存活。負責創建對象的服務稱為 DI 容器或IoC容器。常見的DI容器有:Autofac,StructureMap, Unity, Ninject, Simple Injector . . .
使用DI容器的關系圖如下:
此時UserController中的代碼如下圖所示:
DI容器的優勢就說職責單一:創建對象或服務。向容器請求一個服務的實例,並且計算出增氧創建一個依賴圖,但這些都依賴於怎樣配置它們。
使用明顯的依賴的優勢就是,在一個方法中你永遠不會在寫混亂的方法了。DI容器可以檢查你的服務的構造方法並且計算出代碼自身中重要的部分。DI容器是可以配置的,所以你可以手動創建可提供的服務的實例。它可以通過接口讓你的代碼保持松耦合。
在面向對象語言中耦合是一個很重要的概念。它指出一個類怎樣去依賴其它類並且執行它自己的函數。松耦合的代碼不需要知道一個特別組件的很多細節,就直接可以去使用它。
在UserController中初始化EmailSender就說緊耦合的,你需要直接的創建EmialSender的實例。一個難點就是這個代碼很難去測試,任何嘗試測試UserController的方法將導致發送郵件。將 EmailSender 作為構造函數參數,並刪除創建對象的職責將會減少代碼的耦合。如果EmialSender的實現變了,讓它依賴於另一個實現,不需要在同時更新更新UserController。
通過接口變成在設計模式中是一種常見的方法,它減少了系統中的耦合。並且還不會綁定到單一的實現,並且這也讓類變的可以測試。所以可以創建一個IEmialSender接口,讓EmailSender實現它:
用戶控制器的代碼如下:
現在UserController已經不用關心依賴是怎么實現的了,僅僅實現了IEmailSender接口並且暴露了SendEmail方法。現在應用程序的代碼已經獨立於實現了。現在已經實現了松耦合的代碼了。但是又有一個問題:應用程序怎么知道使用EmailSender去代替DummyEmailSender,DI容器告訴你答案:當你需要IEmailSender,使用EmailSender去注冊。
怎樣在DI容器中注冊接口和實現非常依賴DI容器的實現方式,但是基本所有DI容器的原則都是一樣的。ASP.NET Core包含開箱即用的簡單DI容器,所以讓我們看下在一個典型的請求中怎樣使用它。
2.在ASP.NET Core中使用依賴注入
本章詳細的內容將在下篇博客中詳細說明。