Provider, Service, Factory – Qual a diferença?

Esse post nasceu de um pedido de um amigo que me ensinou muita coisa sobre Javascript.

Tudo farinha do mesmo saco

Pra já começar na polemica, eu vou te dizer que é tudo Provider. Se você dúvida, eu te mostro a documentação:

The injector creates two types of objects, services and specialized objects.

Services are objects whose API is defined by the developer writing the service.

Specialized objects conform to a specific Angular framework API. These objects are one of controllers, directives, filters or animations.

The injector needs to know how to create these objects. You tell it by registering a “recipe” for creating your object with the injector. There are five recipe types.

The most verbose, but also the most comprehensive one is a Provider recipe. The remaining four recipe types — Value, Factory, Service and Constant — are just syntactic sugar on top of a provider recipe.

O que isso diz é que basicamente, Value, Factory, Service e Constant são Providers, mas com um atalho sintático para facilitar a nossa vida, ou seja, eles são providers encapsulados de uma maneira que seja mais fácil criar eles. Claro que na pratica isso cria diferenças entre eles, que é o que importa pra gente vamos ver aqui hoje.

Value e Constant

Esse dois são sem dúvidas os mais simples de entender. Value permite você definir um valor para ser injetado em outras partes da sua aplicação. Você pode querer fazer isso por exemplo para definir o clientId dentro da sua aplicação.

var myApp = angular.module('myApp', []);

myApp.value('clientId', 'a12345654321x');

myApp.controller('DemoController', ['clientId', function DemoController(clientId) {
 this.clientId = clientId;
}]);

Isso permite que você defina valores em uma parte do seu módulo e injete ele em outros.

Constant  tem um funcionamento quase que igual, o que muda entre ele e o Value é que o Constant está disponível na fase de configuração.

var myApp = angular.module('myApp', []);

myApp.constant('planetName', 'Greasy Giant');

myApp.config(['planetName', function(planetName) { 

}]);

Você também consegue injetar o Constant no seu controller, mas se tentar injetar o Value no seu config ele vai gerar um erro.

Factory

O Factory adiciona algumas vantagens em cima dos outros dois:

  • Injeção de dependências
  • Inicialização de serviços
  • Inicialização Lazy (Lazy Loading) (isso quer dizer que ele só é instanciado quando alguém realmente precisa dele)

A ideia do Factory é basicamente criar algo não me diga. Vamos imaginar que temos um Factory que gera o nosso token da api, com base no nosso clientId (definido como Value):


var myApp = angular.module('myApp', []);

myApp.value('clientId', 'a12345654321x');

myApp.factory('apiToken', ['clientId', function apiTokenFactory(clientId) {
 var encrypt = function(data1, data2) {
 // NSA-proof encryption algorithm:
 return (data1 + ':' + data2).toUpperCase();
 };

var secret = window.localStorage.getItem('myApp.secret');
 var apiToken = encrypt(clientId, secret);

return apiToken;
}]);

Como você pode ver, eu injetei a dependência do Value, em seguida gerei um novo token. Se você quer criar algo simples para computar, processar ou criar objetos, o Factory pode ser a melhor opção.

Service

O Service adiciona uma vantagem bem simples em  cima do Factory, ele cria instâncias únicas. A Factory e o Service abaixo tem o mesmo valor.


function RocketLauncher(apiToken) {

this.launchedCount = 0;
this.launch = function() {
// Make a request to the remote API and include the apiToken
...
this.launchedCount++;
}
}
myApp.factory('rocketLauncherFactory', ["apiToken", function(apiToken) {
return new RocketLauncher(apiToken);
}]);
myApp.service('rocketLauncherService', ["apiToken", RocketLauncher]);

Com esse código fica simples de ver que o Service é só uma atalho semântico, você consegue usar um Service em locais que precisa algo mais Stateless,ou seja, que você não precisa de um estado, só de um executor simples.

Provider

Se você sobreviveu até aqui merece meu respeito ser lembrado que o Provider é o core de todos esses já citados “recipientes”. O que acontece que o Provider tem muitas opções de customizações e isso pode ser muito bom, mas para a maioria dos casos, é uma balão de canhão para matar formiga. Desse modo, nas coisas mais simples, você pode usar um Service, ou um Factory. Mas de repente, surge uma situação que você precisa usar um provider. Vamos dar uma olhada em um código.

myApp.provider('RocketLauncherProbider', function RocketLauncher() {
var useTinfoilShielding = false;

this.useTinfoilShielding = function(value) {
useTinfoilShielding = !!value;
};

this.$get = ["apiToken", function RocketLauncherFactory(apiToken) {

// let's assume that the RocketLauncher constructor was also changed to
// accept and use the useTinfoilShielding argument
return new RocketLauncher(apiToken, useTinfoilShielding);
}];
});

Você pode observar que o provider acima tem um método $get, esse método é uma espécie de Factory que é chamada quando você quer efetivamente gerar o seu provedor para usar. Tanto que se você cria uma Factory sem esse $get o Angular cria esse método para você por baixo dos panos.

“Mas Angeliski qual é a vantagem de usar um Provider?”

Você tem razão em perguntar, no cenário acima, podemos usar ele dentro do nosso config:


myApp.config(["RocketLauncherProvider", function(RocketLauncherProvider) {
RocketLauncherProvider.useTinfoilShielding(true);
}]);

Se você observar, no config, nós usamos o método useTinfoilShielding. Isso só é possível dentro da etapa de configuração, onde nós podemos customizar nosso provider, porque quando estamos dentro de um controller, ou dentro de um Service, esse método não pode ser chamado. Essa é uma das melhores características do Provider, a possibilidade de customização dele.

Se você é daqueles céticos, vou deixar a baixo o código do angular.js (versão 1.6.0).

function enforceReturnValue(name, factory) {
    return /** @this */ function enforcedReturnValue() {
      var result = instanceInjector.invoke(factory, this);
      if (isUndefined(result)) {
        throw $injectorMinErr('undef', 'Provider \'{0}\' must return a value from $get factory method.', name);
      }
      return result;
    };
  }

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

  function service(name, constructor) {
    return factory(name, ['$injector', function($injector) {
      return $injector.instantiate(constructor);
    }]);
  }

  function value(name, val) { return factory(name, valueFn(val), false); }

  function constant(name, value) {
    assertNotHasOwnProperty(name, 'constant');
    providerCache[name] = value;
    instanceCache[name] = value;
  }

Pois é, TUDO PROVIDER. Mas isso não é segredo, tanto que a documentação de Provider diz isso. A diferença mais útil no dia a dia é onde você pode injetar algo e onde não, vou deixar uma tabela marota pra você ficar ligado.

 

Features / Recipe type Factory Service Value Constant Provider
Pode ter dependências Sim Sim Não Não Sim
Disponível na fase de configuração Não Não Não Sim Sim
Pode criar funções Sim Sim Sim Sim Sim
Pode criar primitivos Sim Não Sim Sim Sim

 

Duvidas? gostou? Me acha um idiota?

Comenta ai!!

Angeliski

Anúncios

Comenta ai !

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s