Servis vs. továrna – jednou provždy

Počkat, cože? Další článek, který odpovídá na velkou otázku: Co mám použít? Ano, zdá se, že už to není potřeba, protože na internetu existuje tuna zdrojů, které se tímto tématem zabývají. Ukazuje se, že tato otázka se stále objevuje zhruba každý týden na různých kanálech a ani po přečtení deseti nejčastějších odpovědí na StackOverflow v tom stále není příliš jasno. Navzdory tomu se také ukazuje, že současné zdroje na webu příliš nepropagují skutečné osvědčené postupy, zejména pokud vezmeme v úvahu nedávné pohyby webové platformy. ES6 I’m looking at you!“

Tento článek jednou provždy vysvětluje rozdíl mezi službami a továrnami a proč chceme dávat přednost službám před továrnami.

Rozdíl mezi službami a továrnami

Okay, takže jaký je rozdíl mezi službou a továrnou v AngularJS? Jak všichni víme, službu můžeme definovat takto:

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

.service() je metoda v našem modulu, která přebírá název a funkci definující službu. Je to docela jednoduché. Po definování můžeme tuto konkrétní službu injektovat a používat v dalších komponentách, jako jsou řadiče, směrnice a filtry, takto:

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

Okay, jasné. Nyní totéž jako továrna:

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

Znovu, .factory() je metoda na našem modulu a také přebírá jméno a funkci, která definuje továrnu. Tu můžeme injektovat a používat úplně stejně jako službu. Jaký je zde nyní rozdíl?“

No, možná si všimnete, že místo práce s this v továrně vracíme literál objektu. Proč tomu tak je? Ukazuje se, že služba je funkce konstruktoru, zatímco továrna nikoli. Někde hluboko uvnitř tohoto světa Angularu je kód, který volá Object.create() s funkcí konstruktoru služby, když se služba instancuje. Funkce factory je však ve skutečnosti jen funkce, která se volá, a proto musíme explicitně vracet objekt.

Aby to bylo trochu jasnější, můžeme se jednoduše podívat na zdrojový kód systému Angular. Takto vypadá funkce factory():

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

Přijme jméno a předanou tovární funkci a v podstatě vrátí poskytovatele se stejným jménem, který má metodu $get, což je naše tovární funkce. Jak je to tedy s tím providerem? No, kdykoli se injektoru zeptáte na konkrétní závislost, v podstatě se zeptá příslušného poskytovatele na instanci této služby, a to tak, že zavolá metodu $get(). Proto je při vytváření poskytovatelů vyžadována $get().

Jinými slovy, pokud někde injektujeme MyService, v zákulisí se děje následující:

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

Právě se volají tovární funkce, a co kód služby? Tady je další úryvek:

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

Aha, podívejme se, ukazuje se, že když voláme service(), ve skutečnosti se volá factory(). Ale nepředává naší funkci konstruktoru služby továrně jen tak, jak je. Předává funkci, která žádá injektor o instanci a objekt podle daného konstruktoru. Jinými slovy: služba volá předdefinovanou továrnu, která skončí jako metoda $get() na příslušném poskytovateli. $injector.instantiate() je metoda, která nakonec volá Object.create() s funkcí konstruktoru. Proto ve službách používáme this.

Dobře, takže se ukazuje, že ať už použijeme service() nebo factory(), vždy se volá továrna, která vytvoří poskytovatele pro naši službu. Což nás přivádí k nejčastěji kladené otázce v historii systému Angular:

Položení této otázky na internetu nás zavede k několika článkům a odpovědím na StackOverflow. První z nich je tato odpověď. Píše se v ní:

„Rozdíl mezi službou a továrnou je v podstatě následující:“

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

Teď už víme, co se děje v zákulisí, ale tato odpověď přidává další komentář. Říká, že můžeme spustit kód předtím, než vrátíme náš doslovný objekt. To nám v podstatě umožňuje provádět nějaké konfigurační věci nebo podmíněně vytvářet či nevytvářet objekt, což se nezdá být možné při přímém vytváření služby, což je důvod, proč většina zdrojů doporučuje používat továrny místo služeb, ale argumentace je nedocenitelná.

Co když vám řeknu, že přesně totéž můžeme dělat i se službami?“

Jeap, správně. Služba je funkce konstruktoru, to nám však nebrání dělat další práci a vracet literály objektů. Ve skutečnosti mohou konstrukční funkce v JavaScriptu vracet, co chtějí. Můžeme tedy vzít náš kód služby a napsat ho tak, aby v podstatě dělal přesně totéž co naše továrna:

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

Hoppla, tak co teď? Právě jsme si uvědomili, že v závislosti na tom, jak naše služby napíšeme, už mezi nimi není vůbec žádný rozdíl. Velkou otázkou zůstává:

Služby nám umožňují používat třídy ES6

Jistě, psát služby tímto způsobem je tak trochu kontraproduktivní, protože se volají jako funkce konstruktoru, takže by se tak měly také používat. Má to tedy vůbec nějakou výhodu oproti ostatním? Ano, je. Ukazuje se, že pokud jde o přechod na ES6, je skutečně lepší používat služby, kde je to možné. Důvodem je jednoduše to, že služba je funkce konstruktoru a továrna nikoli. Práce s konstrukčními funkcemi v ES5 nám umožňuje snadno používat třídy ES6 při přechodu na ES6.

Například můžeme vzít náš kód a přepsat ho v ES6 takto:

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

Třída ES6 je ve skutečnosti jen konstrukční funkce v ES5. Psali jsme o tom dnes v článku Using ES6 with Angular, pokud jste ten článek ještě nečetli, doporučuji si ho přečíst.

U faktorů to není možné, protože se prostě volají jako funkce. Doufám, že tento článek vše objasnil a povzbudí lidi, aby nepoužívali továrny místo služeb, pokud nevědí, co mají použít.

Toto a ještě více se dozvíte v naší Angular Master Class!

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.