.NET Core的依賴註入[1]: 控制反轉

来源:https://www.cnblogs.com/artech/archive/2018/07/25/net-core-di-01.html
-Advertisement-
Play Games

軟體設計中由一些所謂的理念都沒有一個明確的定義,比如之前流行的SOA和現在炒的火熱的微服務(Micro Service)和無伺服器(Serverless),我們都不能通過一個明確的“內涵”給它們一個準確地定義,只能從“外延”上描述這些架構設計應該具有怎樣的特性。正因為無法給出一個明確的界定,造成了人... ...


寫在前面:我之前寫過一系列關於.NET Core依賴註入的文章,由於.NET Core依賴註入框架的實現原理髮生了很大的改變,加上我對包括IoC和DI這些理論層面的東西又有了一些新的理解,所以我在此基礎上寫了8篇文章詳細介紹.NET Core的DI。我將這些文章發佈到我的微信公眾賬號(大內老A)下,很多人留言說還是博客具有更好的閱讀體驗,所以我將在未來8天時間將它們同步到這裡。

軟體設計中由一些所謂的理念都沒有一個明確的定義,比如之前流行的SOA和現在炒的火熱的微服務(Micro Service)和無伺服器(Serverless),我們都不能通過一個明確的“內涵”給它們一個準確地定義,只能從“外延”上描述這些架構設計應該具有怎樣的特性。正因為無法給出一個明確的界定,造成了人們針對同一個概念出現了很多不同的理解。針對IoC也是這種情況,所以本章所訴的僅僅代表作者的一家之言,讀者朋友姑妄聽之,僅作參考。

一、流程式控制制的反轉

我聽到很多人將IoC說成是一種“面向對象的設計模式”,但在我個人看來IoC不能算作 一種“設計模式”,其自身也與“面向對象”沒有直接的關係。我覺得很多人之所以不能很準確地理解IoC源於他們忽略了一個最根本的東西,那就是IoC這個短語,也就是他們之所以對IoC產生了諸多誤解是因為他們忽略了IoC的定義。

IoC的全名Inverse of Control,翻譯成中文就是“控制反轉”或者“控制倒置”。控制反轉也好,控制倒置也罷,它體現的意思是控制權的轉移,即原來控制權在A手中,現在需要B來接管。那麼具體對於軟體設計來說,IoC所謂的控制權的轉移具有怎樣的體現呢?要回答這個問題,就需要先瞭解IoC的C(Control)究竟指的是怎樣一種控制。對於我們所在的任何一件事,不論其大小,其實可以分解成相應的步驟,所以任何一件事都有其固有的流程,IoC涉及的所謂控制可以理解為“針對流程的控制”。

我們通過一個具體事例來說明傳統的設計在採用了IoC之後針對流程的控制是如何實現反轉的。比如說現在設計一個針對Web的MVC類庫,我們不妨將其命名為MvcLib。簡單起見,這個類庫中只包含如下這個同名的靜態類。

public static class MvcLib
{
    public static Task ListenAsync(Uri address);
    public static Task<Request> ReceiveAsync();
    public static Task<Controller> CreateControllerAsync(Request request);
    public static Task<View> ExecuteControllerAsync(Controller controller);
    public static Task RenderViewAsync(View view);
}

MvcLib提供瞭如上5個方法幫助我們完成整個HTTP請求流程中的5個核心任務。具體來說,ListenAsync方法啟動一個監聽器並將其綁定到指定的地址進行HTTP請求的監聽,抵達的請求通過ReceiveAsync方法進行接收,我們將接收到的請求通過一個Request對象來表示。CreateControllerAsync方法根據接收到的請求解析並激活請求的目標Controller對象。ExecuteControllerAsync方法執行激活的Controller並返回一個表示視圖的View對象。RenderViewAsync最終將View對象轉換成HTML並作為當前請求響應的內容返回給請求的客戶端。

現在我們在這個MvcLib的基礎上創建一個真正的MVC應用,那麼除了按照MvcLib的規範自定義具體的Controller和View之外,我們還需要自行控制從請求的監聽與接收、Controller的激活與執行以及View的最終呈現在內的整個流程,這樣一個執行流程反映在如下所示的代碼中。

class Program
{
    static async Task Main()
    {
         Uri address = new Uri("http://0.0.0.0:8080/mvcapp");
         await MvcLib.ListenAsync(address);
         while (true)
        {
            var request = await MvcLib.ReceiveAsync();
            var controller = await MvcLib.CreateControllerAsync(request);
            var view = await MvcLib.ExecuteControllerAsync(controller);
            await MvcLib.RenderViewAsync(view);
        }
    }    
}

這個例子體現瞭如圖1所示的流程式控制制方式(應用的代碼完全採用非同步的方式來處理請求,為了讓流程圖顯得更加簡單,我們在流程圖中畫成了同步的形式,讀者不必糾結這個問題)。我們設計的類庫(MvcLib)僅僅通過API的形式提供某種單一功能的實現,作為類庫消費者的應用程式(App)則需要自行編排整個工作流程。如果從重用的角度來講,這裡被重用的僅限於實現某個環節單一功能的代碼,編排整個工作流程的代碼並沒有得到重用。

3-1

圖1 流程式控制制掌握在應用程式手中

但是當我們構建一個應用的時候,我們需要的不僅僅是一個能夠提供單一API的類庫,我們希望的理想形式是能夠直接在一個現有的框架上構建我們的應用。類庫(Library)和框架(Framework)的不同之處在於,前者往往只是提供實現某種單一功能的API,而後者則針對一個目標任務對這些單一功能進行編排形成一個完整的流程,這個流程在一個引擎的驅動下自動執行。

對於我們上面演示MvcLib來說,作為消費者的應用程式需要自行控制整個HTTP請求的處理流程,但這是實際上這是一個很“泛化”的工作流程,幾乎所有的MVC應用均採用這樣的流程監聽、接收請求並最終對請求予以響應。如果我們將這個流程實現在一個MVC框架之中,由它構建的所有MVC應用就可以直接使用這個請求處理流程,而不需要自行重覆實現它。

現在我們將MvcLib從類庫改造成一個框架,並姑且將其稱為MvcFrame。如圖2所示,MvcFrame的核心是一個被稱為MvcEngine的執行引擎,它驅動一個編排好的工作流對HTTP請求進行一致性處理。如果我們利用MvcFrame構建一個具體的MVC應用,除了根據我們的業務需求定義相應的Controller和View之外,我們只需要初始化這個引擎並直接啟動它即可。如果你曾經開發過ASP.NET MVC應用,你會發現ASP.NET MVC就是這麼一個框架。

3-2

圖2 流程式控制制反轉到框架手中

有了上面演示的這個例子作為鋪墊,我們應該很容易理解IoC所謂的控制反轉。總的來說,IoC是我們設計框架所採用的一種基本思想,所謂的控制反轉就是將對應用流程的控制轉移到框架中。拿上面這個例子來說,在傳統面向類庫編程的時代,針對HTTP請求處理的流程牢牢控制在應用程式手中。在引入框架之後,請求處理的控制權轉移到了框架手上。

二、好萊塢法則

在好萊塢,把簡歷遞交給演藝公司後就只有回家等待。由演藝公司對整個娛樂項目的完全控制,演員只能被動式的接受電影公司的工作,在需要的環節中,完成自己的演出。“不要給我們打電話,我們會給你打電話(don‘t call us, we‘ll call you)”這是著名的好萊塢法則( Hollywood Principle或者 Hollywood Low),IoC完美地體現了這一法則。

3-3

圖3 好萊塢法則

在IoC的應用語境中,框架就像是掌握整個電影製片流程的電影公司,由於它是整個工作流程的實際控制者,所以只有它知道哪個環節需要哪些組件。應用程式就像是演員,它只需要按照框架定製的規則註冊這些組件就可以了,因為框架會在適當的時機字典載入並執行註冊的組件。

以熟悉的ASP.NET Core MVC或者ASP.NET MVC應用開發來說,我們只需要按照約定規則(比如目錄結構和命名等)定義相應的Controller類型和View文件就可以了。當ASP.NET (Core )MVC框架在進行處理請求的過程中,它會根據解析生成的路由參數定義為對應的Controller類型,並按照預定義的規則找到我們定義的Controller,然後自動創建並執行它。如果定義在當前Action方法需要呈現一個View,框架自身會根據預定義的目錄約定找到我們定義的View文件,並對它實施動態編譯和執行。整個流程處處體現了“框架Call應用”的好萊塢法則。

總的來說,我們在一個框架的基礎上進行應用開發,就相當於在一條調試好的流水線上生成某種商品,我們只需要在相應的環節準備對應的原材料,最終下線的就是我們希望得到的最終產品。IoC幾乎是所有框架均具有的一個固有屬性,從這個意義上講,“IoC框架”的說法其實是錯誤的,世界上並沒有什麼IoC框架,或者說幾乎所有的框架都是IoC框架。

三、流程定製

我們採用IoC實現了流程式控制制從應用程式向框架自身的反轉,但是這個被反轉的僅僅是一個泛化的流程,任何一個具體的應用都可能需要對組成該流程的某些環節進行定製。還是以我們的MVC框架來說,可能預設的請求處理流程只考慮到針對HTTP 1.1的支持,但是當我們在設計框架的時候應該提供相應的擴展點來支持HTTP 2。作為一個Web框架,用戶認證功能是必備的,但是框架自身不能限制於某一種或者幾種固定的認證方式,應該通過擴展的方式讓用戶可以自由地定製任意的認證模式。

我們可以說得更加寬泛點。如圖4所示,我們將一個泛化的工作流程(A=>B=>C)被定義在框架之中,建立在該框架的兩個應用需要對組成這個流程的某些環節進行定製。比如步驟A和C可以被App1重用,但是步驟B卻需要被定製(B1),App2則重用步驟A和B,但是需要按照自己的方式處理步驟C。

3-4

圖4 應用程式對流程的定製

IoC將對流程的控制從應用程式轉移到框架之中,框架利用一個引擎驅動整個流程的執行,應用程式無需關心該工作流程的細節,它只需要啟動這個引擎即可。但是這個引擎一旦被啟動,框架就會完全按照預先編排好的流程進行工作,如果應用程式希望整個流程按照自己希望的方式被執行,針對流程的定製一般在發生在啟動引擎之前。

一般來說,框架會以相應的形式提供一系列的擴展點,應用程式則通過定義擴展的方式實現對流程某個環節的定製。在引擎被啟動之前,應用程式將所需的擴展註冊到框架之中。一旦引擎被正常啟動,這些註冊的擴展會自動參與到整個流程的執行過程中。

綜上所述,IoC一方面通過流程式控制制從應用程式向框架的反轉實現了針對流程自身的重用,另一方面通過內置的擴展機制這個被重用的流程可能自由地被定製,這兩個因素決定了框架自身的價值。重用讓框架不僅僅是為應用程式提供實現單一功能的API,而是提供一整套可執行的解決方案,可定製則使我們可以為不同的應用程式對框架進行定製,這無疑讓框架可以使用到更多的應用之中。


依賴註入[1]: 控制反轉
依賴註入[2]: 基於IoC的設計模式
依賴註入[3]: 依賴註入模式
依賴註入[4]: 創建一個簡易版的DI框架[上篇]
依賴註入[5]: 創建一個簡易版的DI框架[下篇]
依賴註入[6]: .NET Core DI框架[編程體驗]
依賴註入[7]: .NET Core DI框架[服務註冊]
依賴註入[8]: .NET Core DI框架[服務消費]


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 使用jdbc查詢MySQL資料庫表中用戶表記錄 創建java工程,加入jar包 資料庫驅動包(MySQL5.1) jdbc連接資料庫代碼 問題總結 1、資料庫連接,使用時就創建,不使用立即釋放,對資料庫進行頻繁連接開啟和關閉,造成資料庫資源量費,影響資料庫性能。 設想:使用資料庫連接池管理資料庫連接 ...
  • 由於公司需求,要有一個監控,並且可以釘釘、郵件報警,我就做了。雖然,我是tester,因為公司暫時沒運維,我兼職了。 前提條件: * 已經裝好naigos,但沒配置報警 * 需要linux下有python環境,並且安裝requests第三方庫 首先,我先感概一下這nagios的配置真有點麻煩,而且沒 ...
  • [1] 程式設計 [2] 測試結果 測試結果顯示,複製3.01GB大小的文件所用的時間約為1min。 註:希望與各位讀者相互交流,共同學習進步。 ...
  • ORM簡介 MVC框架中包括一個重要的部分,就是ORM,它實現了數據模型與資料庫的解耦,即數據模型的設計不需要依賴於特定的資料庫,通過簡單的配置就可以輕鬆更換資料庫 ORM是“對象-關係-映射”的簡稱,主要任務是: 根據對象的類型生成表結構 將對象、列表的操作,轉換為sql語句 將sql查詢到的結果 ...
  • 數值類型 Python支持四種不同的數值類型,包括int(整數)long(長整數)float(浮點數)complex (複數) python3對整數的大小不做限制 算數運算符 優先順序: 邏輯運算符 優先順序: not > and > or center()在字元串兩邊填充字元,讓字元串居中 ...
  • 前言 隨著Spring Boot2.0正式發佈,Spring WebFlux正式來到了Spring Boot大家族裡面。由於Spring WebFlux可以通過更少的線程去實現更高的併發和使用更少的硬體資源去實現擴展,我對此很感興趣。同時Angular6也發佈了,也想試試自己Angular的功底,便 ...
  • 前言 在 "上一篇" 中我們學習了單例模式,介紹了單例模式創建的幾種方法以及最優的方法。本篇則介紹設計模式中的工廠模式,主要分為簡單工廠模式、工廠方法和抽象工廠模式。 簡單工廠模式 簡單工廠模式是屬於創建型模式,又叫做靜態工廠方法模式。簡單工廠模式是由一個工廠對象決定創建出哪一種產品類的實例。調用只 ...
  • 裝飾模式 裝飾模式之前的面向對象原則介紹: 單一職責原則:就一個類而言,應該僅有一個引起它變化的原因。也就是說功能要單一。 優點: 靈活性,可復用性。 如果一個類承擔的職責太多,就等於把這些職責耦合在一起,一個職責的變化可能會削弱或者阻礙其他職責能力,這種耦合會導致脆弱的設計,當變化發生時,設計會發 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...