Service vs. Factory – ein für alle Mal

Warte, was? Ein weiterer Artikel, der die große Frage beantwortet: Service vs. Factory, was soll ich verwenden? Ja, es scheint, dass dies nicht mehr nötig ist, da es eine Menge Ressourcen im Internet gibt, die dieses Thema diskutieren. Es stellt sich heraus, dass diese Frage immer noch jede Woche oder so auf verschiedenen Kanälen auftaucht, und selbst nach dem Lesen der zehn wichtigsten Antworten auf StackOverflow ist es immer noch nicht ganz klar. Abgesehen davon scheint es auch so, dass die aktuellen Ressourcen im Web nicht wirklich die aktuellen Best Practices fördern, insbesondere wenn wir die jüngsten Bewegungen der Webplattform berücksichtigen. ES6 I’m looking at you!

Dieser Artikel erklärt ein für alle Mal den Unterschied zwischen Diensten und Fabriken und warum wir Dienste den Fabriken vorziehen sollten.

Der Unterschied zwischen Diensten und Fabriken

Okay, was ist also der Unterschied zwischen einem Dienst und einer Fabrik in AngularJS? Wie wir alle wissen, können wir einen Dienst wie folgt definieren:

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

.service() ist eine Methode in unserem Modul, die einen Namen und eine Funktion annimmt, die den Dienst definiert. Ziemlich einfach. Sobald der Dienst definiert ist, können wir ihn in andere Komponenten wie Controller, Direktiven und Filter injizieren und verwenden, etwa so:

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

Okay, klar. Jetzt das Gleiche wie eine Fabrik:

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

Auch hier ist .factory() eine Methode auf unserem Modul und sie nimmt auch einen Namen und eine Funktion, die die Fabrik definiert. Wir können das Ding genau so injizieren und verwenden, wie wir es mit dem Dienst getan haben. Was ist nun der Unterschied?

Nun, Sie werden sehen, dass wir statt mit this in der Fabrik zu arbeiten, ein Objektliteral zurückgeben. Warum ist das so? Es stellt sich heraus, dass ein Service eine Konstruktorfunktion ist und eine Factory nicht. Irgendwo tief in der Angular-Welt gibt es diesen Code, der Object.create() mit der Konstruktorfunktion des Dienstes aufruft, wenn dieser instanziiert wird. Eine Factory-Funktion ist aber eigentlich nur eine Funktion, die aufgerufen wird, weshalb wir explizit ein Objekt zurückgeben müssen.

Um das ein wenig zu verdeutlichen, können wir uns einfach den Angular-Quellcode ansehen. So sieht die factory()-Funktion aus:

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

Sie nimmt den Namen und die übergebene Factory-Funktion und gibt im Grunde einen Provider mit dem gleichen Namen zurück, der eine $get-Methode hat, die unsere Factory-Funktion ist. Was hat es also mit diesem Anbieter auf sich? Nun, immer wenn Sie den Injektor nach einer bestimmten Abhängigkeit fragen, fragt er im Grunde den entsprechenden Anbieter nach einer Instanz dieses Dienstes, indem er die $get()-Methode aufruft. Das ist der Grund, warum $get() bei der Erstellung von Providern benötigt wird.

Mit anderen Worten, wenn wir MyService irgendwo injizieren, passiert hinter den Kulissen folgendes:

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

Alles klar, die Factory-Funktionen werden einfach aufgerufen, aber was ist mit dem Service-Code? Hier ist ein weiterer Ausschnitt:

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

Sieh mal, es stellt sich heraus, dass, wenn wir service() aufrufen, es eigentlich factory() aufruft. Aber es übergibt nicht nur unsere Dienstkonstruktorfunktion an die Fabrik. Es wird eine Funktion übergeben, die den Injektor auffordert, ein Objekt mit dem angegebenen Konstruktor zu instanziieren. Mit anderen Worten: Ein Dienst ruft eine vordefinierte Fabrik auf, die als $get()-Methode auf dem entsprechenden Provider landet. $injector.instantiate() ist die Methode, die letztendlich Object.create() mit der Konstruktorfunktion aufruft. Deshalb verwenden wir this in Diensten.

Okay, es stellt sich also heraus, dass, egal was wir verwenden, service() oder factory(), es ist immer eine Fabrik, die aufgerufen wird, die einen Provider für unseren Dienst erstellt. Das bringt uns zu der meist gestellten Frage in der Angular-Geschichte: Welche soll ich verwenden?

Welche soll ich verwenden?

Wenn man diese Frage im Internet stellt, stößt man auf eine Reihe von Artikeln und StackOverflow-Antworten. Die erste ist diese Antwort. Darin heißt es:

„Der Unterschied zwischen Service und Factory ist im Wesentlichen folgender:“

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 + "!"; } }});

Wir wissen jetzt schon, was hinter den Kulissen passiert, aber diese Antwort fügt noch einen weiteren Kommentar hinzu. Sie besagt, dass wir Code ausführen können, bevor wir unser Objektliteral zurückgeben. Das erlaubt uns im Grunde, einige Konfigurationen vorzunehmen oder ein Objekt bedingt zu erstellen oder nicht, was bei der direkten Erstellung eines Dienstes nicht möglich zu sein scheint, weshalb die meisten Ressourcen empfehlen, Fabriken anstelle von Diensten zu verwenden, aber die Argumentation ist nicht nachvollziehbar.

Was wäre, wenn ich Ihnen sagen würde, dass wir genau das Gleiche auch mit Diensten tun können?

Ja, richtig. Ein Dienst ist eine Konstruktorfunktion, aber das hindert uns nicht daran, zusätzliche Arbeit zu leisten und Objektliterale zurückzugeben. In der Tat können Konstruktorfunktionen in JavaScript zurückgeben, was immer sie wollen. Wir können also unseren Service-Code nehmen und ihn so schreiben, dass er im Grunde genau das Gleiche tut wie unsere Factory:

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

Hoppla, und was nun? Wir haben gerade festgestellt, dass es, je nachdem wie wir unsere Dienste schreiben, überhaupt keinen Unterschied mehr zwischen den beiden gibt. Die große Frage bleibt: Welchen sollten wir verwenden?

Services erlauben es uns, ES6-Klassen zu verwenden

Natürlich ist es irgendwie kontraproduktiv, Services auf diese Weise zu schreiben, da sie als Konstruktorfunktion aufgerufen werden, also sollten sie auch wie eine solche verwendet werden. Gibt es dann überhaupt einen Vorteil gegenüber dem anderen? Ja, den gibt es. Es hat sich herausgestellt, dass es tatsächlich besser ist, Dienste zu verwenden, wenn es möglich ist, wenn es um die Migration zu ES6 geht. Der Grund dafür ist einfach, dass ein Dienst eine Konstruktorfunktion ist und eine Fabrik nicht. Wenn wir in ES5 mit Konstruktorfunktionen arbeiten, können wir bei der Migration zu ES6 problemlos ES6-Klassen verwenden.

Zum Beispiel können wir unseren Code nehmen und ihn in ES6 wie folgt umschreiben:

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

Eine ES6-Klasse ist eigentlich nur eine Konstruktorfunktion in ES5. Wir haben darüber in Using ES6 with Angular today geschrieben, wenn Sie diesen Artikel noch nicht gelesen haben, empfehle ich Ihnen, ihn zu lesen.

Bei Factories ist das nicht möglich, weil sie einfach als Funktionen aufgerufen werden. Ich hoffe, dieser Artikel hat alles klar gemacht und ermutigt die Leute, Fabriken nicht über Dienste zu verwenden, wenn sie nicht wissen, was sie verwenden sollen.

Dies und mehr lernen Sie in unserer Angular Master Class!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.