Service vs. fabrik – én gang for alle

Hvad er det? Endnu en artikel, der besvarer det store spørgsmål: Service vs Factory, hvad skal jeg bruge? Ja, det ser ud til at det ikke længere er nødvendigt, da der findes et væld af ressourcer på internettet, der diskuterer dette emne. Det viser sig, at dette spørgsmål stadig dukker op hver uge eller deromkring på forskellige kanaler, og selv efter at have læst de ti bedste svar på StackOverflow, er det stadig ikke særlig klart. På trods af det viser det sig også, at de nuværende ressourcer på nettet ikke rigtig fremmer den egentlige bedste praksis, især hvis vi tager de seneste bevægelser på webplatformen i betragtning. ES6 I’m looking at you!

Denne artikel forklarer én gang for alle forskellen mellem services og factories, og hvorfor vi vil foretrække services frem for factories.

Skellen mellem services og factories

Okay, så hvad er forskellen mellem en service og en factory i AngularJS? Som vi alle ved, kan vi definere en tjeneste på denne måde:

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

.service() er en metode på vores modul, der tager et navn og en funktion, der definerer tjenesten. Ganske ligetil. Når den er defineret, kan vi injicere og bruge den pågældende tjeneste i andre komponenter, f.eks. controllere, direktiver og filtre, på denne måde:

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

Okay, forstået. Nu det samme som en factory:

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

Og igen, .factory() er en metode på vores modul, og den tager også et navn og en funktion, der definerer fabrikken. Vi kan injicere og bruge den på nøjagtig samme måde, som vi gjorde med tjenesten. Hvad er forskellen her?

Du kan måske se, at vi i stedet for at arbejde med this i fabrikken returnerer vi en objektlitteratur i stedet for at arbejde med this i fabrikken. Hvorfor er det sådan? Det viser sig, at en service er en konstruktørfunktion, mens en factory ikke er det. Et eller andet sted dybt inde i denne Angular-verden er der denne kode, der kalder Object.create() med servicekonstruktorfunktionen, når den bliver instantieret. Men en fabriksfunktion er i virkeligheden bare en funktion, der bliver kaldt, og det er derfor, vi er nødt til at returnere et objekt eksplicit.

For at gøre det lidt mere klart kan vi blot tage et kig på Angular-kildekoden. Sådan ser factory()-funktionen ud:

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

Den tager navnet og den fabriksfunktion, der er overgivet, og returnerer dybest set en provider med samme navn, der har en $get-metode, som er vores fabriksfunktion. Så hvad er det med denne provider-ting? Jo, når du beder injektoren om en bestemt afhængighed, spørger den grundlæggende den tilsvarende provider om en instans af den pågældende tjeneste ved at kalde $get()-metoden. Det er derfor $get() er påkrævet, når man opretter providers.

Med andre ord, hvis vi injicerer MyService et sted, sker der følgende bag kulisserne:

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

Okay, fabriksfunktionerne bliver bare kaldt, hvad med servicekoden? Her er et andet uddrag:

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

Oh se, det viser sig, at når vi kalder service(), kalder den faktisk factory(). Men den giver ikke bare vores servicekonstruktørfunktion videre til fabrikken som den er. Den videregiver en funktion, der beder injektoren om at instantiere og objekt ved hjælp af den givne konstruktør. Med andre ord: En tjeneste kalder en foruddefineret fabrik, som ender som $get()-metode på den tilsvarende provider. $injector.instantiate() er den metode, der i sidste ende kalder Object.create() med konstruktorfunktionen. Det er derfor, vi bruger this i services.

Okay, så det viser sig, at uanset hvad vi bruger, service() eller factory(), er det altid en factory, der kaldes, som skaber en provider for vores service. Hvilket bringer os til det mest stillede spørgsmål i Angular-historien:

Hvilken skal jeg bruge?

Spørge dette spørgsmål på internettet fører os til et par artikler og StackOverflow-svar. Det første er dette svar. Det siger:

“Basically the difference between the service and factory is as follows:”

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

Vi ved nu allerede, hvad der sker bag kulisserne, men dette svar tilføjer endnu en kommentar. Det siger, at vi kan køre kode, før vi returnerer vores objektlitterær. Det giver os dybest set mulighed for at lave nogle konfigurationsting eller betinget oprette et objekt eller ej, hvilket ikke synes at være muligt, når vi opretter en tjeneste direkte, hvilket er grunden til, at de fleste ressourcer anbefaler at bruge factories frem for tjenester, men ræsonnementet er utaknemmeligt.

Hvad nu hvis jeg fortalte dig, at vi kan gøre nøjagtig det samme med tjenester også?

Ja, korrekt. En service er en konstruktørfunktion, men det forhindrer os ikke i at lave yderligere arbejde og returnere objektliteraler. Faktisk kan konstruktorfunktioner i JavaScript returnere hvad de vil. Så vi kan tage vores servicekode og skrive den på en måde, så den stort set gør nøjagtig det samme som vores factory:

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

Hoppla, hvad nu så? Vi har lige indset, at afhængigt af hvordan vi skriver vores services, er der slet ingen forskel på de to længere. Det store spørgsmål er stadigvæk:

Services giver os mulighed for at bruge ES6-klasser

Selvfølgelig er det lidt kontraproduktivt at skrive services på den måde, da det kaldes som en konstruktorfunktion, så det skal også bruges som en sådan. Er der overhovedet nogen fordel i forhold til det andet så? Ja, det er der da. Det viser sig, at det faktisk er bedre at bruge services, hvor det er muligt, når man skal migrere til ES6. Grunden til det er ganske enkelt, at en service er en konstruktørfunktion, og det er en factory ikke. Ved at arbejde med konstruktorfunktioner i ES5 kan vi nemt bruge ES6-klasser, når vi migrerer til ES6.

For eksempel kan vi tage vores kode og omskrive den i ES6 på følgende måde:

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

En ES6-klasse er i virkeligheden bare en konstruktorfunktion i ES5. Det skrev vi om i Brug af ES6 med Angular i dag, hvis du ikke har læst den artikel endnu, vil jeg anbefale at tjekke den ud.

Med factories er dette ikke muligt, fordi de blot kaldes som funktioner. Jeg håber, at denne artikel gjorde alt klart og opfordrer folk til ikke at bruge factories frem for services, hvis de ikke ved, hvad de skal bruge.

Dette og meget mere lærer du i vores Angular Master Class!

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.