Quand on commence à structurer son application Angular, on souhaite profiter de la puissance d’un langage (TypeScript) avec la force du Framework.
Lorsque l’on teste (oui les tests sont importants, essentiels), nous souhaitons pouvoir tester un composant unique (une classe), sans être lié aux dépendances de la classe.
Concrètement ça donne quoi, côté Angular ?
Découplage, le maître mot est découplage
Prenez une classe qui doit calculer des éléments, et qui doit aussi afficher ces éléments sur la console.
Mettons vous avez une méthode calculate(num: number, num2: number).
Premier réflexe ?
> Faire un console.log du résultat.
compute(num: number, num2: number): number {
const result = num * num2;
console.log(result);
return result;
}
Passer du couplage …
Ici, nous avons donc deux classes fortement couplées : la classe de console, avec le Component.
L’une n’allant pas sans l’autre.
OK, que faire alors si :
- on doit changer la méthode pour afficher : ne plus passer par la console ?
- on doit tester le Component, et vérifier si l’on appelle bien la Console ou toute autre méthode d’affichage ?
Au découplage
Angular introduit déjà la notion de découplage avec la notion de Service.
Le tutorial d’Angular nous invite déjà à créer des services, et nous informe que l’on peut utiliser ce que l’on appelle l’injection de dépendances.
Ici, on a bien une classe (le Service) qui est utilisée par une autre classe (le Component).
Et en plus, le Service est instancié depuis un moteur (dans Angular, depuis le Module). Impeccable !
Avec ou sans interface, that’s the question
Pour créer un code testable, fortement découplé, surtout en Javascript, nous ne sommes pas obligés d’utiliser les interfaces.
Les services + L’injection de dépendances nous permettent donc déjà de le faire.
OK, mais TypeScript nous propose la notion d’Interface (qui est très utilisée dans Angular, par exemple, avec OnInit).
Avec les interfaces, nous ajoutons un niveau d’abstraction supplémentaire à notre code.
Ainsi, nous partirons sur l’utilisation des interfaces avec nos services.
Mettre en place un code découplé, avec Interface, et Service, depuis Angular
1. Créer le service de log des informations :
ng generate service Log;
Ajouter lui une méthode :
display(mess: string) {
console.log(mess);
}
2. Créez son interface IDisplayInfo
ng generate interface IDisplayInfo
Ajoutez-lui la déclaration de la méthode : display(mess: string);
3. Implémentez cette interface depuis le Service LogService
export class LogService implements IDisplayInfo {
}
4. Allez dans le module, définissez le lien entre interface à injecter et classe découplée
providers: [
{ provide: 'IDisplayInfo', useClass: LogService }
]
5. Déclarer un attribut privé dans le constructeur de votre Component
@Inject(‘IDisplayInfo’) private __logService: IDisplayInfo
NOTE: Une autre piste aurait été d’utiliser le InjectionToken
Injection d’une classe depuis une déclaration d’interface
Et voilà, le moteur d’injection va détecter qu’il y a une interface, et donc va rechercher la bonne classe à instancier !
And voilà !