1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        面試官問(wèn)我如何理解 IOC 和 DI

        共 5414字,需瀏覽 11分鐘

         ·

        2021-08-16 10:52

        作者:wscats
        來(lái)源:SegmentFault 思否社區(qū)

        IOC/DI

        談到依賴注入,必須先理解 IOC 與 DI。

        • IOC,全稱 Inversion Of Control,控制反轉(zhuǎn)是面向?qū)ο缶幊痰囊环N設(shè)計(jì)思想,主要用來(lái)降低代碼之間的耦合度。

        • DI,全稱 Dependency Injection,依賴注入是 IOC 的具體實(shí)現(xiàn)。是指對(duì)象通過(guò)外部的注入,避免對(duì)象內(nèi)部自身實(shí)現(xiàn)外部依賴的實(shí)例化過(guò)程。


        IOC 控制反轉(zhuǎn)的設(shè)計(jì)模式可以大幅度地降低了程序的耦合性。而 裝飾器在 VSCode 的控制反轉(zhuǎn)設(shè)計(jì)模式里,其主要作用是實(shí)現(xiàn) DI 依賴注入的功能和精簡(jiǎn)部分重復(fù)的寫法。由于該步驟實(shí)現(xiàn)較為復(fù)雜,我們先從簡(jiǎn)單的例子為切入點(diǎn)去了解裝飾器的基本原理。

        Implementation

        @serviceA 和 @serviceB 是參數(shù)裝飾器,用于處理參數(shù),是由 createDecorator 方法創(chuàng)建的。
        • @參數(shù)裝飾器使用方法:接收三個(gè)參數(shù)

          • target: 對(duì)于靜態(tài)成員來(lái)說(shuō)是類的構(gòu)造器,對(duì)于實(shí)例成員來(lái)說(shuō)是類的原型鏈

          • key: 方法的名稱,注意是方法的名稱,而不是參數(shù)的名稱

          • index: 參數(shù)在方法中所處的位置的下標(biāo)

        • @返回:返回的值將會(huì)被忽略

        class C {
          constructor(@serviceA private a: A, @serviceB private b: B) {}
        }
        所有參數(shù)裝飾器均由 createDecorator 方法創(chuàng)建,'A' 和 'B',均是該裝飾器的唯一標(biāo)識(shí)。
        const serviceA = createDecorator("A");
        const serviceB = createDecorator("B");
        裝飾器首先判斷是否被緩存,如果有被緩存則取出已經(jīng)緩存好的參數(shù)裝飾器,如果沒被緩存,則創(chuàng)建一個(gè) serviceIdentifier 的參數(shù)裝飾器。
        function createDecorator<T>(serviceId: string): ServiceIdentifier<T> {
          if (_util.serviceIds.has(serviceId)) {
            return _util.serviceIds.get(serviceId) as ServiceIdentifier<T>;
          }
        }
        serviceIdentifier 參數(shù)裝飾器只做了一件事就是觸發(fā) storeServiceDependency 把所有依賴項(xiàng)給存起來(lái),存裝飾器本身 id,參數(shù)的下標(biāo) index 以及是否可選 optional。
        const id = function serviceIdentifier(target: Ctor<T>, key: string, index: number): void {
          storeServiceDependency(id, target, index, false);
        };
        id.toString = () => serviceId;
        _util.serviceIds.set(serviceId, id);
        storeServiceDependency 本質(zhì)是往 target 即 class C 上設(shè)置兩個(gè)靜態(tài)屬性 $di$target 和 $di$dependencies 上面分別存 target,自身還要再存一次自身 target 是為了判斷是否已經(jīng)存過(guò)依賴。
        C.$di$target; // class C
        C.$di$dependencies[0].id.toString(); // A 或者 B
        C.$di$dependencies; // [{id: serviceIdentifier, index: 1, optional: false}, {id: serviceIdentifier, index: 0, optional: false}]
        除了存在類上,還存在了 _util.serviceIds 上。
        當(dāng)類聲明的時(shí)候,裝飾器就會(huì)被應(yīng)用,所以在有類被實(shí)例化之前依賴關(guān)系就已經(jīng)確定好了。把 ts 編譯就可以證明這點(diǎn),可以看到 __decorate 在類聲明的時(shí)候,裝飾器就會(huì)被執(zhí)行了,
        var C = /** @class */ (function() {
          function C(a, b) {
            this.a = a;
            this.b = b;
          }
          C = __decorate([__param(0, serviceA), __param(1, serviceB)], C);
          return C;
        })();
        緊接著就到了 ServiceCollection,這里會(huì)將裝飾器作為 key 唯一標(biāo)識(shí),實(shí)例化的類作為 value,全部存到 svrsCollection 中,svrsCollection 的實(shí)現(xiàn)也很簡(jiǎn)單,直接用 Map 方法存起來(lái)。
        const aInstance = new A();
        const bInstance = new B();
        const svrsCollection = new ServiceCollection();
        svrsCollection.set(serviceA, aInstance);
        svrsCollection.set(serviceB, bInstance);
        后續(xù)只需要使用 get 方法并傳入對(duì)應(yīng)的參數(shù)裝飾器就可以獲取對(duì)應(yīng)的實(shí)例化好的類了。
        svrsCollection.get(serviceA); // new A()
        svrsCollection.get(serviceB); // new B()
        InstantiationService 是實(shí)現(xiàn)依賴注入的核心,它是以參數(shù)裝飾器,例如 serviceA 和 serviceB 等 ServiceIdentifier 為 key 在私有變量 services 中保存所有依賴注入的被實(shí)例化好的類。services 保存的是 svrsCollection。
        const instantiationService = new InstantiationService(svrsCollection);
        它暴露了三個(gè)公開方法:
        • createInstance 該方法接受一個(gè)類以及構(gòu)造該類的非依賴注入?yún)?shù),然后創(chuàng)建該類的實(shí)例。

        • invokeFunction 該方法接受一個(gè)回調(diào)函數(shù),該回調(diào)函數(shù)通過(guò) acessor 參數(shù)可以訪問(wèn)該 InstantiationService 中的所有依賴注入項(xiàng)。

        • createChild 該方法接受一個(gè)依賴項(xiàng)集合,并創(chuàng)造一個(gè)新的 InstantiationService 說(shuō)明 vscode 的依賴注入機(jī)制也是有層次的。

        createInstance 方法是實(shí)例化的核心方法:
        const cInstance = instantiationService.createInstance(C, "L""R") as C;
        首先是獲取 ctorOrDescriptor 也就是類 class C 和需要傳入非依賴注入的參數(shù) rest
        const result = this.createCtorInstance(ctorOrDescriptor, rest);
        然后使用 getServiceDependencies 把掛載 class C 靜態(tài)屬性的 $di$dependencies 給獲取出來(lái)并排序,因?yàn)榇娴臅r(shí)候順序是倒序的
        const serviceDependencies = _util
          .getServiceDependencies(ctor)
          .sort((a, b) => a.index - b.index);
        取出來(lái)的依賴項(xiàng) serviceDependencies 主要是為了遍歷并獲取里面的參數(shù)裝飾器 serviceA 和 serviceB
        const serviceArgs: any[] = [];
        for (const dependency of serviceDependencies) {
          const serviceInstance = this.getOrCreateServiceInstance(dependency.id);
          serviceArgs.push(serviceInstance);
        }
        getOrCreateServiceInstance 本質(zhì)就是從 services 即 svrsCollection 中獲取實(shí)例化好的類。
        const instanceOrDesc = this.services.get(id);
        // 相當(dāng)于 id 即參數(shù)裝飾器
        // svrsCollection.get(id);
        當(dāng)把所有的這些實(shí)例化好的類取出來(lái)放到 serviceArgs 中后,由于參數(shù)裝飾器是類實(shí)例化的時(shí)候就執(zhí)行完并收集好依賴項(xiàng),所以 serviceArgs 就是對(duì)應(yīng) ctor 即 class C 需要注入的依賴參數(shù),合并非依賴參數(shù)就能幫助我們成功實(shí)例化好我們的 ctor 類。
        new ctor(...[...serviceArgs, ...args]);



        點(diǎn)擊左下角閱讀原文,到 SegmentFault 思否社區(qū) 和文章作者展開更多互動(dòng)和交流,掃描下方”二維碼“或在“公眾號(hào)后臺(tái)回復(fù)“ 入群 ”即可加入我們的技術(shù)交流群,收獲更多的技術(shù)文章~

        - END -


        瀏覽 41
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            久一区二区三区 | 91精品人妻偷拍 | free嫩白的1819性hd | 爆操黑丝美女 | 国产精品电影院 | 乐吧成人门户总站官网 | 国产精品第一区 | 久久小黄片 | 日韩久久草 | 韩国黄色一级大片 |