Espera, o quê? Mais um artigo que responde à grande pergunta: Serviço vs Fábrica, o que devo usar? Sim, parece que isso não é mais necessário, já que há uma tonelada de recursos na internet que discutem esse tópico. Acontece que esta pergunta ainda aparece todas as semanas em canais diferentes, e mesmo depois de ler as dez primeiras respostas no StackOverflow, ainda não está muito claro. Apesar disso, também parece que os recursos atuais na web não promovem a melhor prática, especialmente se considerarmos os movimentos recentes da plataforma web. ES6 Eu estou olhando para você!
Este artigo explica de uma vez por todas a diferença entre serviços e fábricas e porque queremos preferir serviços a fábricas.
A diferença entre serviços e fábricas
Okay, então qual é a diferença entre um serviço e uma fábrica em AngularJS? Como todos sabemos, podemos definir um serviço como este:
app.service('MyService', function () { this.sayHello = function () { console.log('hello'); };});
.service()
é um método no nosso módulo que leva um nome e uma função que define o serviço. Bastante direto. Uma vez definido, podemos injetar e usar esse serviço particular em outros componentes, como controladores, diretivas e filtros, como este:
app.controller('AppController', function (MyService) { MyService.sayHello(); // logs 'hello'});
Okay, clear. Agora a mesma coisa que uma fábrica:
app.factory('MyService', function () { return { sayHello: function () { console.log('hello'); } }});
Again, .factory()
é um método no nosso módulo e também leva um nome e uma função, que define a fábrica. Podemos injetar e usar essa coisa exatamente da mesma forma que fizemos com o serviço. Agora qual é a diferença aqui?
Bem, você pode ver que ao invés de trabalharmos com this
na fábrica, estamos retornando um objeto literalmente. Porque é que isso acontece? Acontece que um serviço é uma função construtora enquanto que uma fábrica não é. Em algum lugar dentro deste mundo angular, há um código que chama Object.create()
com a função construtora de serviço, quando ele se instanciar. No entanto, uma função de fábrica é realmente apenas uma função que é chamada, e é por isso que temos de devolver um objecto explicitamente.
Para tornar isso um pouco mais claro, podemos simplesmente dar uma vista de olhos ao código fonte Angular. Aqui está como a função factory()
se parece:
function factory(name, factoryFn, enforce) { return provider(name, { $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn });}
Pega o nome e a função de fábrica que é passada e basicamente retorna um provedor com o mesmo nome, que tem um método $get
que é a nossa função de fábrica. Então o que é isso de provedor? Bem, sempre que você pede ao injetor uma dependência específica, ele basicamente pede ao provedor correspondente uma instância desse serviço, chamando o método $get()
. É por isso que $get()
é necessário, ao criar provedores.
Em outras palavras, se injetarmos MyService
em algum lugar, o que acontece nos bastidores é:
MyServiceProvider.$get(); // return the instance of the service
Alright, as funções de fábrica são apenas chamadas, e o código do serviço? Aqui está outro trecho:
function service(name, constructor) { return factory(name, );}
Oh olha, acontece que quando chamamos service()
ele realmente chama factory()
. Mas isso não passa apenas a nossa função de construtor de serviços para a fábrica como está. Ele passa uma função que pede ao injetor para instanciar e objetar pelo construtor dado. Em outras palavras: um serviço chama uma fábrica predefinida, que termina como método $get()
no provedor correspondente. $injector.instantiate()
é o método que acaba por chamar Object.create()
com a função construtora. É por isso que usamos this
em services.
Okay, então acontece que, não importa o que usamos, service()
ou factory()
, é sempre uma fábrica que é chamada que cria um provedor para o nosso serviço. O que nos leva à pergunta mais feita na história angular: Qual devo usar?
Qual devo usar?
Asking essa pergunta na internet leva-nos a alguns artigos e respostas StackOverflow. A primeira é esta resposta. Diz:
“Basicamente a diferença entre o serviço e a fábrica é a seguinte:”
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 + "!"; } }});
>
Agora já sabemos o que acontece nos bastidores, mas esta resposta acrescenta outro comentário. Ela diz que podemos executar o código antes de devolvermos nosso objeto literalmente. Isso basicamente nos permite fazer algumas coisas de configuração ou criar condicionalmente um objeto ou não, o que não parece ser possível ao criar um serviço diretamente, e é por isso que a maioria dos recursos recomendam o uso de fábricas sobre serviços, mas o raciocínio é inapreciável.
E se eu lhe disser, podemos fazer exatamente a mesma coisa com serviços também?
Yeap, correto. Um serviço é uma função construtora, no entanto, isso não nos impede de fazer trabalho adicional e retornar literalmente os objetos. Na verdade, funções de construtor em JavaScript podem retornar o que quiserem. Então podemos pegar nosso código de serviço e escrevê-lo de uma forma que basicamente faça exatamente a mesma coisa que nossa fábrica:
app.service('MyService', function () { // we could do additional work here too return { sayHello: function () { console.log('hello'); }; }});
Hoppla, então e agora? Acabamos de perceber que, dependendo de como escrevemos os nossos serviços, não há mais nenhuma diferença entre os dois. A grande questão permanece: Qual devemos usar?
Serviços permitem-nos usar as classes ES6
Obviamente, escrever serviços dessa forma é um pouco contraproducente, já que é chamado de função construtora, por isso também deve ser usado como um. Existe alguma vantagem sobre a outra então? Sim, há. Acontece que é realmente melhor usar serviços onde for possível, quando se trata de migrar para o ES6. A razão para isso é simplesmente que um serviço é uma função construtora e uma fábrica não é. Trabalhar com funções construtoras no ES5 nos permite usar facilmente as classes ES6 quando migramos para ES6.
Por exemplo, podemos pegar nosso código e reescrevê-lo no ES6 assim:
class MyService { sayHello() { console.log('hello'); }}app.service('MyService', MyService);
Uma classe ES6 é realmente apenas uma função construtora no ES5. Nós escrevemos sobre isso em Using ES6 with Angular hoje, se você ainda não leu esse artigo, eu recomendaria verificar isso.
Com fábricas, isso não é possível porque elas são simplesmente chamadas como funções. Espero que este artigo tenha deixado tudo claro e encoraje as pessoas a não usar fábricas em vez de serviços, se não souberem o que usar.
Este e muito mais aprende na nossa aula de Angular Master!