Service vs Factory – Une fois pour toutes

Attendez, quoi ? Encore un article qui répond à la grande question : Service vs Factory, que dois-je utiliser ? Oui, il semble que cette question ne soit plus nécessaire, puisqu’il existe une tonne de ressources sur internet qui traitent de ce sujet. Il s’avère que cette question revient chaque semaine environ sur différents canaux, et même après avoir lu les dix premières réponses sur StackOverflow, ce n’est toujours pas très clair. Malgré cela, il apparaît également que les ressources actuelles sur le web ne favorisent pas vraiment les meilleures pratiques, surtout si l’on considère les récents mouvements de la plateforme web. ES6 je vous regarde !

Cet article explique une fois pour toutes la différence entre les services et les usines et pourquoi nous voulons préférer les services aux usines.

La différence entre les services et les usines

Ok, alors quelle est la différence entre un service et une usine dans AngularJS ? Comme nous le savons tous, nous pouvons définir un service comme ceci :

app.service('MyService', function () { this.sayHello = function () { console.log('hello'); };});

.service() est une méthode sur notre module qui prend un nom et une fonction qui définit le service. C’est assez simple. Une fois défini, nous pouvons injecter et utiliser ce service particulier dans d’autres composants, comme les contrôleurs, les directives et les filtres, comme ceci:

app.controller('AppController', function (MyService) { MyService.sayHello(); // logs 'hello'});

Ok, clair. Maintenant, la même chose qu’une usine:

app.factory('MyService', function () { return { sayHello: function () { console.log('hello'); } }});

Encore, .factory() est une méthode sur notre module et elle prend aussi un nom et une fonction, qui définit l’usine. Nous pouvons injecter et utiliser cette chose exactement de la même manière que nous l’avons fait avec le service. Maintenant, quelle est la différence ici ?

Eh bien, vous pourriez voir qu’au lieu de travailler avec this dans la fabrique, nous retournons un objet littéral. Pourquoi cela ? Il s’avère qu’un service est une fonction de construction alors qu’une usine ne l’est pas. Quelque part au fond de ce monde Angular, il y a ce code qui appelle Object.create() avec la fonction constructeur du service, quand il est instancié. Cependant, une fonction de fabrique est vraiment juste une fonction qui est appelée, ce qui explique pourquoi nous devons retourner un objet explicitement.

Pour rendre cela un peu plus clair, nous pouvons simplement jeter un coup d’œil au code source d’Angular. Voici à quoi ressemble la fonction factory():

function factory(name, factoryFn, enforce) { return provider(name, { $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn });}

Elle prend le nom et la fonction d’usine qui est passée et retourne essentiellement un fournisseur avec le même nom, qui a une méthode $get qui est notre fonction d’usine. Alors c’est quoi cette histoire de provider ? Eh bien, chaque fois que vous demandez à l’injecteur une dépendance spécifique, il demande essentiellement au fournisseur correspondant une instance de ce service, en appelant la méthode $get(). C’est pourquoi $get() est nécessaire, lors de la création de providers.

En d’autres termes, si nous injectons MyService quelque part, ce qui se passe en coulisse est:

MyServiceProvider.$get(); // return the instance of the service

D’accord, les fonctions de la fabrique sont juste appelées, qu’en est-il du code du service ? Voici un autre extrait:

function service(name, constructor) { return factory(name, );}

Oh regardez, il s’avère que lorsque nous appelons service(), il appelle en fait factory(). Mais il ne passe pas simplement notre fonction de constructeur de service à la fabrique telle quelle. Il passe une fonction qui demande à l’injecteur d’instancier un objet par le constructeur donné. En d’autres termes : un service appelle une factory prédéfinie, qui se retrouve comme méthode $get() sur le provider correspondant. $injector.instantiate() est la méthode qui appelle finalement Object.create() avec la fonction constructeur. C’est pourquoi nous utilisons this dans les services.

Ok, donc il s’avère que, peu importe ce que nous utilisons, service() ou factory(), c’est toujours une fabrique qui est appelée qui crée un fournisseur pour notre service. Ce qui nous amène à la question la plus posée dans l’histoire d’Angular : Lequel dois-je utiliser ?

Lequel utiliser ?

Poser cette question sur internet nous amène à un couple d’articles et de réponses StackOverflow. La première est cette réponse. Elle dit :

« Fondamentalement, la différence entre le service et la fabrique est la suivante : »

app.service('myService', function() { // service is just a constructor function // that will be called with 'new' this.sayHello = function(name) { return "Hi " + name + "!"; };});app.factory('myFactory', function() { // factory returns an object // you can run some code before return { sayHello : function(name) { return "Hi " + name + "!"; } }});

Nous savons maintenant déjà ce qui se passe en coulisses, mais cette réponse ajoute un autre commentaire. Elle dit que nous pouvons exécuter du code avant de retourner notre objet littéral. Cela nous permet essentiellement de faire des trucs de configuration ou de créer conditionnellement un objet ou non, ce qui ne semble pas être possible lors de la création directe d’un service, ce qui explique pourquoi la plupart des ressources recommandent d’utiliser des usines plutôt que des services, mais le raisonnement est inappréciable.

Et si je vous disais que nous pouvons faire exactement la même chose avec des services aussi ?

Vraiment, exact. Un service est une fonction constructeur, cependant, cela ne nous empêche pas de faire un travail supplémentaire et de retourner des littéraux d’objet. En fait, les fonctions constructrices en JavaScript peuvent retourner ce qu’elles veulent. Nous pouvons donc prendre notre code de service et l’écrire de manière à ce qu’il fasse exactement la même chose que notre factory:

app.service('MyService', function () { // we could do additional work here too return { sayHello: function () { console.log('hello'); }; }});

Hoppla, et maintenant ? Nous venons de nous rendre compte que, selon la façon dont nous écrivons nos services, il n’y a plus du tout de différence entre les deux. La grande question demeure : Laquelle devons-nous utiliser ?

Les services nous permettent d’utiliser des classes ES6

Bien sûr, écrire les services de cette manière est en quelque sorte contre productif, puisque c’est appelé comme une fonction constructeur, donc il faut aussi l’utiliser comme telle. Y a-t-il un avantage quelconque sur l’autre alors ? Oui, il y en a un. Il s’avère qu’il est en fait préférable d’utiliser des services lorsque cela est possible, lorsqu’il s’agit de migrer vers ES6. La raison en est simplement qu’un service est une fonction de construction et qu’une fabrique ne l’est pas. Travailler avec des fonctions de constructeur dans ES5 nous permet d’utiliser facilement les classes ES6 lorsque nous migrons vers ES6.

Par exemple, nous pouvons prendre notre code et le réécrire dans ES6 comme ceci:

class MyService { sayHello() { console.log('hello'); }}app.service('MyService', MyService);

Une classe ES6 est en réalité juste une fonction de constructeur dans ES5. Nous avons écrit à ce sujet dans Utiliser ES6 avec Angular aujourd’hui, si vous n’avez pas encore lu cet article, je vous recommande de le consulter.

Avec les fabriques, ce n’est pas possible car elles sont simplement appelées comme des fonctions. J’espère que cet article a tout clarifié et encourage les gens à ne pas utiliser les fabriques plutôt que les services, s’ils ne savent pas quoi utiliser.

Ceci et plus encore, vous l’apprenez dans notre Angular Master Class !

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.