The project address

Inspired by Angular and indiv, a framework I wrote myself

Thank you for using star

github

About Dependency Injection

IOC (Inversion of Control) and DI (Dependency Injection).

Suppose we have a class Human. To instance Human, we need to instance a class Clothes. To instantiate Clothes, we need to instantiate Cloth, instantiate buttons, and so on.

When the requirements reach a certain degree of complexity, we can not dress for a person to start from cloth and buttons to achieve, it is better to put all the requirements into a factory or warehouse, what we need directly from the factory warehouse.

This is where dependency injection comes in. We implement an IOC container (repository) and then take the clothes from the repository and give them to wear as properties.

IOC is a good idea for decoupling. In development, IOC means that you design objects to be controlled by the container, rather than the traditional way of controlling them directly from within. It is very useful in software development, not only in JavaEE, but also in other languages.

It basically boils down to you needing me, the factory dresses me up and sends me to you.

Implement an IOC container

Because the key type is not necessarily a character, and Array traversal is a waste of performance, we chose Map instead of Object and Array as the container’s data structure.

Since the goal was to implement a slob IOC container, two private classes have been written into the Injector class, one for token and dependency and one for token and instantiated dependency instances.

When a dependent instance is needed, the token instance is found in the Map of the instance based on the token. If no token instance is found, the token instance is instantiated from the Map of the dependent class and then put into the Map of the instance.

export class Injector {
  private readonly providerMap: Map<any.any> = new Map();
  private readonly instanceMap: Map<any.any> = new Map();
  public setProvider(key: any, value: any) :void {
    if (!this.providerMap.has(key)) this.providerMap.set(key, value);
  }
  public getProvider(key: any) :any {
    return this.providerMap.get(key);
  }
  public setInstance(key: any, value: any) :void {
    if (!this.instanceMap.has(key)) this.instanceMap.set(key, value);
  }
  public getInstance(key: any) :any {
    if (this.instanceMap.has(key)) return this.instanceMap.get(key);
    return null;
  }
  public setValue(key: any, value: any) :void {
    if (!this.instanceMap.has(key)) this.instanceMap.set(key, value); }}Copy the code

Now I’m going to new, and I’m going to create this container.

export const rootInjector = new Injector();
Copy the code

Implementation services

Ng Injectable was stolen and classes were stored in the container through the class decorator.

export function Injectable() : (_constructor: any) = >any {
  return function (_constructor: any) :any {
      rootInjector.setProvider(_constructor, _constructor);
      return _constructor;
  };
}
Copy the code

A class is used as a token, stored in a provider container, and provided to dependent classes.

Implement annotation-based property injection

Setter injection is not implemented because frameworks such as NG and React inject constructors directly or restrict constructor parameters. In order to be used across frameworks and front and back ends, it is better to use decorators for property injection to minimize invasiveness.

Property decorators and reflections help us do this.

export function Inject() : (_constructor: any, propertyName: string) = >any {
  return function (_constructor: any, propertyName: string) :any {
    const  propertyType: any = Reflect.getMetadata('design:type', _constructor, propertyName);
    const injector: Injector = rootInjector;

    let providerInsntance = injector.getInstance(propertyType);
    if(! providerInsntance) { injector.getProvider(propertyType); providerInsntance =new providerClass();
        injector.setInstance(key, providerInsntance);
    }
    _constructor[propertyName] = providerInsntance;

    return (_constructor as any)[propertyName];
  };
}
Copy the code

GetMetadata (‘design:type’) gets the type of the property and uses it as a token to get an instance from injector.getInstance. If there is one, the property is mapped directly to the found instance. This ensures that we get a singleton every time we use the decorator’s properties.

If no Map is found, go to another Map and instantiate the Map stored in the instance. Since there is no such thing as thread-safe in js single-threaded slacker mode, there is no option to instantiate all dependencies at initialization.

demo

import { Inject, Injectable } from '.. /injector';

@Injectable(a)class Cloth {
    public name: string = 'linen';
}

@Injectable(a)class Clothes {
  @Inject(a)public cloth: Cloth;
}

class Human {
  @Inject(a)public clothes: Clothes;
}

const pepe = new Human();
console.log(pepe);
/ / {
// clothes: {
// cloth: {
// name:
/ /}
/ /}
/ /}
Copy the code

Finally, we can directly new pepe without worrying about pepe’s Clothes and Cloth.

Finally, RXJS is recommended for responsive programming. Ng Dafa good!