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.

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.

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):

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.

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.

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:

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).

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 typeFactoryServiceValueConstantProvider
Pode ter dependênciasSimSimNãoNãoSim
Disponível na fase de configuraçãoNãoNãoNãoSimSim
Pode criar funçõesSimSimSimSimSim
Pode criar primitivosSimNãoSimSimSim

Dúvidas? Gostou? Me acha um idiota?

Comenta ai!!

Angeliski