Angular 와 유사 한 의존 주입 을 간단하게 실현 합 니 다.

주입 에 의존 하 는 것 은 매우 중요 한 디자인 모델 이다.그것 은 거의 모든 사람들 이 그것 을 DI 라 고 부 를 정도 로 광범 위 하 게 사용 된다.
Angular 는 자신의 의존 주입 프레임 워 크 가 있 습 니 다. 이 프레임 워 크 를 떠 나 면 Angular 응용 프로그램 을 구축 할 수 없습니다.
다음은 Angular 의존 주입 과 같은 간단 한 (100 줄 정도 코드 만 있 음) 방식 을 소개 합 니 다. 먼저 예 를 들 어 보 겠 습 니 다.
// a.service.ts
@Injectable()
export class AService {

  constructor() {

  }

  public doSomething() {
    console.log('this is AService::doSomething');
  }

}

// b.service.ts
@Injectable()
export class BService {

  constructor(private readonly a: AService) {

  }

  public doSomething() {
    this.a.doSomething();
    console.log('this is BService::doSomething');
  }

}

// some.module.ts
@Module({
  providers: [
    AService,
    BService,
  ],
})
export class SomeModule {

}

위의 예 에서 우 리 는 두 개 Service 를 만 들 었 는데 그 중에서 BServiceAService 에 의존 했다. 그러면 BService 구조 함수 에서 그 의존 을 설명 할 수 있다. 우 리 는 AService 의 인 스 턴 스 를 BService 의 개인 읽 기 전용 변수 a 에 자동 으로 주입 하 는 방법 이 필요 하 다. 다음 에 실현 절 차 를 소개 한다.
우선 우 리 는 Typescript 의 장식 기 (또는 주석) 를 지원 하도록 설정 해 야 합 니 다. 다음은 제 설정 파일 입 니 다.
{
  "compilerOptions": {
    "lib": [
      "dom",
      "es2015"
    ],
    "target": "es5",
    "module": "commonjs",
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

그 중에서 Javascript 는 ES 장식 기 에 실험 지원 을 사용 하 는 것 을 나타 낸다. experimentalDecorators 는 소스 코드 에서 장식 성명 을 위해 유형의 메타 데 이 터 를 만 들 었 다 고 밝 혔 다. 이 두 가 지 를 설정 한 후에 야 우 리 는 emitDecoratorMetadata 장식 기 를 사용 할 수 있다. 그 다음 에 우 리 는 의존 Typescript 을 설치 하여 메타 데 이 터 를 읽 고 설정 해 야 한다.
@ Injectable 의 실현reflect-metadata 은 장식 기구 로 표지 가 장식 되 어 있 는 유형 은 @Injectable 이 고 그의 성명 방식 은 다음 과 같다.
export function Injectable(): ClassDecorator {
  return (target) => {

  };
}

우 리 는 이 장식 기 에서 아무것도 하지 않 고 그 는 표지 의 역할 만 한다.
@ Module 의 실현Provider 은 장식 기구 로 표지 가 장식 되 어 있 는 유형 은 @Module 이 고 그의 성명 방식 은 다음 과 같다.
const DI_IMPORTS_SYMBOL = Symbol('di:imports')
const DI_PROVIDERS_SYMBOL = Symbol('di:providers')

export function Module(options: { imports?: Array, providers?: Array }): ClassDecorator {
  return (target) => {
    Reflect.defineMetadata(DI_IMPORTS_SYMBOL, new Set(options.imports || []), target);
    Reflect.defineMetadata(DI_PROVIDERS_SYMBOL, new Set(options.providers || []), target);
  }
}

우 리 는 Module 역할 영역 에서 설명 한 Set 과 그 가 도입 한 다른 모듈 을 저장 하기 위해 Module 를 사용 합 니 다.
Factory 의 실현
우리 가 달성 하고 자 하 는 목적 은 사용 할 때 Providers 를 통 해 Factory.create(SomeModule) 의 인 스 턴 스 를 얻 은 다음 Module 인 스 턴 스 를 통 해 Module 를 얻 는 것 이다. 예 를 들 어 Provider 이 때 출력 해 야 한다.
Factory.create(SomeModule).get(BService).doSomething();
// this is AService::doSomething
// this is BService::doSomething

Talk is cheap. Show me the code:
export namespace Factory {

  export function create(module: Type) {
    const imports: Set = Reflect.getMetadata(DI_IMPORTS_SYMBOL, module);
    const providers: Set = Reflect.getMetadata(DI_PROVIDERS_SYMBOL, module);
    const providersMap = new Map();

    const importModules = Array.from(imports).map((importModule) => {
      let moduleInstance: ModuleInstance = moduleInstances.get(importModule);
      if(!moduleInstance) {
        moduleInstance = create(importModule);
        moduleInstances.set(importModule, moduleInstance);
      }
      return moduleInstance;
    });
    const moduleInstance = new ModuleInstance(importModules, providersMap);

    providers.forEach(provider => {
      createProvider(provider, providers, moduleInstance);
    });
    return moduleInstance;
  }

  function createProvider(provider: any, providers: Set, moduleInstance: ModuleInstance) {
    let providerInstance = moduleInstance.providers.get(provider);

    if(providerInstance) {
      return providerInstance;
    }

    const deps: Array = Reflect.getMetadata('design:paramtypes', provider);
    if(!deps) {
      throw new Error(`No provider named ${ provider.name }, do yout add @Injectable() to this provider?`);
    }

    const args = deps.map(dep => {
      let depInstance = moduleInstance.providers.get(dep);
      if(!depInstance) {
        if(providers.has(dep)) {
          depInstance = createProvider(dep, providers, moduleInstance);
        } else {
          moduleInstance.imports.some(imp => {
            depInstance = createProvider(dep, new Set(), imp);
            return !!depInstance;
          });
        }
      }
      if(!depInstance) {
        throw new Error(`can not found provider ${ dep.name }`);
      }
      return depInstance;
    });
    providerInstance = new provider(...args);
    moduleInstance.providers.set(provider, providerInstance);
    return providerInstance;
  }

  export class ModuleInstance {

    constructor(
        public imports: Array,
        public providers: Map) {

    }

    get(provider: Type) {
      let instance: T = this.providers.get(provider);
      if(!instance) {
        this.imports.some(imp => {
          instance = imp.get(provider);
          return !!instance;
        });
      }
      if(!instance) {
        throw new Error(`No provider named: ${ provider.name }`);
      }
      return instance;
    }
  }

}

이상 은 전체 의존 주입 의 실현 입 니 다. 관심 이 있 는 친 구 는 제 Github 에서 소스 코드 를 볼 수 있 습 니 다. 핵심 파일 은 Factory.create(SomeModule).get(BService).doSomething() 입 니 다. 주 소 는?https://github.com/hungtcs/li...

좋은 웹페이지 즐겨찾기