angular.service vs angular.factory

Eu vi ambos angular.factory () e angular.service () usado para declarar serviços; no entanto, não consigo encontrar o angular.service em qualquer lugar na documentação oficial.

Qual é a diferença entre os dois methods? Qual deve ser usado para o quê (assumindo que eles fazem coisas diferentes)?

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

Eu tive dificuldade em envolver minha cabeça em torno deste conceito até que eu coloquei para mim mesmo assim:

Serviço : a function que você escreve será newed:

  myInjectedService <---- new myServiceFunction() 

Fábrica : a function (construtor) que você escreve será chamada :

  myInjectedFactory <--- myFactoryFunction() 

O que você faz com isso é com você, mas existem alguns padrões úteis ...

Como escrever uma function 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 function 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 function 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 deles usar?

Você pode conseguir a mesma coisa com ambos. No entanto, em alguns casos, a fábrica oferece a você um pouco mais de flexibilidade para criar uma injetável com uma syntax mais simples. Isso porque, enquanto myInjectedService sempre deve ser um object, myInjectedFactory pode ser um object, uma referência de function 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 forma:

 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, já que os novos objects em seus controladores criam dependencies difíceis de rastrear que são difíceis de burlar para testes. É melhor que um serviço gerencie uma coleção de objects para você do que usar new() wily-nilly.)


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

Também tenha em mente que, em ambos os casos, o angular está ajudando você a gerenciar um singleton. Independentemente de onde ou quantas vezes você injetar seu serviço ou function, você obterá a mesma referência ao mesmo object ou function. (Com a exceção de quando uma fábrica simplesmente retorna um valor como um número ou string. Nesse caso, você sempre obterá o mesmo valor, mas não uma referência.)

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')); 

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 function passada para module.service .

Uso: Pode ser útil para compartilhar funções de utilidade que são úteis para invocar simplesmente anexando ( ) à referência de function 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 function passada para module.factory .

Uso: Pode ser útil para retornar uma function de ‘class’ que pode então ser nova 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 questões similares no stackoverflow confusas sobre serviço vs fábrica .

TL; DR

1) Quando você está usando um Factory, você cria um object, adiciona propriedades a ele e retorna esse mesmo object. Quando você passa esta fábrica para o seu controlador, essas propriedades no object estarão disponíveis naquele controlador através de 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) Quando você está usando o serviço , o Angular instancia isso nos bastidores com a palavra-chave ‘new’. Por causa disso, você adicionará propriedades a ‘this’ e o serviço retornará ‘this’. Quando você passa o serviço para o seu controlador, essas propriedades no ‘this’ 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ábrica
As fábricas são a maneira mais popular de criar e configurar um serviço. Não há realmente muito mais do que o que o TL; DR disse. Você acabou de criar um object, adicionar propriedades a ele e retornar esse mesmo object. Então, quando você passar a fábrica para o seu controlador, essas propriedades no object estarão disponíveis naquele controlador através de sua fábrica. Um exemplo mais extenso está abaixo.

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

Agora, quaisquer propriedades que appendmos ao ‘serviço’ estarão disponíveis para nós quando passarmos ‘myFactory’ para o nosso controlador.

Agora vamos adicionar algumas variables ​​’privadas’ à nossa function de retorno de chamada. Estes não serão diretamente acessíveis a partir do controlador, mas eventualmente criaremos alguns methods getter / setter em ‘service’ para podermos alterar essas variables ​​’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 variables ​​/ function ao ‘serviço’. Estamos simplesmente criando-os para usá-los ou modificá-los mais tarde.

  • baseUrl é o URL base que a API do iTunes exige
  • _artist é o artista que desejamos pesquisar
  • _finalUrl é o URL final e totalmente construído para o qual faremos a chamada para o iTunes O makeUrl é uma function que irá criar e devolver o nosso URL do iTunes.

Agora que as variables ​​e funções do helper / private estão no lugar, vamos adicionar algumas propriedades ao object ‘service’. Seja o que for que colocarmos em ‘service’, poderemos usar diretamente em qualquer controlador em que passemos ‘myFactory’.

Vamos criar methods setArtist e getArtist que simplesmente retornam ou configuram o artista. Também vamos criar um método que irá chamar a API do iTunes com o nosso URL criado. Esse método retornará uma promise que será cumprida assim que os dados forem retornados da API do iTunes. Se você não teve muita experiência usando promises em Angular, eu recomendo dar um mergulho profundo nelas.

Abaixo de setArtist aceita um artista e permite que você defina o artista. getArtist retorna o artista callItunes chama primeiro makeUrl () para construir o URL que usaremos com nossa solicitação $ http. Em seguida, ele configura um object de promise, faz uma solicitação de $ http com nosso URL final e, em seguida, porque $ http retorna uma promise, podemos chamar .success ou .error após nossa solicitação. Em seguida, resolvemos nossa promise com os dados do iTunes ou a rejeitamos com uma mensagem dizendo “Ocorreu 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 então poderemos chamar nossos methods que anexamos ao nosso object 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 object $ escopo provenientes de dados de ‘myFactory’. O único código complicado acima é se você nunca lidou com promises antes. Como o callItunes está retornando uma promise, podemos usar o método .then () e definir somente $ scope.data.artistData quando nossa promise for cumprida com os dados do iTunes. Você notará que nosso controle é muito “fino”. Toda a nossa lógica e dados persistentes estão localizados em nosso serviço, não em nosso controlador.

2) serviço
Talvez a coisa mais importante a saber ao lidar com a criação de um serviço seja que ele seja instanciado com a palavra-chave “novo”. Para você, gurus do JavaScript, isso deve lhe dar uma grande dica sobre a natureza do código. Para aqueles com um histórico limitado em JavaScript ou para aqueles que não estão muito familiarizados com o que a “nova” palavra-chave realmente faz, vamos revisar alguns fundamentos JavaScript que eventualmente nos ajudarão a entender a natureza de um Serviço.

Para realmente ver as mudanças que ocorrem quando você invoca uma function com a palavra-chave ‘new’, vamos criar uma function e chamá-la com a palavra-chave ‘new’, então vamos mostrar o que o interpretador 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; } 

Esta é uma function típica do construtor de JavaScript. Agora, sempre que invocarmos a function Person usando a palavra-chave ‘new’, ‘this’ será vinculado ao object recém-criado.

Agora vamos adicionar um método ao protótipo de nossa pessoa, para que ele esteja disponível em todas as instâncias de nossa “class” pessoal.

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

Agora, como colocamos a function sayName no protótipo, cada instância de Person poderá chamar a function sayName para alertar o nome dessa instância.

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

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

Então, todos juntos, o código para criar um construtor Person, adicionando uma function ao seu protótipo, criando uma instância Person e, em seguida, chamando a function 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 ‘novo’ em nosso exemplo, podemos chamar um método (sayName) em ‘tyler’ como se fosse um object – é porque é. Então, primeiro, sabemos que nosso construtor Person está retornando um object, se podemos ver isso no código ou não. Segundo, sabemos que, como nossa function sayName está localizada no protótipo e não diretamente na instância Person, o object que a function Person está retornando deve estar delegando a seu protótipo em pesquisas com falha. Em termos mais simples, quando chamamos tyler.sayName (), o interpretador diz “OK, vou olhar o object ‘tyler’ que acabamos de criar, localize a function sayName e, em seguida, chame-o. Espere um minuto, não vejo aqui – tudo 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 ‘nova palavra-chave’ está realmente fazendo em JavaScript. É basicamente um exemplo de código do parágrafo acima. Eu coloquei a ‘visão de 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, ter esse conhecimento do que a palavra-chave ‘new’ realmente faz no JavaScript, criar um Service in Angular deve ser mais fácil de entender.

A maior 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 que estará anexando suas propriedades e methods diretamente a ‘this’, que será então retornado do próprio Serviço. Vamos dar uma olhada nisso em ação.

Ao contrário do que fizemos originalmente com o exemplo Factory, não precisamos criar um object e, em seguida, retornar esse object porque, como mencionado muitas vezes antes, usamos a palavra-chave ‘new’ para que o interpretador crie esse object, tenha ele delegado para é protótipo, depois devolva para nós sem que tenhamos que fazer o trabalho.

Primeiras coisas primeiro, vamos criar nossa function ‘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 eu fiz isso no exemplo da fábrica, se você está confuso, releia o exemplo da 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, vamos append todos os nossos methods que estarão disponíveis em nosso controller para ‘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 passemos 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 eu mencionei antes, quando você realmente entende o que “novo” faz, os Serviços são quase idênticos às fábricas em Angular.

A pista está no nome

Serviços e fábricas são semelhantes uns aos outros. Ambos produzirão um object singleton que pode ser injetado em outros objects e, portanto, são freqüentemente 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 angulares são normalmente apenas objects 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. Sabe como falar com o Github. Sabe sobre URLs e methods. Podemos injetá-lo em um controlador e ele irá gerar e retornar uma promise.

 (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

Fábricas, por outro lado, destinam-se a implementar um padrão de fábrica. Um padrão de fábrica em um em que usamos uma function de fábrica para gerar um object. 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); }) 

Nós faríamos uso disso assim:

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

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

Fábricas podem retornar um construtor

Como uma fábrica simplesmente retorna um object, ele pode retornar qualquer tipo de object que você goste, incluindo uma function construtora, como vemos acima.

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

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

  • Os serviços são construtores novos.
  • Fábricas são chamadas simplesmente e retornam um object.

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

Para ilustrar isso, aqui está o mesmo object 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!"; } } }); 

app.factory (‘fn’, fn) vs. app.service (‘fn’, fn)

Construção

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

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

Com serviços, o Angular invocará a function de construtor chamando new . A function construída é armazenada em cache e injetada.

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

Implementação

Normalmente, as fábricas retornam um literal de object porque o valor de retorno é o que é injetado nos controladores, nos blocos de execução, nas 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 } }); 

Funções de serviço normalmente não retornam nada. Em vez disso, eles executam a boot e expõem as funções. Funções também podem fazer referência a ‘this’, uma vez que foi construído 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, blocos de execução, etc e usados ​​no código do cliente praticamente da mesma maneira. Eles também são 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 deles – eles são tão semelhantes que as diferenças são triviais. Se você escolher um sobre o outro, saiba como eles são construídos, para que você possa implementá-los corretamente.

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

A chave para 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 tem alguma limitação adicional.

Para decidir quando usar, basta ver qual deles permite que você faça o que deseja em menos código. Aqui está uma imagem ilustrando o quão semelhantes eles são:

insira a descrição da imagem aqui

Para um detalhamento completo passo a passo e referência rápida de quando usar cada um, você pode visitar o post do blog em que obtive essa imagem:

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

Eu passei algum tempo tentando descobrir a diferença.

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

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

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

  • Se você se preocupa 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á tem uma function de construtor que você está usando em outro lugar que você também quer usar como um serviço (embora provavelmente não seja muito útil se você quiser injetar algo nele!).

Indiscutivelmente, o padrão de serviço é uma maneira um pouco mais agradável de criar um novo object de um ponto de vista de syntax, mas também é mais dispendioso instanciar. Outros indicaram que o angular usa “novo” para criar o serviço, mas isso não é bem verdade – não é possível fazer isso porque cada 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 function de construtor. Em seguida, ele faz alguns pokery inteligentes para simular o “novo” operador do javascript, invocando o seu construtor com um número variável de argumentos injetáveis ​​- mas você pode deixar de fora esta etapa se você usar o padrão de fábrica diretamente, aumentando muito ligeiramente a eficiência do seu código.

Intereting Posts