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 inInstantiationService
acessor
createChild
This method accepts a set of dependencies and creates a newInstantiationService
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😁
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。