Modelul Strategie cu Angular și Typescript
Aș dori să implementez tiparul Strategy în Angular/Typescript și să folosesc un serviciu într-un component; serviciul primește o interfață de strategie ca parametru al constructorului. De asemenea, constrângerea este că serviciul depinde de un alt serviciu injectat la nivel Angular.
Răsfoiesc documentația, dar nu reușesc să găsesc o modalitate de a face acest lucru. Aș prefera să nu ajung la un cod suprainginerizat, căutând o soluție simplă pentru a implementa un pattern de strategie.
Vă rugăm să vedeți mai jos în codul simulat:
export interface IStrategy {
calculate(a,b): number;
}
export class MinusStrategy implements IStrategy {
calculate(a,b): number {
return a - b;
}
}
export class PlusStrategy implements IStrategy {
calculate(a,b): number {
return a + b;
}
}
@Injectable()
export class CalculatorService {
// I understand it is not possible to have DI of the Interface here for calcStrategy
constructor(private http: HttpClient; private calcStrategy: IStrategy);
getByCalc() {
this.http.get('someurl?calc=' + calcStrategy.calculate(1,1));
}
}
@Component(// skipped config here...)
export class MyComponent implements OnInit {
// How to inject appropriate concrete strategy (and either autowire or provide httpClient?)
constructor(private service: new CalculatorService(httpClient, new MinusStrategy()));
useInComponent() {
this.service.getByCalc();
}
}
Răspuns #1
Opinia mea - Nu te poți baza pe DI pentru a furniza o instanță concretă în astfel de cazuri. DI nu are cum să știe ce tip de instanță este necesar în fiecare context.
Aș sugera să folosești modelul factory aici. De exemplu -
@Injectable()
export class StrategyFactory {
createStrategy<T extends IStrategy>(c: new () => T): T {
return new c();
}
}
//then you can inject StrategyFactory in your service class, use it like -
factory.createStrategy(MinusStrategy).calculate(2, 1);
Răspunsul #2
Trebuie să folosiți o clasă abstractă în loc de interfață pentru „IStrategy”. Pentru că Angular nu suportă o interfață ca token pentru injecție. ( https://angular.io/guide/dependency-injection-providers#non-class-dependencies ). După aceea, puteți defini în providers ai modulului astfel
{ provide: IStrategy, useClass: MinusStrategy }
După aceea, CalculatorService va folosi MinusStrategy pentru a se injecta în orice componentă din acel modul care a injectat serviciul.
export abstract class IStrategy {
abstract calculate(a,b): number;
}
export class PlusStrategy extends IStrategy {
calculate(a,b): number {
return a + b;
}
}
export class MinusStrategy extends IStrategy {
calculate(a,b): number {
return a - b;
}
}
@Injectable({
providedIn: 'root',
})
export class CalculatorService {
constructor(
private http: HttpClient,
private calcStrategy: IStrategy) {};
getByCalc() {
console.log(`Result is: ${this.calcStrategy.calculate(1,1)}`);
}
}
//The module need to add token to providers for Strategy classes.
@NgModule({
declarations: [
...
],
imports: [
...
],
providers: [
{ provide: IStrategy, useClass: MinusStrategy }
],
bootstrap: [...]
})
export class AppModule { }
Rețineți că păstrez numele „IStrategy” pentru clasa abstractă, de exemplu. Ar trebui să fie „BaseStrategy” sau altceva.
============================================================
[ 07/12/2020 17:00:00 GMT+7 ]
Am creat un proiect demo și mi-am actualizat abordarea urmând această problemă. Ai putea să îl deschizi și să vezi din nou abordarea mea.
Link Stackblitz: https://stackblitz.com/github/sangnt-developer/demo-injection-in-component-level
Link Github: https://github.com/sangnt-developer/demo-injection-in-component-level
