Așteaptă, ce? Încă un articol care răspunde la marea întrebare: Service vs Factory, ce ar trebui să folosesc? Da, se pare că acest lucru nu mai este necesar, deoarece există o tonă de resurse pe internet care discută acest subiect. Se pare că această întrebare încă apare la fiecare săptămână sau cam așa ceva pe diferite canale, și chiar și după ce am citit primele zece răspunsuri de pe StackOverflow, tot nu este foarte clar. În ciuda acestui fapt, se pare, de asemenea, că resursele actuale de pe internet nu promovează cu adevărat cea mai bună practică reală, mai ales dacă luăm în considerare mișcările recente ale platformei web. ES6 I’m looking at you!
Acest articol explică o dată pentru totdeauna diferența dintre servicii și fabrici și de ce vrem să preferăm serviciile în locul fabricilor.
Diferența dintre servicii și fabrici
Ok, deci care este diferența dintre un serviciu și o fabrică în AngularJS? După cum știm cu toții, putem defini un serviciu astfel:
app.service('MyService', function () { this.sayHello = function () { console.log('hello'); };});
.service()
este o metodă pe modulul nostru care primește un nume și o funcție care definește serviciul. Destul de simplu. Odată definit, putem injecta și utiliza acel serviciu particular în alte componente, cum ar fi controlorii, directivele și filtrele, astfel:
app.controller('AppController', function (MyService) { MyService.sayHello(); // logs 'hello'});
Ok, clar. Acum, același lucru ca și în cazul unei fabrici:
app.factory('MyService', function () { return { sayHello: function () { console.log('hello'); } }});
Din nou, .factory()
este o metodă a modulului nostru și ia, de asemenea, un nume și o funcție, care definește fabrica. Putem injecta și utiliza acest lucru exact în același mod în care am făcut-o cu serviciul. Acum, care este diferența aici?
Bine, ați putea vedea că, în loc să lucrăm cu this
în fabrică, returnăm un obiect literal. De ce se întâmplă asta? Se pare că un serviciu este o funcție de constructor, în timp ce o fabrică nu este. Undeva adânc în interiorul acestei lumi Angular, există acest cod care apelează Object.create()
cu funcția de constructor a serviciului, atunci când acesta este instanțiat. Cu toate acestea, o funcție factory este, de fapt, doar o funcție care este apelată, motiv pentru care trebuie să returnăm un obiect în mod explicit.
Pentru a face acest lucru un pic mai clar, putem pur și simplu să aruncăm o privire la codul sursă Angular. Iată cum arată funcția factory()
:
function factory(name, factoryFn, enforce) { return provider(name, { $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn });}
Aceasta ia numele și funcția de fabrică care este trecută și, practic, returnează un furnizor cu același nume, care are o metodă $get
care este funcția noastră de fabrică. Deci, care este treaba cu acest furnizor? Ei bine, ori de câte ori cereți injectorului o anumită dependență, acesta solicită practic furnizorului corespunzător o instanță a acelui serviciu, prin apelarea metodei $get()
. Acesta este motivul pentru care $get()
este necesar, atunci când se creează furnizori.
Cu alte cuvinte, dacă injectăm MyService
undeva, ceea ce se întâmplă în spatele scenei este:
MyServiceProvider.$get(); // return the instance of the service
În regulă, funcțiile de fabrică sunt doar apelate, dar cum rămâne cu codul serviciului? Iată un alt fragment:
function service(name, constructor) { return factory(name, );}
Oh, uite, se pare că atunci când apelăm service()
de fapt se apelează factory()
. Dar nu transmite pur și simplu funcția noastră de construcție a serviciului către fabrică așa cum este. El transmite o funcție care cere injectorului să instanțieze și obiectul prin constructorul dat. Cu alte cuvinte: un serviciu apelează o fabrică predefinită, care sfârșește ca metodă $get()
pe furnizorul corespunzător. $injector.instantiate()
este metoda care, în cele din urmă, apelează Object.create()
cu funcția de constructor. Acesta este motivul pentru care folosim this
în servicii.
Ok, deci se pare că, indiferent ce folosim, service()
sau factory()
, întotdeauna este apelată o fabrică care creează un furnizor pentru serviciul nostru. Ceea ce ne aduce la cea mai frecventă întrebare pusă în istoria Angular: Care dintre ele ar trebui să o folosesc?
Ce să folosesc?
Întrebarea asta pe internet ne duce la câteva articole și răspunsuri de pe StackOverflow. Primul este acest răspuns. Acesta spune:
„Practic, diferența dintre serviciu și fabrică este următoarea:”
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 + "!"; } }});
Acum știm deja ce se întâmplă în spatele scenei, dar acest răspuns adaugă un alt comentariu. Acesta spune că putem rula codul înainte de a returna obiectul nostru literal. Practic, asta ne permite să facem niște chestii de configurare sau să creăm condiționat un obiect sau nu, ceea ce nu pare a fi posibil atunci când creăm direct un serviciu, motiv pentru care majoritatea resurselor recomandă folosirea fabricilor în locul serviciilor, dar raționamentul este inapreciabil.
Și dacă v-aș spune că putem face exact același lucru și cu serviciile?
Da, corect. Un serviciu este o funcție constructor, însă asta nu ne împiedică să facem muncă suplimentară și să returnăm literali de obiect. De fapt, funcțiile constructor în JavaScript pot returna orice doresc. Așadar, putem lua codul serviciului nostru și să-l scriem în așa fel încât, practic, să facă exact același lucru ca și fabrica noastră:
app.service('MyService', function () { // we could do additional work here too return { sayHello: function () { console.log('hello'); }; }});
Hoppla, și acum ce facem? Tocmai ne-am dat seama că, în funcție de modul în care ne scriem serviciile, nu mai există nicio diferență între cele două. Marea întrebare rămâne: Pe care dintre ele ar trebui să le folosim?
Serviciile ne permit să folosim clasele ES6
Desigur, scrierea serviciilor în acest mod este un fel de contraproducție, deoarece este apelată ca o funcție de constructor, deci ar trebui să fie folosită tot ca atare. Există atunci vreun avantaj față de celelalte? Da, există. Se pare că, de fapt, este mai bine să folosiți servicii acolo unde este posibil, atunci când vine vorba de migrarea la ES6. Motivul este pur și simplu faptul că un serviciu este o funcție de construcție, iar o fabrică nu este. Lucrul cu funcții de constructor în ES5 ne permite să folosim cu ușurință clasele ES6 atunci când migrăm la ES6.
De exemplu, putem lua codul nostru și îl putem rescrie în ES6 astfel:
class MyService { sayHello() { console.log('hello'); }}app.service('MyService', MyService);
O clasă ES6 este de fapt doar o funcție de constructor în ES5. Am scris despre asta în Using ES6 with Angular today, dacă nu ați citit încă acel articol, vă recomand să îl consultați.
Cu fabricile, acest lucru nu este posibil, deoarece acestea sunt pur și simplu apelate ca funcții. Sper că acest articol a clarificat totul și îi încurajează pe oameni să nu folosească fabricile în locul serviciilor, dacă nu știu ce să folosească.
Acesta și multe altele le veți învăța în cursul nostru Angular Master Class!
.