Дюжина строк кода для реализации внедрения зависимостей ts

JavaScript

адрес проекта

Вдохновленный angular и фреймворком, который я написал самindiv

Спасибо за использование звезды большими парнями

github

О внедрении зависимостей

IOC (инверсия управления) означает инверсию управления, а DI (внедрение зависимостей) означает внедрение зависимостей.

Предположим, у нас есть классHuman, чтобы создать экземплярHuman, нам нужно создать экземпляр классаClothes. при создании экземпляра одеждыClothes, нам снова нужно создать экземпляр тканиCloth, созданные кнопки и т. д.

Когда спрос достигает определенного уровня сложности, мы не можем начать с ткани с пуговицы, чтобы человек носил одежду.Лучше поставить все требования на фабрике или складе.То, что нам нужно, прямо со склада завода.

В это время нам нужна инъекция зависимости. Мы реализуем контейнер IOC (склад), а затем нам нужно взять одежду со склада и поставить их в качестве атрибутов.

IOC — хорошая идея разделения.В разработке IoC означает, что объекты, которые вы проектируете, передаются контейнеру для управления вместо использования традиционного метода прямого управления объектами. Он играет очень важную роль в разработке программного обеспечения не только для JavaEE, но и для других языков.

На самом деле, резюме, вероятно, таково, что если я вам понадоблюсь, фабрика оденет меня и отправит вас найти.

Реализовать IOC-контейнер

Поскольку тип ключа не обязательно является символьным типом, а обход массива снижает производительность, Object и Array не выбираются, а Map выбирается в качестве структуры данных контейнера.

Поскольку цель состоит в том, чтобы реализовать контейнер IOC, который может ленивоInjectorВ нем написано два закрытых класса: один для хранения токенов и зависимостей, а другой для хранения токенов и созданных экземпляров зависимостей.

Когда требуется зависимый экземпляр, сначала перейдите на Карту экземпляра, чтобы найти экземпляр соответствующего токена по токену, если нет, выньте его из Карты, в которой хранится зависимый класс, и создайте его экземпляр, а затем поместите его в карту, в которой хранится экземпляр.

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

Теперь нажмите «Создать», и контейнер будет создан.

export const rootInjector = new Injector();

внедрить сервис

тайно позаимствовал нгInjectable, сохраните класс в контейнере через декоратор класса.

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

Используйте класс в качестве маркера, сохраните класс в контейнере провайдера и предоставьте его классам, от которых необходимо зависеть.

Реализовать внедрение свойств на основе аннотаций

Причина, по которой внедрение конструктора и внедрение сеттера не реализовано, заключается в том, что такие фреймворки, как ng и react, будут напрямую внедрять конструктор или ограничивать параметры конструктора Inject для минимизации инвазивности.

В этом нам могут помочь декораторы свойств и отражение.

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

Использование метаданных ReflectReflect.getMetadata('design:type')Получить тип атрибута и перейти в качестве токенаinjector.getInstanceЗапросите соответствующий экземпляр и напрямую сопоставьте атрибут с найденным экземпляром, если он есть. Это гарантирует, что мы получим синглтон каждый раз, когда используем свойство декоратора.

Если он не запрошен, перейдите к другой карте, чтобы удалить зависимости и создать экземпляр карты, хранящейся в экземпляре. Поскольку в однопоточном ленивом режиме js нет потокобезопасности, все зависимости создаются, когда инициализация не выбрана.

demo

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

@Injectable()
class Cloth {
    public name: string = '麻布';
}

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

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

const pepe = new Human();
console.log(pepe);
// {
//   clothes: {
//      cloth: {
//        name: '麻布'
//      }
//   }
//}

Наконец, мы можем напрямую создать новый Пепе, нам не нужно заботиться об одежде, которую носит Пепе.Clothesи ткань для одеждыCloth.

Последняя рекомендуемая комбинацияrxjsЗаймитесь реактивным программированием.Дафа несёт добро!