angular.service vs angular.factory

1065

He visto que angular.factory () y angular.service () se utilizan para declarar servicios; Sin embargo, no puedo encontrar angular.service ningún lugar en la documentación oficial.

¿Cuál es la diferencia entre los dos métodos?
¿Cuál debería usarse para qué (suponiendo que hagan cosas diferentes)?

jacob
fuente
25
posible duplicado de confundido sobre servicio vs fábrica
Mark Rajcok
44
Busqué "[angularjs] service factory", pero también recordé que ya había una pregunta al respecto (porque pensé en escribir esa / esta pregunta yo mismo en un momento).
Mark Rajcok
2
En una búsqueda, ¿los corchetes significan una etiqueta?
jacob
11
@Jacob Los corchetes están reduciendo su búsqueda. directivas [angularjs]: buscará 'directivas' para preguntas ya etiquetadas con angularjs.
Mahbub
1
@Mahbub En otras palabras, "sí" :)
Brian

Respuestas:

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

Tuve problemas para comprender este concepto hasta que me lo planteé de esta manera:

Servicio : la función que escriba será nueva :

  myInjectedService  <----  new myServiceFunction()

Fábrica : se invocará la función (constructor) que escriba :

  myInjectedFactory  <---  myFactoryFunction()

Lo que hagas con eso depende de ti, pero hay algunos patrones útiles ...

Como escribir una función de servicio para exponer una API pública:

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

O usando una función de fábrica para exponer una 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();

O usando un función de fábrica para devolver un constructor:

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

¿Cuál usar?

Puedes lograr lo mismo con ambos. Sin embargo, en algunos casos la fábrica le brinda un poco más de flexibilidad para crear un inyectable con una sintaxis más simple. Eso es porque mientras myInjectedService siempre debe ser un objeto, myInjectedFactory puede ser un objeto, una referencia de función o cualquier valor. Por ejemplo, si escribió un servicio para crear un constructor (como en el último ejemplo anterior), debería crearse una instancia de esta manera:

var myShinyNewObject = new myInjectedService.myFunction()

lo cual es posiblemente menos deseable que esto:

var myShinyNewObject = new myInjectedFactory();

(Pero debe tener cuidado al usar este tipo de patrón en primer lugar porque es nuevo objetos en sus controladores crean dependencias difíciles de rastrear que son difíciles de burlar para las pruebas. Es mejor tener un servicio que administre una colección de objetos para que usar new()astutamente)


Una cosa más, todos son Singletons ...

También tenga en cuenta que en ambos casos, angular le ayuda a administrar un singleton. Independientemente de dónde o cuántas veces inyecte su servicio o función, obtendrá la misma referencia al mismo objeto o función. (Con la excepción de cuando una fábrica simplemente devuelve un valor como un número o una cadena. En ese caso, siempre obtendrá el mismo valor, pero no una referencia).

Gil Birman
fuente
2
¿Sería mejor llamarlo constructor de objetos que Newable?
marksyzm
2
@Hugo, estaba demostrando que puedes lograr efectivamente lo mismo con ambos, es solo que la sintaxis será diferente.
Gil Birman
105
No estoy seguro de cuántas veces tendré que leer sobre la diferencia entre el servicio y la fábrica antes de convencerme de que ambos son necesarios
DMac the Destroyer
10
Ya tenemos un verbo para decir "a nuevo", es "instanciar". Solo para referencia. :)
sscarduzio
77
Las fábricas son funciones que se invocan, por lo que pueden devolver cualquier cosa. Por otro lado, los servicios se instancian por vía angular new fn(), por lo que deben devolver una instancia.
Gil Birman
318

Simplemente pon ..

// 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
fuente
169
Amigo, gracias. No es que los detalles de las otras respuestas no sean válidos, pero algunas veces necesitas la versión de 10 segundos.
R Claven
44
Simplemente haga que la función de servicio no devuelva nada. El this.name = ... es suficiente para mostrar que se está exponiendo una API.
pixelbits
3
Sin embargo, si devuelve y se opone, lo usará en lugar de esto. jsfiddle.net/Ne5P8/1221
MrB
@MrB, esa es una característica normal de JavaScript, no específica de Angular o el contexto de esta pregunta.
Om Shankar
@ Om Shankar, la respuesta anterior muestra que la diferencia es el uso de este frente a un objeto devuelto. Estaba mostrando que "ESTE" es el valor predeterminado que se utilizará con un servicio, sin embargo, si devuelve un valor, actuará casi exactamente como una fábrica. Sin embargo, por otro lado, una fábrica parece requerir un valor devuelto; de lo contrario, se producirá un error (se muestra en este ejemplo: jsfiddle.net/hmoc0q3v/1 ).
MrB
247

Aquí están las principales diferencias:

Servicios

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

Resultado: al declarar serviceName como argumento inyectable, se le proporcionará la instancia de una función pasada a module.service.

Uso: podría ser útil para compartir funciones de utilidad que son útiles para invocar simplemente agregando ( )a la referencia de función inyectada. También podría ejecutarse coninjectedArg.call( this ) o similar.

Suerte

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

Resultado: al declarar factoryName como argumento inyectable, se le proporcionará el valor que se devuelve invocando la referencia de función pasada amodule.factory .

Uso: podría ser útil para devolver una 'clase' que luego se puede actualizar para crear instancias.

Aquí hay un ejemplo de uso de servicios y fábrica . Leer más sobre AngularJS Service vs Factory .

También puede consultar la documentación de AngularJS y una pregunta similar sobre stackoverflow confundido sobre el servicio frente a la fábrica .

Chishbra Manish
fuente
27
No estoy de acuerdo con su ejemplo de uso de una fábrica. Tanto los servicios como las fábricas (suponiendo que se devuelva una función. Podría ser solo un valor o un objeto) pueden renovarse. De hecho, un servicio es la única opción que se garantiza que sea nueva ya que se le proporciona una instancia de función. Diría que el beneficio de usar una FÁBRICA sobre un SERVICIO es que permite cierto control sobre el acceso a propiedades, privadas y públicas per se, mientras que todas las propiedades del servicio están expuestas por naturaleza. Y pienso en un proveedor como una fábrica de una fábrica, solo que es inyectable y configurable en el momento de la configuración.
Drew R
1
@DrewR Gracias por tu comentario, encontré un buen ejemplo de métodos públicos y privados usando una Fábrica: stackoverflow.com/a/14904891/65025
edzillion
Tengo que estar de acuerdo con @DrewR en este caso, en realidad. He usado fábricas para devolver objetos antes, pero honestamente en este punto, podría valer la pena usarlo $providerstodo el tiempo.
jedd.ahyoung
El servicio es una instancia automática del constructor, ¿verdad?
Martian2049
1
@DrewR: desde mi punto de vista, es cierto que puede lograr el mismo nuevo efecto posible del servicio que puede lograr con una fábrica, pero no es para lo que está destinado. Su objetivo principal es cuando solo desea devolver algún objeto de utilidad y para eso proporciona una sintaxis más adecuada: simplemente puede escribir this.myFunc = function(){}en su servicio (le ahorra escribir código para crear el objeto como lo haría con una fábrica )
BornToCode
137

TL; DR

1) Cuando utiliza una Fábrica , crea un objeto, le agrega propiedades y luego devuelve el mismo objeto. Cuando pasa esta fábrica a su controlador, esas propiedades en el objeto ahora estarán disponibles en ese controlador a través de su 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) Cuando usa el Servicio , Angular lo instancia detrás de escena con la palabra clave 'nuevo'. Debido a eso, agregará propiedades a 'this' y el servicio devolverá 'this'. Cuando pasa el servicio a su controlador, esas propiedades en 'esto' ahora estarán disponibles en ese controlador a través de su servicio.

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

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



No TL; DR

1)
Fábricas Las fábricas son la forma más popular de crear y configurar un servicio. Realmente no hay mucho más de lo que dice el TL; DR. Simplemente crea un objeto, le agrega propiedades y luego devuelve el mismo objeto. Luego, cuando pase la fábrica a su controlador, esas propiedades en el objeto ahora estarán disponibles en ese controlador a través de su fábrica. Un ejemplo más extenso está abajo.

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

Ahora, cualquier propiedad que adjuntemos al 'servicio' estará disponible cuando pasemos 'myFactory' a nuestro controlador.

Ahora agreguemos algunas variables 'privadas' a nuestra función de devolución de llamada. Estos no serán accesibles directamente desde el controlador, pero eventualmente configuraremos algunos métodos getter / setter en 'servicio' para poder alterar estas variables 'privadas' cuando sea necesario.

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

Aquí notará que no estamos asociando esas variables / funciones a 'servicio'. Simplemente los estamos creando para usarlos o modificarlos más tarde.

  • baseUrl es la URL base que requiere la API de iTunes
  • _artist es el artista que deseamos buscar
  • _finalUrl es la URL final y totalmente construida a la que haremos la llamada a iTunes makeUrl es una función que creará y devolverá nuestra URL amigable de iTunes.

Ahora que nuestras funciones y variables auxiliares / privadas están en su lugar, agreguemos algunas propiedades al objeto 'servicio'. Independientemente de lo que le demos al 'servicio', podremos usarlo directamente en cualquier controlador al que le pasemos 'myFactory'.

Vamos a crear métodos setArtist y getArtist que simplemente devuelven o configuran al artista. También vamos a crear un método que llame a la API de iTunes con nuestra URL creada. Este método devolverá una promesa que se cumplirá una vez que los datos hayan regresado de la API de iTunes. Si no ha tenido mucha experiencia usando promesas en Angular, le recomiendo que profundice en ellas.

A continuación, setArtist acepta un artista y le permite configurarlo. getArtist devuelve el artista callItunes primero llama a makeUrl () para construir la URL que usaremos con nuestra solicitud $ http. Luego configura un objeto de promesa, realiza una solicitud de $ http con nuestra url final, luego, debido a que $ http devuelve una promesa, podemos llamar a .success o .error después de nuestra solicitud. Luego resolvemos nuestra promesa con los datos de iTunes, o la rechazamos con un mensaje que dice 'Hubo un error'.

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

Ahora nuestra fábrica está completa. Ahora podemos inyectar 'myFactory' en cualquier controlador y luego podremos llamar a nuestros métodos que adjuntamos a nuestro objeto de servicio (setArtist, getArtist y 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);
      })
  }
});

En el controlador anterior, estamos inyectando en el servicio 'myFactory'. Luego establecemos propiedades en nuestro objeto $ scope que provienen de datos de 'myFactory'. El único código complicado anterior es si nunca antes ha tratado con promesas. Debido a que callItunes está devolviendo una promesa, podemos usar el método .then () y solo establecemos $ scope.data.artistData una vez que nuestra promesa se cumpla con los datos de iTunes. Notarás que nuestro controlador es muy 'delgado'. Todos nuestros datos lógicos y persistentes se encuentran en nuestro servicio, no en nuestro controlador.

2) servicio
Quizás lo más importante que se debe saber cuando se trata de crear un Servicio es que se instancia con la palabra clave 'nueva'. Para sus gurús de JavaScript, esto debería darle una gran pista sobre la naturaleza del código. Para aquellos de ustedes con antecedentes limitados en JavaScript o para aquellos que no están demasiado familiarizados con lo que realmente hace la 'nueva' palabra clave, revisemos algunos fundamentos de JavaScript que eventualmente nos ayudarán a comprender la naturaleza de un Servicio.

Para ver realmente los cambios que ocurren cuando invoca una función con la palabra clave 'nueva', creemos una función e invoquémosla con la palabra clave 'nueva', luego muestremos qué hace el intérprete cuando ve la palabra clave 'nueva'. Los resultados finales serán los mismos.

Primero creemos nuestro Constructor.

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

Esta es una función típica de constructor de JavaScript. Ahora, cada vez que invoquemos la función Persona usando la palabra clave 'nuevo', 'esto' estará vinculado al objeto recién creado.

Ahora agreguemos un método al prototipo de nuestra Persona para que esté disponible en cada instancia de nuestra 'clase' de Persona.

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

Ahora, debido a que ponemos la función sayName en el prototipo, cada instancia de Person podrá llamar a la función sayName para alertar el nombre de esa instancia.

Ahora que tenemos nuestra función de constructor Person y nuestra función sayName en su prototipo, creemos una instancia de Person y luego llamemos a la función sayName.

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

Entonces, todo el código para crear un constructor de Persona, agregar una función a su prototipo, crear una instancia de Persona y luego llamar a la función en su prototipo se ve así.

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'

Ahora echemos un vistazo a lo que realmente está sucediendo cuando usa la palabra clave 'nuevo' en JavaScript. Lo primero que debe notar es que después de usar 'nuevo' en nuestro ejemplo, podemos llamar a un método (sayName) en 'tyler' como si fuera un objeto, eso es porque lo es. Primero, sabemos que nuestro constructor de Persona está devolviendo un objeto, ya sea que podamos ver eso en el código o no. En segundo lugar, sabemos que debido a que nuestra función sayName se encuentra en el prototipo y no directamente en la instancia de Person, el objeto que devuelve la función Person debe delegarse en su prototipo en búsquedas fallidas. En términos más simples, cuando llamamos a tyler.sayName () el intérprete dice "OK, voy a buscar el objeto 'tyler' que acabamos de crear, ubicar la función sayName y luego llamarla. Espera un momento, no lo veo aquí, todo lo que veo es nombre y edad, Déjame comprobar el prototipo. Sí, parece que está en el prototipo, déjenme llamarlo ".

A continuación se muestra el código de cómo puede pensar qué está haciendo la palabra clave 'nueva' en JavaScript. Básicamente es un ejemplo de código del párrafo anterior. Puse la 'vista del intérprete' o la forma en que el intérprete ve el código dentro de las 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;
}

Ahora que conocemos lo que realmente hace la 'nueva' palabra clave en JavaScript, crear un Servicio en Angular debería ser más fácil de entender.

Lo más importante para entender al crear un Servicio es saber que los Servicios se instancian con la palabra clave "nueva". Combinando ese conocimiento con nuestros ejemplos anteriores, ahora debería reconocer que adjuntará sus propiedades y métodos directamente a 'esto', que luego será devuelto por el Servicio mismo. Echemos un vistazo a esto en acción.

A diferencia de lo que hicimos originalmente con el ejemplo de Factory, no necesitamos crear un objeto y luego devolverlo porque, como se mencionó muchas veces antes, usamos la palabra clave 'new' para que el intérprete cree ese objeto, haga que delegue en es prototipo, luego devuélvanoslo sin que tengamos que hacer el trabajo.

Primero lo primero, creemos nuestra función 'privada' y auxiliar. Esto debería parecer muy familiar ya que hicimos exactamente lo mismo con nuestra fábrica. No explicaré qué hace cada línea aquí porque lo hice en el ejemplo de fábrica, si está confundido, vuelva a leer el ejemplo 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;
  }
});

Ahora, adjuntaremos todos nuestros métodos que estarán disponibles en nuestro controlador a 'esto'.

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;
  }

});

Ahora, al igual que en nuestra fábrica, setArtist, getArtist y callItunes estarán disponibles en cualquier controlador al que pasamos myService. Aquí está el controlador myService (que es casi exactamente el mismo que nuestro 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 mencioné antes, una vez que realmente comprende lo que hace 'nuevo', los Servicios son casi idénticos a las fábricas en Angular.

Tyler McGinnis
fuente
12
Es posible que desee proporcionar un enlace directamente a su blog. tylermcginnis.com/angularjs-factory-vs-service-vs-provider Me pareció un poco más fácil de leer.
Tyler Collier
3
No hay nada malo en repetir tu blog aquí, pero estoy de acuerdo en que es una publicación de blog de Greta.
R Claven
55
Una buena explicación detallada de lo que cada uno hace bajo el capó, pero aún no está claro por qué y cuándo alguien elegiría usar un Servicio en lugar de una Fábrica. En otras palabras, ¿cuándo preferiría tener un objeto nuevo frente al que devuelve una fábrica? Creo que esta es la mayor confusión.
retiro el
2
Básicamente, si desea crear una conexión persistente a un servicio remoto, como la API de iTunes mencionada en el ejemplo con una conexión constante (estado de conexión, historial de llamadas, almacenamiento de datos), puede ir a Factory. Si lo implementa como un Servicio, cada vez que desee algo de la API deberá volver a crear la conexión y realmente no podrá almacenar nada en ella. Porque cada vez que vuelve a crear el servicio, obtendrá un objeto en blanco / predeterminado.
Meki
44
No creo que sea correcto, @Aznim. Como han dicho otros, ambos proporcionan singletons.
Criptovirus
35

La pista está en el nombre

Los servicios y las fábricas son similares entre sí. Ambos producirán un objeto singleton que se puede inyectar en otros objetos y, por lo tanto, a menudo se usan indistintamente.

Están destinados a ser utilizados semánticamente para implementar diferentes patrones de diseño.

Los servicios son para implementar un patrón de servicio

Un patrón de servicio es aquel en el que su aplicación se divide en unidades de funcionalidad lógicamente consistentes. Un ejemplo podría ser un descriptor de acceso a la API o un conjunto de lógica empresarial.

Esto es especialmente importante en Angular porque los modelos angulares generalmente son solo objetos JSON extraídos de un servidor, por lo que necesitamos un lugar para poner nuestra lógica de negocios.

Aquí hay un servicio de Github, por ejemplo. Sabe hablar con Github. Sabe sobre urls y métodos. Podemos inyectarlo en un controlador, y generará y devolverá una promesa.

(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);
      }
    });
  )();

Las fábricas implementan un patrón de fábrica

Las fábricas, por otro lado, están destinadas a implementar un patrón de fábrica. Un patrón de fábrica en el que usamos una función de fábrica para generar un objeto. Por lo general, podríamos usar esto para construir modelos. Aquí hay una fábrica que devuelve un constructor Autor:

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

Haríamos uso de esto así:

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

Tenga en cuenta que las fábricas también devuelven singletons.

Las fábricas pueden devolver un constructor

Como una fábrica simplemente devuelve un objeto, puede devolver cualquier tipo de objeto que desee, incluida una función de constructor, como vemos más arriba.

Las fábricas devuelven un objeto; los servicios son renovables

Otra diferencia técnica está en la forma en que se componen los servicios y las fábricas. Se actualizará una función de servicio para generar el objeto. Se llamará a una función de fábrica y devolverá el objeto.

  • Los servicios son constructores renovables.
  • Las fábricas simplemente se llaman y devuelven un objeto.

Esto significa que en un servicio, agregamos "esto" que, en el contexto de un constructor, apuntará al objeto en construcción.

Para ilustrar esto, aquí está el mismo objeto simple creado usando un servicio y una fábrica:

angular.module('app', [])
  .service('helloService', function() {
    this.sayHello = function() {
      return "Hello!";
    }
  })
  .factory('helloFactory', function() {
    return {
      sayHello: function() {
        return "Hello!";
      }
    }
  });
superluminario
fuente
2
gran explicación, gracias! también hay un tipo en el código de muestra de Fábricas donde Authordebería estar el parámetro del inyector Person.
mikhail-t
Gracias @ mik-T, arreglé los errores tipográficos.
superluminary
1
Su uso del patrón de servicio es incorrecto; esto debería ser una fábrica. Si llama a .factory () en lugar de .service () verá que funciona exactamente igual. El patrón de servicio está destinado a ser suministrado con una función constructora, no una función que devuelve un nuevo objeto. Angular (efectivamente) llama "nuevo" en su función de constructor. La única razón por la que su servicio funciona es que si llama "nuevo" a una función de constructor que devuelve un objeto, en realidad recupera el objeto devuelto en lugar del construido. Y las fábricas se pueden usar para crear lo que quieras, no solo modelos.
Dan King
27

Todas las respuestas aquí parecen estar relacionadas con el servicio y la fábrica, y eso es válido ya que eso era lo que se preguntaba. Pero también es importante tener en cuenta que hay varios otros provider(), incluidos value(), yconstant() .

La clave para recordar es que cada uno es un caso especial del otro. Cada caso especial en la cadena le permite hacer lo mismo con menos código. Cada uno también tiene alguna limitación adicional.

Para decidir cuándo usar cuál solo ve cuál le permite hacer lo que quiere en menos código. Aquí hay una imagen que ilustra cuán similares son:

ingrese la descripción de la imagen aquí

Para obtener un desglose completo paso a paso y una referencia rápida de cuándo usar cada uno, puede visitar la publicación del blog de donde obtuve esta imagen:

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

Luis perez
fuente
3
@jacob tal vez sea así, pero creo que el concepto general no solo de cuándo usar cada uno, sino que todos son esencialmente variaciones de lo mismo es importante.
Luis Perez
1
@LuisPerez El enlace a su blog y el video que explica la diferencia es realmente genial. Es más fácil de entender con esos ejemplos del video :)
Alin Ciocan
24

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

Construcción

Con las fábricas, Angular invocará la función para obtener el resultado. Es el resultado que se almacena en caché e inyectado.

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

Con los servicios, Angular invocará la función constructora llamando a new . La función construida se almacena en caché e inyecta.

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

Implementación

Las fábricas suelen devolver un objeto literal porque el valor de retorno es lo que se inyecta en los controladores, bloques de ejecución, directivas, 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
         }
  });

Las funciones de servicio generalmente no devuelven nada. En cambio, realizan la inicialización y exponen funciones. Las funciones también pueden hacer referencia a 'esto' ya que se construyó usando 'nuevo'.

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

Conclusión

Cuando se trata de usar fábricas o servicios, ambos son muy similares. Se inyectan en controladores, directivas, bloques de ejecución, etc., y se usan en el código del cliente de la misma manera. También son ambos singletons, lo que significa que la misma instancia se comparte entre todos los lugares donde se inyecta el servicio / fábrica.

Entonces, ¿cuál debería preferir? Cualquiera de los dos: son tan similares que las diferencias son triviales. Si elige uno sobre el otro, solo tenga en cuenta cómo se construyen, para que pueda implementarlos correctamente.

bits de píxel
fuente
Las funciones de servicio no "no devuelven nada", devuelven implícitamente el objeto construido SI no especifica su propia declaración de devolución (en este último caso, el objeto que devolvió es lo que se creará y almacenará en caché, similar a una fábrica).
Criptovirus
Creo que lo estás malinterpretando ... Cuando digo regresar, me refiero desde el punto de vista de la implementación de la función de servicio
pixelbits
¿Estás seguro de que la fábrica también es una ciudad única?
Martian2049
5

He pasado algún tiempo tratando de descubrir la diferencia.

Y creo que la función de fábrica usa el patrón de módulo y la función de servicio usa el patrón estándar de Java Script Constructor.

PD.
fuente
2

El patrón de fábrica es más flexible, ya que puede devolver funciones y valores, así como objetos.

No tiene mucho sentido en el patrón de servicio en mi humilde opinión, ya que todo lo que hace puede hacerlo fácilmente con una fábrica. Las excepciones pueden ser:

  • Si por algún motivo le importa el tipo declarado de su servicio instanciado, si utiliza el patrón de servicio, su constructor será el tipo del nuevo servicio.
  • Si ya tiene una función de constructor que está usando en otro lugar que también desea usar como servicio (¡aunque probablemente no sea muy útil si desea inyectarle algo!).

Podría decirse que el patrón de servicio es una forma un poco mejor de crear un nuevo objeto desde el punto de vista de la sintaxis, pero también es más costoso crear instancias. Otros han indicado que angular usa "nuevo" para crear el servicio, pero esto no es del todo cierto, no puede hacerlo porque cada constructor de servicios tiene un número diferente de parámetros. Lo que angular realmente hace es usar el patrón de fábrica internamente para ajustar su función de constructor. Luego, hace un poco de ingenioso juego de jiggery para simular el "nuevo" operador de JavaScript, invocando a su constructor con un número variable de argumentos inyectables, pero puede omitir este paso si solo usa el patrón de fábrica directamente, lo que aumenta ligeramente la eficiencia de su código.

Dan King
fuente
Los servicios son más eficientes de construir que las fábricas, ya que las fábricas usan cierres relativamente caros y los servicios (clases) pueden aprovechar el prototipo.
jacob
@jacob ¿No estás seguro de lo que quieres decir con los cierres? La fábrica es solo una función que devuelve un objeto. Solo tiene que usar un cierre si su objeto devuelto requiere un estado "privado". Aún tendría que hacer lo mismo si usara un constructor (servicio). Sin embargo, me refiero a su prototipo, aunque aún podría hacerlo en una fábrica si lo desea.
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 } }); } Si bien MyFactory y MyService usan un prototipo, MyFactory aún sufre un impacto en el rendimiento al tener que construir el objeto que se devuelve. En ambos ejemplos, tienen elementos privados, pero en MyService no hay diferencias de rendimiento.
jacob
1
Para mí, la diferencia es si quiero usar la fábrica directamente sin un método: MyFactory(someArgument)(ej $http().). Eso no es posible con un servicio como usted estaría haciendo referencia al constructor: MyService(someArgument).
jacob
En el tiempo de construcción de objetos, realmente no veo cómo factory = {} es un éxito en el rendimiento, más que javascript inicializando "esto" para ti cuando llama a tu constructor. Y creo que el mayor impacto en el rendimiento está en el lado angular cuando envuelve a su constructor en una fábrica y luego tiene que saltar a través de aros para simular "nuevo" para que pueda inyectar sus dependencias.
Dan King