2
头图

IOC/DI

When it comes to dependency injection, one must first understand IOC and DI.

  • IOC, full name Inversion Of Control, inversion of control is a design idea of object-oriented programming, mainly used to reduce the coupling between codes.
  • DI, full name Dependency Injection, Dependency Injection is the specific implementation of IOC. It refers to the instantiation process of the object through external injection to avoid the object's internal realization of external dependencies.

The IOC control inversion design pattern can greatly reduce the coupling of the program. In VSCode's Inversion of Control design mode, the decorator's main function is to realize the DI dependency injection function and streamline part of the repetitive writing. Since the implementation of this step is more complicated, let's start with a simple example as a starting point to understand the basic principles of decorators.

Implementation

@serviceA and @serviceB are parameter decorators used to process parameters and are created by the createDecorator method.

  • @Parameter decorator usage: receive three parameters

    • target: For static members, it is the constructor of the class, and for instance members, it is the prototype chain of the class
    • key: the name of the method, pay attention to the name of the method, not the name of the parameter
    • index: the index of the position of the parameter in the method
  • @Return: The returned value will be ignored
class C {
  constructor(@serviceA private a: A, @serviceB private b: B) {}
}

All parameter decorators are created by the createDecorator method. 'A' and 'B' are the unique identifiers of the decorator.

const serviceA = createDecorator("A");
const serviceB = createDecorator("B");

The decorator first judges whether it is cached, if it is cached, the cached parameter decorator is taken out, if it is not cached, a parameter decorator of serviceIdentifier

function createDecorator<T>(serviceId: string): ServiceIdentifier<T> {
  if (_util.serviceIds.has(serviceId)) {
    return _util.serviceIds.get(serviceId) as ServiceIdentifier<T>;
  }
}

serviceIdentifier only thing that the 0611afc0c90b10 parameter decorator does is to trigger storeServiceDependency to save all dependencies, save the decorator itself id , the parameter subscript index and whether it is optional 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 essentially to target i.e. class C disposed on two static properties $di$target and $di$dependencies above are stored target , but also to reload itself again itself target is to determine whether over-dependent memory.

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}]

In addition to the existing class, there is also _util.serviceIds .

When the class is declared, the decorator is applied, so the dependencies are determined before the class is instantiated. This can be proved by compiling ts . It can be seen that the decorator will be executed when the class is declared in __decorate

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;
})();

Then immediately to ServiceCollection , as the decorative key here will be a unique identification, as instantiated class value, to keep all svrsCollection in, svrsCollection achieved is also very simple, direct Map method deposit.

const aInstance = new A();
const bInstance = new B();
const svrsCollection = new ServiceCollection();
svrsCollection.set(serviceA, aInstance);
svrsCollection.set(serviceB, bInstance);

Afterwards, you only need to use the get method and pass in the corresponding parameter decorator to get the corresponding instantiated class.

svrsCollection.get(serviceA); // new A()
svrsCollection.get(serviceB); // new B()

InstantiationService is the core of the implementation of dependency injection, which is a parameter decorator, such as serviceA and serviceB etc. ServiceIdentifier is key in the private variable services to save all the dependency injection instantiated classes. services saves svrsCollection .

const instantiationService = new InstantiationService(svrsCollection);

It exposes three public methods:

  • createInstance This method accepts a class and constructs the non-dependency injection parameters of the class, and then creates an instance of the class.
  • invokeFunction This method accepts a callback function, which can access all dependency injection items in InstantiationService acessor
  • createChild This method accepts a set of dependencies and creates a new InstantiationService show that the dependency injection mechanism of vscode is also hierarchical.

createInstance method is the core method of instantiation:

const cInstance = instantiationService.createInstance(C, "L", "R") as C;

The first is to obtain ctorOrDescriptor which is the class class C rest needs to be passed in for non-dependency injection.

const result = this.createCtorInstance(ctorOrDescriptor, rest);

Then use getServiceDependencies to obtain and sort the class C static properties of $di$dependencies

const serviceDependencies = _util
  .getServiceDependencies(ctor)
  .sort((a, b) => a.index - b.index);

The dependency item serviceDependencies taken out is mainly to traverse and obtain the parameter decorators serviceA and serviceB .

const serviceArgs: any[] = [];
for (const dependency of serviceDependencies) {
  const serviceInstance = this.getOrCreateServiceInstance(dependency.id);
  serviceArgs.push(serviceInstance);
}

getOrCreateServiceInstance essence of services is to get the instantiated class from 0611afc0c90d5d, which is svrsCollection

const instanceOrDesc = this.services.get(id);
// 相当于 id 即参数装饰器
// svrsCollection.get(id);

When all these instantiated classes are taken out and placed in serviceArgs , since the parameter decorator is executed when the class is instantiated and the dependencies are collected, serviceArgs corresponds to ctor , that is, the dependency parameters that need to be injected for class C Combining non-dependent parameters can help us successfully instantiate our ctor class.

new ctor(...[...serviceArgs, ...args]);

comminicate

If the articles and notes can bring you a hint of help or inspiration, please don't be stingy with your likes and favorites. The articles are continuously updated synchronously, and past articles are also included in https://github.com/Wscats/articles
Welcome your attention and communication, yours is definitely my biggest motivation to move forward😁


wscats
7.1k 声望13k 粉丝

分享和总结不易,求关注一下⭐️