angular.service vs angular.factory

1065

Vi tanto angular.factory () quanto angular.service () usados ​​para declarar serviços; no entanto, não consigo encontrar angular.service nenhum lugar na documentação oficial.

Qual é a diferença entre os dois métodos?
Qual deve ser usado para quê (supondo que eles façam coisas diferentes)?

jacob
fonte
25
possível duplicata de confuso sobre o serviço vs fábrica
Mark Rajcok
4
Eu procurei por "[angularjs] service factory", mas também me lembrei de que já havia uma pergunta sobre isso (porque pensei em escrever essa pergunta em algum momento).
precisa saber é o seguinte
2
Em uma pesquisa, os colchetes significam uma tag?
jacob
11
Os colchetes quadrados @Jacob estão restringindo sua pesquisa. diretivas [angularjs] - procurará por 'diretivas' as perguntas já marcadas com angularjs.
Mahbub
1
@Mahbub Em outras palavras, "sim" :)
Brian

Respostas:

1268
  angular.service('myService', myServiceFunction);
  angular.factory('myFactory', myFactoryFunction);

Eu tive problemas para entender esse conceito até colocar para mim mesmo desta maneira:

Serviço : a função que você escreve será nova :

  myInjectedService  <----  new myServiceFunction()

Factory : a função (construtor) que você escreve será chamada :

  myInjectedFactory  <---  myFactoryFunction()

Depende de você, mas existem alguns padrões úteis ...

Como escrever uma função de serviço para expor uma API pública:

function myServiceFunction() {
  this.awesomeApi = function(optional) {
    // calculate some stuff
    return awesomeListOfValues;
  }
}
---------------------------------------------------------------------------------
// Injected in your controller
$scope.awesome = myInjectedService.awesomeApi();

Ou usando uma função de fábrica para expor uma API pública:

function myFactoryFunction() {
  var aPrivateVariable = "yay";

  function hello() {
    return "hello mars " + aPrivateVariable;
  }

  // expose a public API
  return {
    hello: hello
  };
}
---------------------------------------------------------------------------------
// Injected in your controller
$scope.hello = myInjectedFactory.hello();

Ou usando uma função de fábrica para retornar um construtor:

function myFactoryFunction() {
    return function() {
        var a = 2;
        this.a2 = function() {
            return a*2;
        };
    };
}
---------------------------------------------------------------------------------
// Injected in your controller
var myShinyNewObject = new myInjectedFactory();
$scope.four = myShinyNewObject.a2();

Qual usar? ...

Você pode realizar a mesma coisa com ambos. No entanto, em alguns casos, a fábrica oferece um pouco mais de flexibilidade para criar um injetável com uma sintaxe mais simples. Isso porque enquanto myInjectedService sempre deve ser um objeto, myInjectedFactory pode ser um objeto, uma referência de função ou qualquer valor. Por exemplo, se você escreveu um serviço para criar um construtor (como no último exemplo acima), ele teria que ser instanciado da seguinte maneira:

var myShinyNewObject = new myInjectedService.myFunction()

que é sem dúvida menos desejável que isso:

var myShinyNewObject = new myInjectedFactory();

(Mas você deve ser cauteloso ao usar esse tipo de padrão em primeiro lugar, porque os novos objetos em seus controladores criam dependências difíceis de rastrear que são difíceis de serem simuladas para teste. É melhor ter um serviço para gerenciar uma coleção de objetos para você do que usar new()astuciosamente.)


Mais uma coisa, eles são todos Singletons ...

Lembre-se também de que, nos dois casos, angular está ajudando você a gerenciar um singleton. Independentemente de onde ou quantas vezes você injeta seu serviço ou função, você obterá a mesma referência para o mesmo objeto ou função. (Com exceção de quando uma fábrica simplesmente retorna um valor como um número ou sequência. Nesse caso, você sempre obterá o mesmo valor, mas não uma referência.)

Gil Birman
fonte
2
Seria melhor chamá-lo de construtor de objetos do que Newable?
marksyzm
2
@ Hugo, eu estava demonstrando que você pode efetivamente realizar a mesma coisa com ambos, mas a sintaxe será diferente.
Gil Birman
105
Não sei quantas vezes precisarei ler sobre a diferença entre serviço e fábrica antes de me convencer de que ambos são necessários.
DMac the Destroyer
10
Já temos um verbo para dizer "para novo", é "instanciar". Somente para referência. :)
sscarduzio
7
Fábricas são funções que são chamadas, para que possam retornar qualquer coisa. Por outro lado, os serviços são instanciados por via angular new fn(), portanto, eles devem retornar uma instância.
Gil Birman
318

Basta colocar ..

// Service
service = (a, b) => {
  a.lastName = b;
  return a;
};

// Factory
factory = (a, b) => Object.assign({}, a, { lastName: b });

const fullName = { firstName: 'john' };

// Service
const lastNameService = (a, b) => {
  a.lastName = b;
  return a;
};
console.log(lastNameService(fullName, 'doe'));

// Factory
const lastNameFactory = (a, b) => 
  Object.assign({}, a, { lastName: b })
console.log(lastNameFactory(fullName, 'doe'));

Kirk Strobeck
fonte
169
Cara, obrigado. Não que os detalhes das outras respostas não sejam válidos, mas algumas vezes você precisa da versão de 10 segundos.
R Claven
4
Apenas faça com que a função de serviço não retorne nada. O this.name = ... é suficiente para mostrar que está expondo uma API.
pixelbits
3
No entanto, se você retornar e objetar, ele será usado em vez disso. jsfiddle.net/Ne5P8/1221
MrB 4/04
@ MRB, esse é um recurso normal do JavaScript, não específico ao Angular ou ao contexto desta pergunta.
Om Shankar
@ Shan Shankar, A resposta acima está mostrando que a diferença é o uso desse objeto vs um objeto retornado. Eu estava mostrando que "ISTO" é o valor padrão que será usado com um serviço, no entanto, se você retornar um valor, ele atuará quase exatamente como uma fábrica. No entanto, por outro lado, uma fábrica parece exigir um valor retornado, caso contrário, ocorrerá um erro - (mostrado neste exemplo - jsfiddle.net/hmoc0q3v/1 ).
MrB
247

Aqui estão as principais diferenças:

Serviços

Sintaxe: module.service( 'serviceName', function );

Resultado: ao declarar serviceName como um argumento injetável, você receberá a instância de uma função passada para module.service.

Uso: Pode ser útil para compartilhar funções utilitárias que são úteis para chamar, simplesmente anexando ( )à referência da função injetada. Também pode ser executado com injectedArg.call( this )ou similar.

Fábricas

Sintaxe: module.factory( 'factoryName', function );

Resultado: ao declarar factoryName como um argumento injetável, você receberá o valor retornado invocando a referência de função passada para module.factory.

Uso: Pode ser útil para retornar uma função 'class' que pode ser renovada para criar instâncias.

Aqui está um exemplo usando serviços e fábrica . Leia mais sobre o AngularJS Service vs Factory .

Você também pode verificar a documentação do AngularJS e uma pergunta semelhante sobre o stackoverflow confusa sobre serviço versus fábrica .

Manish Chhabra
fonte
27
Não concordo com o exemplo de uso de uma fábrica. Serviços e fábricas (assumindo que uma função seja retornada. Poderia ser apenas um valor ou um objeto) podem ser atualizados. De fato, um serviço é a única opção que pode ser renovada, pois você recebe uma instância de função. Eu diria que o benefício de usar uma FÁBRICA em vez de um SERVIÇO é que ele permite algum controle sobre o acesso a propriedades - privadas e públicas em si, enquanto todas as propriedades do serviço são expostas por natureza. E penso em um fornecedor como uma fábrica - apenas é injetável e configurável no momento da configuração.
Desenhou R
1
@DrewR Obrigado por seu comentário, eu encontrei um bom exemplo de métodos públicos e privados usando uma fábrica: stackoverflow.com/a/14904891/65025
edzillion
Na verdade, tenho que concordar com @DrewR. Eu já usei fábricas para retornar objetos antes, mas, honestamente, nesse ponto, pode valer a pena usar $providerso tempo todo.
jedd.ahyoung
o serviço é automático instanciando o construtor, certo?
precisa saber é o seguinte
1
@DrewR - Pelo que entendi, é verdade que você pode obter o mesmo efeito renovável do serviço como em uma fábrica, mas não é para isso que ele se destina. Seu principal objetivo é quando você deseja retornar algum objeto utilitário e, para isso, ele fornece uma sintaxe mais adequada - você pode simplesmente escrever this.myFunc = function(){}em seu serviço (evita escrever código para criar o objeto como faria com uma fábrica) )
BornToCode 9/03/2017
137

TL; DR

1) Ao usar um Factory, você cria um objeto, adiciona propriedades a ele e retorna o mesmo objeto. Quando você passar esta fábrica para o seu controlador, essas propriedades no objeto estarão agora disponíveis nesse controlador através da sua fábrica.

app.controller('myFactoryCtrl', function($scope, myFactory){
  $scope.artist = myFactory.getArtist();
});

app.factory('myFactory', function(){
  var _artist = 'Shakira';
  var service = {};

  service.getArtist = function(){
    return _artist;
  }

  return service;
});


2) Ao usar o Serviço , o Angular o instancia nos bastidores com a palavra-chave 'new'. Por isso, você adicionará propriedades a 'this' e o serviço retornará 'this'. Quando você passa o serviço para o seu controlador, essas propriedades agora estarão disponíveis nesse controlador através do seu serviço.

app.controller('myServiceCtrl', function($scope, myService){
  $scope.artist = myService.getArtist();
});

app.service('myService', function(){
  var _artist = 'Nelly';
  this.getArtist = function(){
    return _artist;
  }
});



Não TL; DR

1)
Fábricas de fábrica são a maneira mais popular de criar e configurar um serviço. Realmente não há muito mais do que o TL; DR disse. Você acabou de criar um objeto, adicionar propriedades a ele e retornar o mesmo objeto. Então, quando você passar a fábrica para o seu controlador, essas propriedades no objeto estarão agora disponíveis nesse controlador através da sua fábrica. Um exemplo mais extenso está abaixo.

app.factory('myFactory', function(){
  var service = {};
  return service;
});

Agora, quaisquer propriedades que anexarmos a 'service' estarão disponíveis quando passarmos 'myFactory' para o nosso controlador.

Agora vamos adicionar algumas variáveis ​​'privadas' à nossa função de retorno de chamada. Eles não estarão diretamente acessíveis no controlador, mas, eventualmente, configuraremos alguns métodos getter / setter no 'service' para poder alterar essas variáveis ​​'privadas' quando necessário.

app.factory('myFactory', function($http, $q){
  var service = {};
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  var makeUrl = function(){
   _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK';
    return _finalUrl
  }

  return service;
});

Aqui você notará que não estamos anexando essas variáveis ​​/ funções ao 'serviço'. Estamos simplesmente criando-os para usá-los ou modificá-los posteriormente.

  • baseUrl é o URL base que a API do iTunes requer
  • _artist é o artista que queremos pesquisar
  • _finalUrl é o URL final e totalmente construído para o qual faremos a chamada para o iTunes makeUrl é uma função que cria e retorna nosso URL amigável do iTunes.

Agora que nossas variáveis ​​e funções auxiliares / particulares estão em vigor, vamos adicionar algumas propriedades ao objeto 'service'. O que colocarmos em 'serviço', poderemos usar diretamente em qualquer controlador em que passarmos 'myFactory'.

Vamos criar métodos setArtist e getArtist que simplesmente retornam ou definem o artista. Também vamos criar um método que chamará a API do iTunes com nosso URL criado. Esse método retornará uma promessa que será cumprida assim que os dados voltarem da API do iTunes. Se você não teve muita experiência no uso de promessas no Angular, eu recomendo fazer um mergulho profundo nelas.

Abaixo, o setArtist aceita um artista e permite que você o defina. O getArtist retorna o artista callItunes primeiro chama makeUrl () para criar o URL que usaremos com nossa solicitação $ http. Em seguida, ele configura um objeto de promessa, faz uma solicitação $ http com nosso URL final e, como $ http retorna uma promessa, podemos chamar .success ou .error após nossa solicitação. Em seguida, resolvemos nossa promessa com os dados do iTunes ou a rejeitamos com uma mensagem dizendo 'Houve um erro'.

app.factory('myFactory', function($http, $q){
  var service = {};
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }

  service.setArtist = function(artist){
    _artist = artist;
  }

  service.getArtist = function(){
    return _artist;
  }

  service.callItunes = function(){
    makeUrl();
    var deferred = $q.defer();
    $http({
      method: 'JSONP',
      url: _finalUrl
    }).success(function(data){
      deferred.resolve(data);
    }).error(function(){
      deferred.reject('There was an error')
    })
    return deferred.promise;
  }

  return service;
});

Agora nossa fábrica está completa. Agora podemos injetar 'myFactory' em qualquer controlador e, em seguida, poderemos chamar nossos métodos que anexamos ao nosso objeto de serviço (setArtist, getArtist e callItunes).

app.controller('myFactoryCtrl', function($scope, myFactory){
  $scope.data = {};
  $scope.updateArtist = function(){
    myFactory.setArtist($scope.data.artist);
  };

  $scope.submitArtist = function(){
    myFactory.callItunes()
      .then(function(data){
        $scope.data.artistData = data;
      }, function(data){
        alert(data);
      })
  }
});

No controlador acima, estamos injetando no serviço 'myFactory'. Em seguida, definimos propriedades em nosso objeto $ scope que são provenientes de dados de 'myFactory'. O único código complicado acima é se você nunca lidou com promessas antes. Como o callItunes está retornando uma promessa, podemos usar o método .then () e definir apenas $ scope.data.artistData quando nossa promessa for cumprida com os dados do iTunes. Você notará que nosso controlador é muito "fino". Todos os nossos dados lógicos e persistentes estão localizados em nosso serviço, não em nosso controlador.

2) Serviço
Talvez a maior coisa a saber ao lidar com a criação de um Serviço seja o fato de ele ser instanciado com a palavra-chave 'new'. Para os gurus do JavaScript, isso deve fornecer uma grande dica sobre a natureza do código. Para aqueles com conhecimentos limitados em JavaScript ou para aqueles que não conhecem muito bem o que a palavra-chave 'new' realmente faz, vamos revisar alguns fundamentos do JavaScript que eventualmente nos ajudarão a entender a natureza de um Serviço.

Para realmente ver as alterações que ocorrem quando você invoca uma função com a palavra-chave 'new', vamos criar uma função e invocá-la com a palavra-chave 'new', depois vamos mostrar o que o intérprete faz quando vê a palavra-chave 'new'. Os resultados finais serão os mesmos.

Primeiro, vamos criar nosso Construtor.

var Person = function(name, age){
  this.name = name;
  this.age = age;
}

Essa é uma função típica do construtor JavaScript. Agora, sempre que invocarmos a função Pessoa usando a palavra-chave 'new', 'this' será vinculado ao objeto recém-criado.

Agora, vamos adicionar um método ao protótipo de nossa Person, para que fique disponível em todas as instâncias da nossa classe de Person.

Person.prototype.sayName = function(){
  alert('My name is ' + this.name);
}

Agora, como colocamos a função sayName no protótipo, todas as instâncias de Person poderão chamar a função sayName para alertar o nome dessa instância.

Agora que temos nossa função construtora Person e nossa função sayName em seu protótipo, vamos criar uma instância de Person e chamar a função sayName.

var tyler = new Person('Tyler', 23);
tyler.sayName(); //alerts 'My name is Tyler'

Portanto, todo o código para criar um construtor Person, adicionar uma função a seu protótipo, criar uma instância Person e chamar a função em seu protótipo se parece com isso.

var Person = function(name, age){
  this.name = name;
  this.age = age;
}
Person.prototype.sayName = function(){
  alert('My name is ' + this.name);
}
var tyler = new Person('Tyler', 23);
tyler.sayName(); //alerts 'My name is Tyler'

Agora, vamos ver o que realmente está acontecendo quando você usa a palavra-chave 'new' em JavaScript. A primeira coisa que você deve notar é que, depois de usar 'new' em nosso exemplo, podemos chamar um método (sayName) em 'tyler' como se fosse um objeto - é porque é. Então, primeiro, sabemos que nosso construtor Person está retornando um objeto, se podemos ver isso no código ou não. Segundo, sabemos que, como nossa função sayName está localizada no protótipo e não diretamente na instância Person, o objeto que a função Person está retornando deve delegar ao seu protótipo em pesquisas com falha. Em termos mais simples, quando chamamos tyler.sayName (), o intérprete diz: “OK, vou olhar para o objeto 'tyler' que acabamos de criar, localize a função sayName e chame-a. Espere um minuto, não o vejo aqui - tudo o que vejo é nome e idade, deixe-me verificar o protótipo. Sim, parece que está no protótipo, deixe-me chamá-lo. ”.

Abaixo está o código de como você pode pensar sobre o que a palavra-chave 'nova' está realmente fazendo em JavaScript. É basicamente um exemplo de código do parágrafo acima. Coloquei a 'visão do intérprete' ou a maneira como o intérprete vê o código dentro das notas.

var Person = function(name, age){
  //The line below this creates an obj object that will delegate to the person's prototype on failed lookups.
  //var obj = Object.create(Person.prototype);

  //The line directly below this sets 'this' to the newly created object
  //this = obj;

  this.name = name;
  this.age = age;

  //return this;
}

Agora, tendo esse conhecimento do que a palavra-chave 'new' realmente faz em JavaScript, a criação de um Serviço em Angular deve ser mais fácil de entender.

A principal coisa a entender ao criar um Serviço é saber que os Serviços são instanciados com a palavra-chave 'new'. Combinando esse conhecimento com nossos exemplos acima, você deve reconhecer agora que anexará suas propriedades e métodos diretamente a 'this', que serão devolvidos pelo próprio Serviço. Vamos dar uma olhada nisso em ação.

Diferentemente do que fizemos originalmente com o exemplo do Factory, não precisamos criar um objeto e, em seguida, retorná-lo, porque, como mencionado muitas vezes antes, usamos a palavra-chave 'new' para que o intérprete crie esse objeto, faça-o delegar para é protótipo e, em seguida, devolva-o para nós sem que tenhamos que fazer o trabalho.

Primeiramente, vamos criar nossa função 'privada' e auxiliar. Isso deve parecer muito familiar, já que fizemos exatamente a mesma coisa com nossa fábrica. Não vou explicar o que cada linha faz aqui, porque fiz isso no exemplo de fábrica; se você estiver confuso, releia o exemplo de fábrica.

app.service('myService', function($http, $q){
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }
});

Agora, anexaremos todos os nossos métodos que estarão disponíveis em nosso controlador a 'this'.

app.service('myService', function($http, $q){
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }

  this.setArtist = function(artist){
    _artist = artist;
  }

  this.getArtist = function(){
    return _artist;
  }

  this.callItunes = function(){
    makeUrl();
    var deferred = $q.defer();
    $http({
      method: 'JSONP',
      url: _finalUrl
    }).success(function(data){
      deferred.resolve(data);
    }).error(function(){
      deferred.reject('There was an error')
    })
    return deferred.promise;
  }

});

Agora, assim como em nossa fábrica, setArtist, getArtist e callItunes estarão disponíveis em qualquer controlador em que passarmos o myService. Aqui está o controlador myService (que é quase exatamente o mesmo que o nosso controlador de fábrica).

app.controller('myServiceCtrl', function($scope, myService){
  $scope.data = {};
  $scope.updateArtist = function(){
    myService.setArtist($scope.data.artist);
  };

  $scope.submitArtist = function(){
    myService.callItunes()
      .then(function(data){
        $scope.data.artistData = data;
      }, function(data){
        alert(data);
      })
  }
});

Como mencionei antes, quando você realmente entende o que "novo" faz, os Serviços são quase idênticos às fábricas da Angular.

Tyler McGinnis
fonte
12
Convém fornecer um link diretamente para o seu blog. tylermcginnis.com/angularjs-factory-vs-service-vs-provider Achei isso um pouco mais fácil de ler.
Tyler Collier
3
Não há nada errado em repetir seu blog aqui, mas eu concordo que é uma publicação greta.
R Claven
5
Uma boa explicação detalhada do que cada um faz sob o capô, mas ainda não está claro por que e quando alguém escolheria usar um Serviço em uma Fábrica. Em outras palavras, quando vou preferir que um objeto novo seja devolvido por uma fábrica. Eu acho que essa é a maior confusão.
demisx 15/09/14
2
Basicamente, se você deseja criar uma conexão persistente com um serviço remoto, como a API do iTunes mencionada no exemplo, com uma conexão constante (estado da conexão, histórico de chamadas, armazenamento de dados), você pode ir com o Factory. Se você implementá-lo como um Serviço, toda vez que desejar algo da API, você terá que recriar a conexão e não poderá realmente armazenar nada nela. Porque toda vez que você recriar o serviço, você receberá um objeto em branco / padrão.
Meki
4
Não acho que isso esteja correto, @Aznim. Como outros já disseram, ambos oferecem singletons.
Cryptovirus
35

A pista está no nome

Serviços e fábricas são semelhantes entre si. Ambos produzirão um objeto singleton que pode ser injetado em outros objetos e, portanto, são frequentemente usados ​​de forma intercambiável.

Eles devem ser usados ​​semanticamente para implementar diferentes padrões de design.

Os serviços são para implementar um padrão de serviço

Um padrão de serviço é aquele em que seu aplicativo é dividido em unidades de funcionalidade logicamente consistentes. Um exemplo pode ser um acessador de API ou um conjunto de lógica de negócios.

Isso é especialmente importante no Angular, porque os modelos Angular geralmente são apenas objetos JSON extraídos de um servidor e, portanto, precisamos de um lugar para colocar nossa lógica de negócios.

Aqui está um serviço do Github, por exemplo. Ele sabe como falar com o Github. Ele sabe sobre URLs e métodos. Podemos injetá-lo em um controlador e ele gera e retorna uma promessa.

(function() {
  var base = "https://api.github.com";

  angular.module('github', [])
    .service('githubService', function( $http ) {
      this.getEvents: function() {
        var url = [
          base,
          '/events',
          '?callback=JSON_CALLBACK'
        ].join('');
        return $http.jsonp(url);
      }
    });
  )();

Fábricas implementam um padrão de fábrica

As fábricas, por outro lado, pretendem implementar um padrão de fábrica. Um padrão de fábrica no qual usamos uma função de fábrica para gerar um objeto. Normalmente, podemos usar isso para construir modelos. Aqui está uma fábrica que retorna um construtor Author:

angular.module('user', [])
  .factory('User', function($resource) {
    var url = 'http://simple-api.herokuapp.com/api/v1/authors/:id'
    return $resource(url);
  })

Usaríamos isso assim:

angular.module('app', ['user'])
  .controller('authorController', function($scope, User) {
    $scope.user = new User();
  })

Observe que as fábricas também retornam singletons.

Fábricas podem retornar um construtor

Como uma fábrica simplesmente retorna um objeto, ela pode retornar qualquer tipo de objeto que você desejar, incluindo uma função construtora, como vemos acima.

Fábricas retornam um objeto; serviços são renováveis

Outra diferença técnica está na forma como serviços e fábricas são compostos. Uma função de serviço será atualizada para gerar o objeto. Uma função de fábrica será chamada e retornará o objeto.

  • Os serviços são construtores novos.
  • As fábricas são simplesmente chamadas e retornam um objeto.

Isso significa que em um serviço, acrescentamos "this" que, no contexto de um construtor, apontará para o objeto em construção.

Para ilustrar isso, aqui está o mesmo objeto simples criado usando um serviço e uma fábrica:

angular.module('app', [])
  .service('helloService', function() {
    this.sayHello = function() {
      return "Hello!";
    }
  })
  .factory('helloFactory', function() {
    return {
      sayHello: function() {
        return "Hello!";
      }
    }
  });
superluminário
fonte
2
ótima explicação, obrigado! Também existe um tipo no código de amostra Fábricas onde o Authorparâmetro do injetor deve estar Person.
Mikhail-t
Obrigado @ mik-T, corrigi os erros de digitação.
superluminary
1
Seu uso do padrão de serviço está incorreto - isso deve ser uma fábrica. Se você chamar .factory () em vez de .service (), verá que funciona exatamente da mesma maneira. O padrão de serviço deve ser fornecido com uma função construtora, não uma função que retorna um novo objeto. Angular (efetivamente) chama "novo" em sua função de construtor. A única razão pela qual o serviço funciona é que, se você chamar "new" em uma função construtora que retorna um objeto, na verdade você recupera o objeto retornado em vez do objeto construído. E fábricas podem ser usadas para criar o que você quiser, não apenas modelos.
Dan King
27

Todas as respostas aqui parecem estar relacionadas a serviço e fábrica, e isso é válido, pois era sobre isso que estava sendo perguntado. Mas também é importante ter em mente que existem vários outros provider(), incluindo,, value()e constant().

A chave a lembrar é que cada um é um caso especial do outro. Cada caso especial na cadeia permite que você faça a mesma coisa com menos código. Cada um também com algumas limitações adicionais.

Para decidir quando usar qual você acabou de ver qual permite que você faça o que deseja em menos código. Aqui está uma imagem que ilustra quão semelhantes elas são:

insira a descrição da imagem aqui

Para obter um detalhamento passo a passo completo e uma referência rápida de quando usar cada um, visite o post do blog onde obtive esta imagem:

http://www.simplygoodcode.com/2015/11/the-difference-between-service-provider-and-factory-in-angularjs/

Luis Perez
fonte
3
@jacob talvez sim, mas acho que o conceito geral não apenas de quando usar cada um, mas de que todos são essencialmente variações da mesma coisa, é importante.
Luis Perez
1
@LuisPerez O link para o seu blog e o vídeo explicando a diferença é realmente ótimo. É mais fácil de entender com esses exemplos do vídeo :)
Alin Ciocan
24

app.factory ('fn', fn) vs. app.service ('fn', fn)

Construção

Com fábricas, Angular invocará a função para obter o resultado. É o resultado que é armazenado em cache e injetado.

 //factory
 var obj = fn();
 return obj;

Com os serviços, o Angular chamará a função construtora chamando new . A função construída é armazenada em cache e injetada.

  //service
  var obj = new fn();
  return obj;

Implementação

As fábricas normalmente retornam um literal de objeto porque o valor de retorno é o que é injetado nos controladores, blocos de execução, diretivas etc.

  app.factory('fn', function(){
         var foo = 0;
         var bar = 0;
         function setFoo(val) {
               foo = val;
         }
         function setBar (val){
               bar = val;
         }
         return {
                setFoo: setFoo,
                serBar: setBar
         }
  });

As funções de serviço normalmente não retornam nada. Em vez disso, eles executam a inicialização e expõem funções. As funções também podem fazer referência a 'this', pois foram construídas usando 'new'.

app.service('fn', function () {
         var foo = 0;
         var bar = 0;
         this.setFoo = function (val) {
               foo = val;
         }
         this.setBar = function (val){
               bar = val;
         }
});

Conclusão

Quando se trata de usar fábricas ou serviços, ambos são muito semelhantes. Eles são injetados em controladores, diretivas, bloco de execução, etc., e usados ​​no código do cliente da mesma maneira. Eles também são dois singletons - o que significa que a mesma instância é compartilhada entre todos os locais onde o serviço / fábrica é injetado.

Então, qual você prefere? Qualquer um - eles são tão parecidos que as diferenças são triviais. Se você escolher um sobre o outro, esteja ciente de como eles são construídos, para que você possa implementá-los adequadamente.

pixelbits
fonte
As funções de serviço "não retornam nada", elas implicitamente retornam o objeto construído SE você não especificar sua própria declaração de retorno (neste último caso, o objeto retornado será o que será criado e armazenado em cache, semelhante a uma fábrica).
Cryptovirus
Acho que você está interpretando mal-lo ... Quando eu digo retorno, quero dizer, do ponto de vista da implementação da função de serviço
pixelbits
você tem certeza de que a fábrica também é uma cidade?
Martian2049
5

Passei algum tempo tentando descobrir a diferença.

E eu acho que a função de fábrica usa o padrão do módulo e a função de serviço usa o padrão construtor de scripts java padrão.

ps.
fonte
2

O padrão de fábrica é mais flexível, pois pode retornar funções e valores, além de objetos.

Não há muito sentido no padrão de serviço IMHO, pois tudo o que faz você pode facilmente fazer com uma fábrica. As exceções podem ser:

  • Se você se importa com o tipo declarado de seu serviço instanciado por algum motivo - se você usar o padrão de serviço, seu construtor será o tipo do novo serviço.
  • Se você já possui uma função construtora que está usando em outro lugar que também deseja usar como serviço (embora provavelmente não seja de muita utilidade se deseja injetar algo nela!).

Indiscutivelmente, o padrão de serviço é uma maneira um pouco mais agradável de criar um novo objeto do ponto de vista da sintaxe, mas também é mais caro instanciar. Outros indicaram que o angular usa "novo" para criar o serviço, mas isso não é verdade - não é possível fazer isso porque todo construtor de serviço tem um número diferente de parâmetros. O que o angular realmente faz é usar o padrão de fábrica internamente para envolver sua função de construtor. Em seguida, ele faz alguns truques inteligentes para simular o operador "novo" do javascript, chamando seu construtor com um número variável de argumentos injetáveis ​​- mas você pode deixar de fora essa etapa se usar o padrão de fábrica diretamente, aumentando muito ligeiramente a eficiência do seu código.

Dan King
fonte
Os serviços são mais eficientes de construir do que as fábricas, pois as fábricas usam fechamentos relativamente caros e os serviços (classes) podem tirar proveito do protótipo.
jacob
@jacob Não sabe o que você quer dizer com fechamentos? A fábrica é apenas uma função que retorna um objeto. Você só precisa usar um fechamento se o objeto retornado exigir um estado "privado". Você ainda teria que fazer a mesma coisa se usasse um construtor (serviço). No entanto, entendo o seu protótipo - embora você ainda possa fazer isso em uma fábrica, se quiser.
Dan King
function MyFactory(dep1) { var $$foo = 'bar', factory = {}; Object.defineProperties(factory.prototype, { foo: { value: $$foo } }); return factory; } function MyService(dep1) { var $$foo = 'bar'; Object.defineProperties(MyService.prototype, { foo: { value: $$foo } }); } Enquanto o MyFactory e o MyService usam o protótipo, o MyFactory ainda sofre um impacto no desempenho ao construir o objeto que está sendo retornado. Nos dois exemplos, eles têm privates, mas no MyService não há relativamente nenhuma diferença de desempenho.
jacob
1
Para mim, a diferença é se eu quero usar a fábrica diretamente sem um método: MyFactory(someArgument)(ex $http()). Isso não é possível com um serviço como você estaria fazendo referência ao construtor: MyService(someArgument).
jacob
No tempo de construção do objeto, eu realmente não vejo como o factory = {} é um problema de desempenho, mais do que o javascript inicializando "this" para você quando chama seu construtor? E acho que o maior impacto no desempenho está do lado angular quando envolve seu construtor em uma fábrica e, em seguida, precisa pular arcos para simular "novo" para que ele possa injetar suas dependências.
Dan King