问题
Our Angular application has a service which can have different implementations, aka. “strategies”. For simplicity, let’s assume it’s an “event reporting” service. The event reporting service has a function for reporting information. Depending on a runtime preference, the reporting can either turned off, go to the console
, or be sent to a REST API, or go somewhere else.
Using pure TS, this would look like:
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.
In a consumer, I’d put some factory which delivers the desired reporter, e.g. based on a user-preference setting:
getReporter (key: string): IReporter {
switch (key) {
// … tedious cases, which need to be mainted. Yikes!
}
}
However, this feels not very “Angular”-like. First step would probably be to make proper Angular services of the individual reporters, but this alone feels like little gained. So: Is there some existing mechanism which would give me above factory functionality to dynamically look up an implementation? Any patterns, example or best practices to follow?
回答1:
Like mentioned in the comment FactoryProviders perfectly solve this problem.
You have to define your IReporter
interface as an class interface because interfaces don't exist at runtime and therefore they are no valid DI tokens:
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);
}
}
Now you could create your reporterFactory
and reporterProvider
like shown below:
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)
}
};
And inject a reporter in your component:
@Component({
selector: 'test',
template: `TESTCOMPONENT`,
providers: [
reporterProvider('console-report')
]
})
export class TestComponent {
constructor(private reporter: IReporter) {
reporter.report('it works!!!');
}
}
来源:https://stackoverflow.com/questions/60298855/look-up-service-implementations-dynamically