chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

VS Code源碼深入淺出--依賴注入設(shè)計(jì)

jf_8lIj6kO1 ? 來源:SegmentFault思否 ? 作者:Duang ? 2022-12-14 10:37 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

在閱讀 VS Code 代碼的過程中,我們會(huì)發(fā)現(xiàn)每一個(gè)模塊中都有大量裝飾器的使用,用來裝飾模塊以及其中依賴的模塊變量。這樣做的目的是什么呢?在這一篇中我們來詳細(xì)分析一下。

依賴注入介紹


如果有這樣一個(gè)模塊 A,它的實(shí)現(xiàn)依賴另一個(gè)模塊 B 的能力,那么應(yīng)該如何設(shè)計(jì)呢?很簡單,我們可以在 A 模塊的構(gòu)造函數(shù)中實(shí)例化模塊 B,這樣就可以在模塊 A 內(nèi)部使用模塊 B 的能力了。

classA{
constructor(){
this.b=newB();
}
}

classB{}

consta=newA();

但是這樣做有兩個(gè)問題,一是模塊 A 的實(shí)例化過程中,需要手動(dòng)實(shí)例化模塊 B,而且如果模塊 B 的依賴關(guān)系發(fā)生變化,那么也需要修改模塊 A 的構(gòu)造函數(shù),導(dǎo)致代碼耦合。

二是在復(fù)雜項(xiàng)目中,我們在實(shí)例化模塊 A 時(shí),難以判斷模塊 B 是否被其他模塊依賴而已經(jīng)實(shí)例化過了,從而可能將模塊 B 多次實(shí)例化。若模塊 B 較重或者需要為單例設(shè)計(jì),這將帶來性能問題。

因此,更好的方式是,將所有模塊的實(shí)例化交給外層框架,由框架統(tǒng)一管理模塊的實(shí)例化過程,這樣就可以解決上述兩個(gè)問題。

classA{
constructor(privateb:B){
this.b=b;
}
}

classB{}

classC{
constructor(privatea:A,privateb:B){
this.b=b;
}
}

constb=newB();
consta=newA(b);
constc=newC(a,b);

這種將依賴對象通過外部注入,避免在模塊內(nèi)部實(shí)例化依賴的方式,稱為依賴注入 (Dependencies Inject, 簡稱 DI)。這在軟件工程中是一種常見的設(shè)計(jì)模式,我們在 Java 的 Spring,JS 的 Angular,Node 的 NestJS 等框架中都可以看到這種設(shè)計(jì)模式的應(yīng)用。

當(dāng)然,在實(shí)際應(yīng)用中,由于模塊眾多,依賴復(fù)雜,我們很難像上面的例子一樣,規(guī)劃出來每個(gè)模塊的實(shí)例化時(shí)機(jī),從而編寫模塊實(shí)例化順序。并且,許多模塊可能并不需要第一時(shí)間被創(chuàng)建,需要按需實(shí)例化,因此,粗暴的統(tǒng)一實(shí)例化是不可取的。

因此我們需要一個(gè)統(tǒng)一的框架來分析并管理所有模塊的實(shí)例化過程,這就是依賴注入框架的作用。

借助于 TypeScript 的裝飾器能力,VSCode 實(shí)現(xiàn)了一個(gè)極為輕量化的依賴注入框架。我們可以先來簡單實(shí)現(xiàn)一下,解開這個(gè)巧妙設(shè)計(jì)的神秘面紗。

最簡依賴注入框架設(shè)計(jì)


實(shí)現(xiàn)一個(gè)依賴注入框架只需要兩步,一個(gè)是將模塊聲明并注冊到框架中進(jìn)行管理,另一個(gè)是在模塊構(gòu)造函數(shù)中,聲明所需要依賴的模塊有哪些。

我們先來看模塊的注冊過程,這需要 TypeScript 的類裝飾器能力。我們在注入時(shí),只需要判斷模塊是否已經(jīng)注冊,如果沒有注冊,將模塊的 id(這里簡化為模塊 Class 名稱)與類型傳入即可完成單個(gè)模塊的注冊。

exportfunctionInjectable():ClassDecorator{
return(Target:Class):any=>{
if(!collection.providers.has(Target.name)){
collection.providers.set(Target.name,target);
}
returntarget;
};
}

之后我們再來看看模塊是如何聲明依賴的,這需要 TypeScript 的屬性裝飾器能力。我們在注入時(shí),先判斷依賴的模塊是否已經(jīng)被實(shí)例化,如果沒有,則將依賴模塊進(jìn)行實(shí)例化,并存入框架中管理。最終返回已經(jīng)被實(shí)例化完成的模塊實(shí)例。

exportfunctionInject():PropertyDecorator{
return(target:Property,propertyKey:string)=>{

constinstance=collection.dependencies.get(propertyKey);
if(!instance){
constDependencyProvider:Class=collection.providers.get(propertyKey);
collection.dependencies.set(propertyKey,newDependencyProvider());
}

target[propertyKey]=collection.dependencies.get(propertyKey);
};
}

最后只需要保證框架本身在項(xiàng)目運(yùn)行前完成實(shí)例化即可。(在例子中表示為 injector)

exportclassServiceCollection{
readonlyproviders=newMap();
readonlydependencies=newMap();
}

constcollection=newServiceCollection();
exportdefaultcollection;

這樣,一個(gè)最簡化的依賴注入框架就完成了。由于保存了模塊的類型與實(shí)例,它實(shí)現(xiàn)了模塊的按需實(shí)例化,無需在項(xiàng)目啟動(dòng)時(shí)就初始化所有模塊。

我們可以嘗試調(diào)用它,以上面舉出的例子為例:

@injectable()
classA{
constructor(@inject()privateb:B){
this.b=b;
}
}

@injectable()
classB{}

classC{
constructor(@inject()privatea:A,@inject()privateb:B){
this.b=b;
}
}

constc=newC();

無需知曉模塊 A,B 的實(shí)例化時(shí)機(jī),直接初始化任何一個(gè)模塊,框架會(huì)自動(dòng)幫你找到并實(shí)例化好所有依賴的模塊。

VSCode 的依賴收集實(shí)現(xiàn)


上面介紹了一個(gè)依賴注入框架的最簡實(shí)現(xiàn)。但當(dāng)我們真正閱讀 VSCode 的源碼時(shí),我們發(fā)現(xiàn) VSCode 中的依賴注入框架貌似并不是這樣消費(fèi)的。

例如在下面這段鑒權(quán)服務(wù)中,我們發(fā)現(xiàn)該類并沒有@injectable()作為類的依賴收集,并且依賴服務(wù)也直接用其類名作為修飾器,而不是@inject()。

//srcvsworkbenchservicesauthenticationrowserauthenticationService.ts
exportclassAuthenticationServiceextendsDisposableimplementsIAuthenticationService{
constructor(
@IActivityServiceprivatereadonlyactivityService:IActivityService,
@IExtensionServiceprivatereadonlyextensionService:IExtensionService,
@IStorageServiceprivatereadonlystorageService:IStorageService,
@IRemoteAgentServiceprivatereadonlyremoteAgentService:IRemoteAgentService,
@IDialogServiceprivatereadonlydialogService:IDialogService,
@IQuickInputServiceprivatereadonlyquickInputService:IQuickInputService
){}
}

其實(shí)這里的修飾符并不是真正指向類名,而是一個(gè)同名的資源描述符 id(VSCode 中稱之為 ServiceIdentifier),通常使用字符串或 Symbol 標(biāo)識(shí)。

通過 ServiceIdentifier 作為 id,而不是簡單粗暴地通過類名稱作為 id 注冊 Service,有利于處理項(xiàng)目中一個(gè) interface 可能存在多態(tài)實(shí)現(xiàn),需要同時(shí)多個(gè)同名類實(shí)例的問題。

此外,在構(gòu)造 ServiceIdentifier 時(shí),我們便可以將該類聲明注入框架,而無需@injectable()顯示調(diào)用了。

那么,這樣一個(gè) ServiceIdentifier 該如何構(gòu)造呢?

//srcvsplatforminstantiationcommoninstantiation.ts
/**
*The*only*validwaytocreatea{{ServiceIdentifier}}.
*/
exportfunctioncreateDecorator(serviceId:string):ServiceIdentifier{

if(_util.serviceIds.has(serviceId)){
return_util.serviceIds.get(serviceId)!;
}

constid=function(target:Function,key:string,index:number):any{
if(arguments.length!==3){
thrownewError('@IServiceName-decoratorcanonlybeusedtodecorateaparameter');
}
storeServiceDependency(id,target,index);
};

id.toString=()=>serviceId;

_util.serviceIds.set(serviceId,id);
returnid;
}

//被 ServiceIdentifier 裝飾的類在運(yùn)行時(shí),將收集該類的依賴,注入到框架中。
functionstoreServiceDependency(id:Function,target:Function,index:number):void{
if((targetasany)[_util.DI_TARGET]===target){
(targetasany)[_util.DI_DEPENDENCIES].push({id,index});
}else{
(targetasany)[_util.DI_DEPENDENCIES]=[{id,index}];
(targetasany)[_util.DI_TARGET]=target;
}
}

我們僅需通過createDecorator方法為類創(chuàng)建一個(gè)唯一的ServiceIdentifier,并將其作為修飾符即可。

以上面的 AuthenticationService 為例,若所依賴的 ActivityService 需要變更多態(tài)實(shí)現(xiàn),僅需修改 ServiceIdentifier 修飾符確定實(shí)現(xiàn)方式即可,無需更改業(yè)務(wù)的調(diào)用代碼。

exportconstIActivityServicePlanA=createDecorator("IActivityServicePlanA");
exportconstIActivityServicePlanB=createDecorator("IActivityServicePlanB");
exportinterfaceIActivityService{...}

exportclassAuthenticationService{
constructor(
@IActivityServicePlanAprivatereadonlyactivityService:IActivityService,
){}
}

循環(huán)依賴問題


模塊之間的依賴關(guān)系是有可能存在循環(huán)依賴的,比如 A 依賴 B,B 依賴 A。這種情況下進(jìn)行兩個(gè)模塊的實(shí)例化會(huì)造成死循環(huán),因此我們需要在框架中加入循環(huán)依賴檢測機(jī)制來進(jìn)行規(guī)避。

本質(zhì)上,一個(gè)健康的模塊依賴關(guān)系就是一個(gè)有向無環(huán)圖(DAG),我們之前介紹過有向無環(huán)圖在 excel 表格函數(shù)中的應(yīng)用,放在依賴注入框架的設(shè)計(jì)中也同樣適用。

我們可以通過深度優(yōu)先搜索(DFS)來檢測模塊之間的依賴關(guān)系,如果發(fā)現(xiàn)存在循環(huán)依賴,則拋出異常。

//src/vs/platform/instantiation/common/instantiationService.ts
while(true){
letroots=graph.roots();

//ifthereisnomorerootsbutstill
//nodesinthegraphwehaveacycle
if(roots.length===0){
if(graph.length!==0){
throwCycleError();
}
break;
}

for(letrootofroots){
//createinstanceandoverwritetheservicecollections
constinstance=this._createInstance(root.data.desc,[]);
this._services.set(root.data.id,instance);
graph.removeNode(root.data);
}
}

該方法通過獲取圖節(jié)點(diǎn)的出度,將該類的全部依賴提取出來作為roots,然后逐個(gè)實(shí)例化,并從途中剝離該依賴節(jié)點(diǎn)。由于依賴樹的構(gòu)建是逐層依賴的,因此按順序?qū)嵗纯?。?dāng)發(fā)現(xiàn)該類的所有依賴都被實(shí)例化后,圖中仍存在節(jié)點(diǎn),則認(rèn)為存在循環(huán)依賴,拋出異常。

總結(jié)


本篇文章簡要介紹并實(shí)現(xiàn)了一個(gè)依賴注入框架,并解析了VSCode在實(shí)際問題上做出的一些改進(jìn)。

實(shí)際上 VSCode 的依賴注入能力還有很多細(xì)節(jié)需要處理。例如異步實(shí)例化能力支持,通過封裝 Deferred 類取得Promise執(zhí)行狀態(tài),等等,在此就不一一展開了。感興趣的同學(xué)可以參考 VSCode 源碼:src/vs/platform/instantiation/common/instantiationService.ts,https://segmentfault.com/a/src/vs/platform/instantiation/common/instantiationService.ts做更進(jìn)一步的學(xué)習(xí)。


審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 源碼
    +關(guān)注

    關(guān)注

    8

    文章

    684

    瀏覽量

    31280
  • 變量
    +關(guān)注

    關(guān)注

    0

    文章

    615

    瀏覽量

    29485
  • vscode
    +關(guān)注

    關(guān)注

    1

    文章

    172

    瀏覽量

    9123

原文標(biāo)題:VS Code 源碼深入淺出 -- 依賴注入設(shè)計(jì)

文章出處:【微信號(hào):玩轉(zhuǎn)VS Code,微信公眾號(hào):玩轉(zhuǎn)VS Code】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    深入淺出:SN65LVDSxxx高速差分線驅(qū)動(dòng)與接收器解析

    深入淺出:SN65LVDSxxx高速差分線驅(qū)動(dòng)與接收器解析 在高速數(shù)據(jù)傳輸?shù)念I(lǐng)域中,低電壓差分信號(hào)(LVDS)技術(shù)以其低功耗、高速度和抗干擾能力強(qiáng)等優(yōu)勢,成為了眾多電子工程師的首選。德州儀器(TI
    的頭像 發(fā)表于 01-15 15:30 ?206次閱讀

    深入淺出GMSSL:掌握SM2、SM3、SM4國密算法的高效實(shí)踐

    將帶你從零開始,深入理解這三大核心算法在GMSSL中的高效使用方式,幫助你在實(shí)際項(xiàng)目中快速落地國密安全方案。 本文將以通信定位二合一系列Air780EGH核心板為例,帶你快速上手GMSSL國密算法SM2、SM3、SM4相關(guān)示例。 一、SM2:橢圓曲線公鑰密碼算法 SM2橢
    的頭像 發(fā)表于 12-12 18:20 ?566次閱讀
    <b class='flag-5'>深入淺出</b>GMSSL:掌握SM2、SM3、SM4國密算法的高效實(shí)踐

    VS Code 中`xiaozhi-esp32` 項(xiàng)目文件夾 修改 I2C 設(shè)備地址為 `0x78`

    VS Code 中`xiaozhi-esp32` 項(xiàng)目文件夾 修改 I2C 設(shè)備地址為 `0x78`
    的頭像 發(fā)表于 11-28 07:30 ?987次閱讀

    VS Code運(yùn)行 pytest_hello_world.py

    VS Code運(yùn)行 pytest_hello_world.py
    的頭像 發(fā)表于 11-24 00:33 ?570次閱讀

    Joycode 無法跨項(xiàng)目讀取源碼怎么辦?MCP Easy Code Reader 幫你解決!

    Code Agent 幫我們分析邏輯和編寫代碼,而無需再手動(dòng)將源碼復(fù)制到對話框中發(fā)送給 AI,提高 Code Agent 準(zhǔn)確度和編碼效率。MCP 已發(fā)布 Github: easy-code
    的頭像 發(fā)表于 11-19 15:50 ?1038次閱讀
    Joycode 無法跨項(xiàng)目讀取<b class='flag-5'>源碼</b>怎么辦?MCP Easy <b class='flag-5'>Code</b> Reader 幫你解決!

    如何在VS code中配置Zephyr集成開發(fā)環(huán)境

    上一篇文章介紹了如何在VS code中使用瑞薩官方插件為RA芯片創(chuàng)建項(xiàng)目與項(xiàng)目調(diào)試,相信大家對RA在VS code中的開發(fā)有了基礎(chǔ)的了解。
    的頭像 發(fā)表于 11-05 14:46 ?1467次閱讀
    如何在<b class='flag-5'>VS</b> <b class='flag-5'>code</b>中配置Zephyr集成開發(fā)環(huán)境

    SEGGER工具鏈集成到CMake和VS Code

    SEGGER公司已將其嵌入式開發(fā)工具鏈集成到了廣泛使用的CMake構(gòu)建配置工具中,這意味著基于Visual Studio CodeVS Code)代碼編輯器的應(yīng)用開發(fā)可以方便的使用SEGGER工具實(shí)現(xiàn)了。
    的頭像 發(fā)表于 07-23 15:06 ?981次閱讀

    深入淺出解析:為什么高精度測量要選擇12bit示波器?

    示波器是電子工程師的核心工具,能夠直觀觀察信號(hào)特性。本文通過對比實(shí)驗(yàn),分析12bit和8bit示波器在信號(hào)捕捉和波形還原方面的關(guān)鍵差異,解釋為何高精度測量工作中,12bit示波器能提供更可靠的測量結(jié)果。8bit與12bit:數(shù)字背后的真相什么是示波器的“bit數(shù)”時(shí)?簡單來說,這是示波器ADC(模數(shù)轉(zhuǎn)換器)的分辨能力,決定了儀器能夠識(shí)別的最小電壓變化。這個(gè)看
    的頭像 發(fā)表于 05-22 11:40 ?1259次閱讀
    <b class='flag-5'>深入淺出</b>解析:為什么高精度測量要選擇12bit示波器?

    門老師教你快速看懂電子電路圖

    本文從最基本的電容電阻開講,包含模電數(shù)電,以及部分電氣知識(shí)點(diǎn),深入淺出。 資料介紹: 全文共分7課,以老師授課和師生交流的形式系統(tǒng)地介紹了電子電路識(shí)圖方面的基本知識(shí)和技能,包括電路圖的基本概念和要素
    發(fā)表于 05-16 15:17

    程序設(shè)計(jì)與數(shù)據(jù)結(jié)構(gòu)

    的地址)出發(fā),采用推導(dǎo)的方式,深入淺出的分析了廣大C程序員學(xué)習(xí)和開發(fā)中遇到的難點(diǎn)。 2. 從方法論的高度對C語言在數(shù)據(jù)結(jié)構(gòu)和算法方面的應(yīng)用進(jìn)行了深入講解和闡述。 3. 講解了絕大多數(shù)C程序員開發(fā)
    發(fā)表于 05-13 16:45

    如何在VS Code中使用瑞薩RA系列MCU

    VS Code(Visual Studio Code)是微軟公司出品,它是一個(gè)免費(fèi)且多功能的代碼編輯器,幾乎支持所有主要的編程語言和框架。特別是最近又新加了Github Copilot功能,讓用戶
    的頭像 發(fā)表于 04-16 14:02 ?3546次閱讀
    如何在<b class='flag-5'>VS</b> <b class='flag-5'>Code</b>中使用瑞薩RA系列MCU

    全面解析新概念模擬電路(建議下載?。?/a>

    全文共五冊,近50萬字,一樣的風(fēng)趣幽默,一樣的social化語言,深入淺出地將枯燥深?yuàn)W的模電知識(shí)講得簡單易學(xué)。 《新概念模擬電路》內(nèi)容包含了《晶體管》、《負(fù)反饋和運(yùn)算放大器》、《運(yùn)放電路的頻率特性
    發(fā)表于 04-16 13:37

    深入淺出解析低功耗藍(lán)牙協(xié)議棧

    深入Bluetooth LE協(xié)議棧各個(gè)組成部分之前,我們先看一下Bluetooth LE協(xié)議棧整體架構(gòu)。 如上圖所述,要實(shí)現(xiàn)一個(gè)Bluetooth LE應(yīng)用,首先需要一個(gè)支持Bluetooth
    的頭像 發(fā)表于 04-09 14:49 ?1275次閱讀
    <b class='flag-5'>深入淺出</b>解析低功耗藍(lán)牙協(xié)議棧

    2025 中國華東智能家居創(chuàng)新技術(shù)研討會(huì)現(xiàn)場直擊,高精度算法如何改變生活?--其利天下

    在此次展會(huì)上,我司技術(shù)總監(jiān)馮建武先生帶來了《智能家居的 “心臟” 革命:高精度電機(jī)驅(qū)動(dòng)算法如何重塑未來生活》的演講,深入淺出地闡述了我司目前在無刷馬達(dá)自適應(yīng)算法、FOC控制算法等技術(shù)上的優(yōu)勢,以及在智能家居領(lǐng)域上的發(fā)展戰(zhàn)略。
    的頭像 發(fā)表于 03-30 11:11 ?977次閱讀
    2025 中國華東智能家居創(chuàng)新技術(shù)研討會(huì)現(xiàn)場直擊,高精度算法如何改變生活?--其利天下

    《零基礎(chǔ)開發(fā)AI Agent——手把手教你用扣子做智能體》

    《零基礎(chǔ)開發(fā)AI Agent——手把手教你用扣子做智能體》是一本為普通人量身打造的AI開發(fā)指南。它不僅深入淺出地講解了Agent的概念和發(fā)展,還通過詳細(xì)的工具介紹和實(shí)戰(zhàn)案例,幫助讀者快速掌握
    發(fā)表于 03-18 12:03