Service vs. Factory – Egyszer és mindenkorra

Várj, mi? Egy újabb cikk, amely választ ad a nagy kérdésre: Service vs Factory, mit használjak? Igen, úgy tűnik, erre már nincs is szükség, hiszen rengeteg forrás található az interneten, ami ezt a témát tárgyalja. Kiderült, hogy ez a kérdés még mindig nagyjából hetente felbukkan különböző csatornákon, és még a StackOverflow top tíz válaszának elolvasása után sem egyértelmű. Ennek ellenére az is látszik, hogy a jelenlegi források a weben nem igazán támogatják a tényleges legjobb gyakorlatot, különösen, ha figyelembe vesszük a webes platform közelmúltbeli mozgásait. ES6 I’m looking at you!

Ez a cikk egyszer és mindenkorra elmagyarázza, mi a különbség a szolgáltatások és a gyárak között, és miért akarjuk előnyben részesíteni a szolgáltatásokat a gyárakkal szemben.

A szolgáltatások és gyárak közötti különbség

Oké, szóval mi a különbség egy szolgáltatás és egy gyár között az AngularJS-ben? Mint tudjuk, egy szolgáltatást így definiálhatunk:

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

.service() egy metódus a modulunkon, amely elfogad egy nevet és egy függvényt, amely meghatározza a szolgáltatást. Elég egyszerű. Miután definiáltuk, az adott szolgáltatást más komponensekbe, például vezérlőkbe, direktívákba és szűrőkbe injektálhatjuk és használhatjuk, így:

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

Oké, világos. Most ugyanaz a dolog, mint egy gyár:

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

Az .factory() ismét egy metódus a modulunkon, és ez is elfogad egy nevet és egy függvényt, ami meghatározza a gyárat. Ezt a dolgot pontosan ugyanúgy tudjuk befecskendezni és használni, mint ahogy a szolgáltatással tettük. Most mi a különbség itt?

Hát, láthatod, hogy ahelyett, hogy a this gyárral dolgoznánk, egy objektum literált adunk vissza. Miért van ez így? Kiderül, hogy a szolgáltatás egy konstruktorfunkció, míg a gyár nem az. Valahol mélyen ebben az Angular világban van ez a kód, ami a Object.create()-t hívja a szolgáltatás konstruktor függvénnyel, amikor instanciálódik. A factory függvény azonban valójában csak egy függvény, amit meghívnak, ezért kell explicit módon visszaadnunk egy objektumot.

Hogy ez egy kicsit világosabb legyen, egyszerűen vessünk egy pillantást az Angular forráskódjára. Így néz ki a factory() függvény:

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

Veszi a nevet és az átadott gyári függvényt, és alapvetően egy azonos nevű szolgáltatót ad vissza, amely rendelkezik egy $get metódussal, ami a mi gyári függvényünk. Szóval mi van ezzel a szolgáltató dologgal? Nos, amikor az injektortól egy adott függőséget kérünk, a $get() metódus meghívásával alapvetően a megfelelő szolgáltatótól kér egy példányt az adott szolgáltatásból. Ezért van szükség a $get()-re, amikor a szolgáltatókat létrehozzuk.

Más szóval, ha valahova MyService injektálunk, a színfalak mögött a következő történik:

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

A gyári függvények csak meghívásra kerülnek, mi van a szolgáltatás kódjával? Itt egy másik részlet:

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

Oh nézd, kiderül, hogy amikor meghívjuk a service()-ot, akkor valójában a factory()-et hívja. De nem csak úgy átadja a szolgáltatáskonstruktor függvényünket a gyárnak. Átad egy függvényt, ami arra kéri az injektort, hogy a megadott konstruktorral instanciáljon és objektumot. Más szóval: egy szolgáltatás meghív egy előre definiált gyárat, ami a megfelelő szolgáltató $get() metódusaként végzi. A $injector.instantiate() az a metódus, amely végül a Object.create() metódust hívja meg a konstruktorfüggvénnyel. Ezért használjuk a this-t a szolgáltatásokban.

Oké, tehát kiderült, hogy mindegy, hogy mit használunk, service() vagy factory(), mindig egy gyárat hívunk meg, amely létrehozza a szolgáltatásunk szolgáltatóját. Ezzel el is érkeztünk az Angular történetében legtöbbször feltett kérdéshez:

Melyiket használjam?

Az interneten erre a kérdésre rákérdezve számos cikkhez és StackOverflow válaszhoz jutunk. Az első ez a válasz. Azt mondja:

“Alapvetően a szolgáltatás és a gyár közötti különbség a következő:”

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

Most már tudjuk, mi történik a színfalak mögött, de ez a válasz még egy megjegyzést tesz hozzá. Azt mondja, hogy futtathatunk kódot, mielőtt visszaadnánk az objektumunk literalitását. Ez alapvetően lehetővé teszi számunkra, hogy valami konfigurációs dolgot csináljunk, vagy feltételesen hozzunk létre egy objektumot vagy ne, ami nem tűnik lehetségesnek, ha közvetlenül egy szolgáltatást hozunk létre, ezért a legtöbb forrás a factories használatát javasolja a szolgáltatásokkal szemben, de az érvelés nem értékelhető.

Mi lenne, ha azt mondanám, hogy pontosan ugyanezt a dolgot a szolgáltatásokkal is meg tudjuk csinálni?

Yeap, helyes. A szolgáltatás egy konstruktorfunkció, ez azonban nem akadályoz meg minket abban, hogy további munkát végezzünk és objektum literálokat adjunk vissza. Valójában a JavaScriptben a konstruktorfunkciók azt adnak vissza, amit akarnak. Tehát foghatjuk a szolgáltatásunk kódját, és megírhatjuk úgy, hogy alapvetően ugyanazt csinálja, mint a gyárunk:

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

Hoppla, akkor most mi van? Most jöttünk rá, hogy attól függően, hogy hogyan írjuk meg a szolgáltatásainkat, már egyáltalán nincs különbség a kettő között. Marad a nagy kérdés: Melyiket használjuk?

A szolgáltatások lehetővé teszik számunkra, hogy ES6 osztályokat használjunk

A szolgáltatások ilyen módon történő megírása persze eléggé kontraproduktív, hiszen konstruktorfunkcióként hívjuk, tehát úgy is kell használni. Van akkor egyáltalán valami előnye a másikkal szemben? Igen, van. Kiderült, hogy az ES6-ra való áttérés során valójában jobb, ha ahol csak lehet, szolgáltatásokat használunk. Ennek oka egyszerűen az, hogy a szolgáltatás egy konstruktorfunkció, a gyár pedig nem. Az ES5-ben a konstruktorfunkciókkal való munka lehetővé teszi számunkra, hogy könnyedén használhassuk az ES6 osztályokat, amikor áttérünk az ES6-ra.

Vegyük például a kódunkat, és írjuk át ES6-ban így:

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

Egy ES6 osztály valójában csak egy konstruktorfunkció az ES5-ben. Erről ma írtunk az ES6 használata Angularral című cikkben, ha még nem olvastad azt a cikket, ajánlom, hogy nézd meg.

A factories esetében ez nem lehetséges, mert azokat egyszerűen függvényként hívjuk meg. Remélem, ez a cikk mindent világossá tett, és arra ösztönzi az embereket, hogy ne használjanak factories-t a szolgáltatásokkal szemben, ha nem tudják, hogy mit használjanak.

Ezt és még többet megtudhatsz az Angular Master Class-on!

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.