Поиск сервисных реализаций динамически - PullRequest
1 голос
/ 19 февраля 2020

Наше приложение Angular имеет сервис, который может иметь различные реализации, иначе. «стратегия». Для простоты, давайте предположим, что это услуга «сообщения о событиях». Служба отчетов о событиях имеет функцию для сообщения информации. В зависимости от предпочтения времени выполнения, отчетность может быть либо отключена, go для console, либо отправлена ​​в REST API, либо go где-то еще.

При использовании чистого TS это будет выглядеть как:

interface IReporter {
  report (message: string): void;
}

class NoReporter implements IReporter {
  report (message: string): void {
    // no op.
  }
}

class ConsoleReporter implements IReporter {
  report (message: string): void {
    console.log(message);
  }
}

// etc.

В потребителе я бы поставил некоторую фабрику, которая доставляет требуемый репортер, например, на основе настройки предпочтений пользователя:

getReporter (key: string): IReporter {
  switch (key) {
    // … tedious cases, which need to be mainted. Yikes!
  }
}

Однако это не так очень «угловой» -подобный Первым шагом, вероятно, было бы предоставление надлежащих Angular услуг отдельным репортерам, но это само по себе кажется малоприобретенным. Итак: существует ли какой-то существующий механизм, который дал бы мне над фабричной функциональностью динамический поиск реализации? Какие-либо образцы, примеры или лучшие практики для подражания?

1 Ответ

2 голосов
/ 19 февраля 2020

Как упомянуто в комментарии FactoryProviders прекрасно решают эту проблему.

Вы должны определить свой IReporter интерфейс как интерфейс класса , потому что интерфейсы этого не делают существуют во время выполнения и, следовательно, они не являются действительными токенами DI:

export abstract class IReporter {
  report: (message: string) => void;
}

@Injectable()
export class NoReporter implements IReporter{
  report(message:string):void{

  }
}

@Injectable()
export class ConsoleReporter implements IReporter {
  report(message: string): void {
    console.log(message);
  }
}

Теперь вы можете создать свои reporterFactory и reporterProvider, как показано ниже:

const reporterFactory = (key: string) => {
  switch (key) {
    case 'no-report':
      return () => new NoReporter();
    case 'console-report':
      return () => new ConsoleReporter();
    default:
      return () => new ConsoleReporter();
  }
};

export const reporterProvider = (key: string) => {
  return {
    provide: IReporter,
    useFactory: reporterFactory(key)
  }
};

И внедрить репортера в вашем компоненте:

@Component({
  selector: 'test',
  template: `TESTCOMPONENT`,
  providers: [
    reporterProvider('console-report')
  ]
})
export class TestComponent {
  constructor(private reporter: IReporter) {
    reporter.report('it works!!!');
  }

}
...