依賴注入,在 English 裏縮寫是
DI
,感興趣但還不太瞭解這個概念的大哥能夠去 Dependency Injectionphp
開頭先來一句:M$ 大法好,TypeScript真香!前端
前些日子一直在抽空搞一個本身的TypeScript相關項目,大致是一個base在依賴注入(DI,後面就縮寫了)平面之下的node服務端框架。寫着寫着,以爲頗有必要把 DI 平面從框架裏面剝離出來,改善一下擴展性,沉澱成一個通用的解決方案。在這裏呢,我打算整理一下以前所寫的東西,也大體講講怎樣一步一步地搭建出一個簡單的 DI 系統。node
概念就不囉嗦了,直接來一發「 Dependency Injection 」維基百科解決。angularjs
我曾經接觸過幾款好用的 DI 框架,好比偉大的信仰「ASP.NET Core」,有近幾年在前端引領ts潮流的「Angular」,也包括如今在node領域裏也很是火的「nest」。我甚至在php中(我真的很是討厭寫php)也見過 DI 框架,只是對於php我的瞭解的太少,也就不拿出來細說了。es6
說來慚愧,我尚未看過其中任何一個的源代碼,任何一個!我太懶了🐷💣 !!!express
我曾經用過 C# 的「Simple Injector」,大體和「ASP.NET Core」官方的理念類似,我也在JS中見過 DI,好比最先的「angularjs」,相比靜態語言強大的 DI 框架,JS 裏面的 DI 雖然相對蹩腳,但那也的確算得上是實用至上了。後端
按照我本身的理解,只要是類C/S模型,在設計上秉承着靜態語義、接口分離、開放封閉、供應消費、定義先行這些理念,DI基本能夠認爲老是一個首選的解決方案。DI 可以帶來API層面更大的穩定性,不只可讓咱們更專一於業務的編排,解開領域之間的強耦合,還能優化邏輯的拆分隔離,爲系統提供更強的健壯性。服務器
相比較前端而言,後端老闆們已經玩了 DI 不少年了,雖然有些複雜的DI特性在大多數狀況下顯得是屠龍術了,但 DI 自己上面提到最精幹的那些能力,足以經受住時間和實踐的考驗。session
而從另外一個方面來說,近年來TypeScript火起來其實也並非一個偶然。JS 這門動態語言在經歷歲月的磨洗以後,終於讓廣大的前端老闆認識到 「不自由毋寧死,一自由就墮落」 這句真諦。穩定仍是須要靜態語法的強暴才能夠保障,而 DI 則是能夠突破靜態語言缺陷的超級武器。架構
這個問題詳細展開以前,不如來用一個例子來描述一下現實的痛點。
就拿用的不少的koa來說吧(express的回調語法太蛋疼了,不用- -),koa2 是一個很優秀也很輕巧的node服務端框架,包了一些簡單的能力出來,以適應最小化的服務器模型。
但 koa 終究仍是過於輕量了,或者說並不存在那種絕對完備的node框架,能夠適應一切業務的需求,大多數時候咱們都須要對他進行定製。因此咱們可能會對 koa2 進行基於 es6 class 語法的從新組裝,構造出諸如controller、service的典型MVC概念。koa 提供了一個 context 對象來承載單詞請求的全部信息,爲了方便咱們就在ctx(context,上下文)上擴展咱們所要的能力,並把他送往請求的任意一個角落。
到目前爲止這並不會存在問題。
接下來咱們會面臨一些能力共用的場景:假若有個基礎能力,簡單到諸如存儲和讀取當前session的用戶信息,這個能力真的很是基礎和簡單,但可能用到的地方卻又️至關普遍。在service裏咱們可能須要直接讀取登陸態來發動請求或者數據落庫,在controller咱們須要用戶信息進行一些簡單的邏輯組合,在中間件裏咱們可能須要針對用戶信息進行權限處理。有一些基礎而通用的能力,咱們老是在不少地方重複的使用,這並不適合掛載在單繼承模式的controller或者service的繼承鏈路裏面,由於存在一個很簡單的矛盾:不一樣繼承分支中的能力並不可以共用。
這些暫且均可以放在一遍,業務不大,這些問題並非什麼痛點。
而後業務擴大了,能力共用的需求迫在眉睫,框架繼承是一種相比分包引用更佳穩妥的思路(雖然擴展和定製能力相對較弱)來適應業務的普適性。因而經驗豐富的老闆開始圍繞koa設計出了一套框架繼承的作法,每一層框架會繼承前一層框架全部的配置、插件、ctx擴展和基礎服務組件等等,至關牛b。這是合理的,一個公司可能會抽象一些通用邏輯做爲基礎框架,就省去了各個業務的大量重複開發成本。
但這樣的處理,會加重以前面臨的問題:每個倉庫提供出來一個繼承了原始框架基礎service和controller的新base,事實上已經將後續的能力完全的隔離開來了,將來基於這個框架的業務都會本身繼承base擴展進去新的能力,並按照本身的繼承鏈路繼續下去,有些能力在業務中原本能夠共享,但他們卻在以前一步分道揚鑣,進入了兩條獨立的繼承鏈路中去了。形象的描述就是:一條單分叉的河(一單分叉就不會再匯合了)在一個地方分叉(分紅支流controller和支流service)了,他的兩條支流將來就不會再有交集了,包括一些通用的邏輯(好比獲取session,兩邊必須有一份徹底相同的邏輯),都不能在想互共享了,你只能寫兩份。
那不還有context嗎?
對,context能夠很好的解決這種困境。只要service和controller都擁有一個context,那掛載context傷的屬性和行數就均可以在service和controller中共用了。這也正是koa的作法。koa 的 context 是一個巨大的對象,大到幾乎整個業務的核心要素都在裏面了。各級框架經過不判定製context,service和controller或者中間件經過修改context 的數據,就能夠簡單地作到數據共享。prefect!
事情彷佛完美的解決了?
而後有一天,咱們的老闆一聲令下,整個架構都應該準備切到TypeScript來了。
糟糕,這下問題可就大了。。。。
。。。 。。 。
太晚了,再不睡覺明天早上又要遲到了 😢😂 (待續