L'injection de dépendance n'est pas chose aisée quand on veut instancier des objet par soi même. J'ai trouvé un moyen assez élégant, et j'aimerais bien avoir des avis sur la méthode.
L'idée est d'avoir les éléments suivants :
- Un service
Api
qui injecteHttpClient
et qui sert à formatter les requêtes vers le backend à l'aide d'une méthoderequest()
. - Un service
BiduleService
qui injecteApi
et qui permet de chopper tous les bidules via la méthodegetAll()
. - Un modèle
BiduleModel
instancié pour chaque bidule récupéré parBiduleService
.
La difficulté était donc de pouvoir injecter des services dans ce BiduleService
. En effet, je l'instancie à la main dans le callback de BiduleService.getAll()
et il faudrait alors que j'injecte les services requis dans BiduleService
afin de les passer au constructeur comme on peut le voir dans cet exemple :
// bidule-model.js
class BiduleModel {
constructor (data, api) {
this.data = data;
this.api = api;
}
}
// bidule-service.js
import Api from './api';
import BiduleModel from './bidule-model';
export default class BiduleService {
static inject() { return [Api]; }
constructor(Api) {
this.api = Api;
}
getAll () {
this.api.request('/api/bidules')
.then(bidules => bidules.map(bidule => {
return new BiduleModel(bidule, this.api);
})
}
Le gros problème avec cette approche, c'est que si on veut injecter un autre service plus tard dans BiduleModel
, il faut aller modifier tous les endroits où on l'instancie. Il va falloir donc trouver autre chose.
On va alors utiliser une factory !
Factory
Le modèle devient un service injectable avec une méthode create
qui va s'occuper d'instancier le modèle tout en lui passant automatiquement les services qu'il demande. On remplacera alors le new BiduleModel(data, service1, service2)
par BiduleModelFactory.create(data)
.
Ça va ressembler à ça :
// model.js
class Model {
constructor (data, ...services) {
this.data = data || {};
for (let service of services) {
this[service.constructor.name] = service;
}
}
}
const SERVICES = Symbol();
export default class ModelFactory {
static Model () { return Model; }
constructor (...services) {
this[SERVICES] = services;
}
create (data) {
let Model = this.constructor.Model();
return new Model(data, ...this[SERVICES]);
}
}
// bidule-model.js
import Api from './api';
import ModelFactory from './model';
class BiduleModel extends ModelFactory.Model() {
getTrucs () {
return this.Api.request(`/api/bidules/${this.id}/trucs`);
}
}
export default class BiduleModelFactory extends ModelFactory {
static inject() { return [Api]; }
static Model () { return BiduleModel; }
}
// bidule-service.js
import Api from './api';
import BiduleModelFactory from './bidule-model';
export default class BiduleService {
static inject() { return [Api, BiduleModelFactory]; }
constructor(Api, BiduleModelFactory) {
this.api = Api;
this.BiduleModelFactory = BiduleModelFactory
}
getAll () {
this.api.request('/api/bidules')
.then(bidules => bidules.map(bidule => {
return this.BiduleModelFactory.create(bidule);
});
}
}
Qu'en pensez vous ?