angular.service vs angular.factory


1065

Ho visto sia angular.factory () che angular.service () usati per dichiarare i servizi; tuttavia, non riesco a trovare da angular.service nessuna parte nella documentazione ufficiale.

Qual è la differenza tra i due metodi?
Quale dovrebbe essere usato per cosa (supponendo che facciano cose diverse)?



4
Ho cercato "[angularjs] service factory", ma avevo anche ricordato che c'era già una domanda al riguardo (perché a un certo punto ho pensato di scrivere questa / questa domanda).
Mark Rajcok,

2
In una ricerca, le parentesi quadre indicano un tag?
Jacob

11
Le parentesi quadre di @Jacob stanno restringendo la ricerca. [angularjs] direttive - cercherà "direttive" per le domande già taggate con angularjs.
Mahbub,

1
@Mahbub In altre parole, "sì" :)
Brian

Risposte:


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

Ho avuto problemi a avvolgere la testa attorno a questo concetto fino a quando non l'ho messo per me in questo modo:

Servizio : la funzione che scrivi sarà nuova :

  myInjectedService  <----  new myServiceFunction()

Factory : la funzione (costruttore) che scrivi sarà invocata :

  myInjectedFactory  <---  myFactoryFunction()

Quello che fai con quello dipende da te, ma ci sono alcuni schemi utili ...

Come scrivere una funzione di servizio per esporre un'API pubblica:

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

O usando una funzione factory per esporre un'API pubblica:

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 una funzione di fabbrica per restituire un costruttore:

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

Quale usare? ...

Puoi realizzare la stessa cosa con entrambi. Tuttavia, in alcuni casi la fabbrica offre un po 'più di flessibilità per creare un iniettabile con una sintassi più semplice. Questo perché mentre myInjectedService deve essere sempre un oggetto, myInjectedFactory può essere un oggetto, un riferimento di funzione o qualsiasi valore. Ad esempio, se hai scritto un servizio per creare un costruttore (come nell'ultimo esempio sopra), dovrebbe essere istanziato in questo modo:

var myShinyNewObject = new myInjectedService.myFunction()

che è probabilmente meno desiderabile di questo:

var myShinyNewObject = new myInjectedFactory();

(Ma dovresti essere cauto nell'usare questo tipo di modello in primo luogo perché gli oggetti new -ing nei tuoi controller creano dipendenze difficili da rintracciare che sono difficili da deridere per i test. Meglio avere un servizio per gestire una raccolta di oggetti per che usi new()astutamente o volentieri.)


Un'altra cosa, sono tutti i Singleton ...

Inoltre, tieni presente che in entrambi i casi, angolare ti aiuta a gestire un singleton. Indipendentemente da dove o quante volte inietti il ​​tuo servizio o funzione, otterrai lo stesso riferimento allo stesso oggetto o funzione. (Ad eccezione di quando una factory restituisce semplicemente un valore come un numero o una stringa. In tal caso, otterrai sempre lo stesso valore, ma non un riferimento.)


2
Sarebbe meglio chiamarlo un costruttore di oggetti piuttosto che Newable?
MarkSzm,

2
@Hugo, stavo dimostrando che puoi realizzare efficacemente la stessa cosa con entrambi, è solo che la sintassi differirà.
Gil Birman,

105
Non sono sicuro di quante volte dovrò leggere la differenza tra servizio e fabbrica prima di essere convinto che entrambi siano necessari
DMac the Destroyer,

10
Abbiamo già un verbo da dire "a nuovo", è "un'istanza". Solo per riferimento. :)
sscarduzio,

7
Le fabbriche sono funzioni invocate, quindi possono restituire qualsiasi cosa. D'altra parte, i servizi sono istanziati da via angolare new fn(), quindi devono restituire un'istanza.
Gil Birman,

318

In poche parole ..

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


169
Amico, grazie. Non che i dettagli delle altre risposte non siano validi, ma a volte è necessaria la versione da 10 secondi.
R Claven,

4
Basta che la funzione di servizio non restituisca nulla. Il this.name = ... è sufficiente a dimostrare che si sta esponendo un'API.
pixelbits

3
Tuttavia, se si restituisce e si oppone, utilizzerà questo invece di questo. jsfiddle.net/Ne5P8/1221
MrB

@MrB, questa è una normale funzione JavaScript, non specifica per Angular o il contesto di questa domanda.
Om Shankar,

@Om Shankar, la risposta sopra sta dimostrando che la differenza sta nell'uso di questo rispetto a un oggetto restituito. Stavo mostrando che "QUESTO" è il valore predefinito che verrà utilizzato con un servizio, tuttavia se si restituisce un valore funzionerà quasi esattamente come una fabbrica. Tuttavia, il rovescio della medaglia sembra che una fabbrica richieda un valore restituito, altrimenti si verificherà un errore - (mostrato in questo esempio - jsfiddle.net/hmoc0q3v/1 ).
MrB,

247

Ecco le differenze principali:

Servizi

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

Risultato: quando si dichiara serviceName come argomento iniettabile, verrà fornita l' istanza di una funzione passata a module.service.

Utilizzo: potrebbe essere utile per la condivisione di funzioni di utilità utili per richiamare semplicemente aggiungendo ( )il riferimento alla funzione iniettata. Potrebbe anche essere eseguito con injectedArg.call( this )o simili.

fabbriche

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

Risultato: quando si dichiara factoryName come argomento iniettabile, verrà fornito il valore restituito invocando il riferimento della funzione passato a module.factory.

Utilizzo: potrebbe essere utile per restituire una funzione 'class' che può quindi essere modificata per creare istanze.

Ecco un esempio di utilizzo dei servizi e della fabbrica . Maggiori informazioni su AngularJS Service vs Factory .

È inoltre possibile controllare la documentazione di AngularJS e domande simili su StackOverflow confuso su servizio vs fabbrica .


27
Non sono d'accordo con il tuo esempio di utilizzo di una fabbrica. Sia i servizi che le fabbriche (supponendo che venga restituita una funzione. Potrebbe essere solo un valore o un oggetto) possono essere rinnovati. In effetti, un servizio è l'unica opzione garantita per essere ripristinabile poiché viene fornita un'istanza di funzione. Direi che il vantaggio di utilizzare una FABBRICA rispetto a un SERVIZIO è che consente un certo controllo sull'accesso alle proprietà - private e pubbliche di per sé mentre tutte le proprietà del servizio sono per natura esposte. E penso a un fornitore come a una fabbrica di fabbrica - solo che è iniettabile e configurabile al momento della configurazione.
Ha disegnato R

1
@DrewR Grazie per il tuo commento, ho trovato un buon esempio di metodi pubblici e privati che utilizzano una fabbrica: stackoverflow.com/a/14904891/65025
edzillion

Devo essere d'accordo con @DrewR su questo, in realtà. Ho usato fabbriche per restituire oggetti prima, ma onestamente a questo punto potrebbe valere la pena usarlo $providerssempre.
jedd.ahyoung,

il servizio sta istanziando automaticamente il costruttore, giusto?
Martian2049,

1
@DrewR - Da quanto ho capito, è vero che puoi ottenere lo stesso nuovo effetto dal servizio come puoi con una fabbrica, ma non è quello per cui è destinato. Il suo obiettivo principale è quando vuoi solo restituire un oggetto di utilità e per questo fornisce una sintassi più adatta: puoi semplicemente scrivere this.myFunc = function(){}nel tuo servizio (ti salva dalla scrittura del codice per creare l'oggetto come avresti a che fare con una fabbrica ).
BornToCode

137

TL; DR

1) Quando si utilizza una Factory, si crea un oggetto, si aggiungono proprietà, quindi si restituisce lo stesso oggetto. Quando passi questa fabbrica nel tuo controller, quelle proprietà sull'oggetto saranno ora disponibili in quel controller attraverso la tua fabbrica.

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 si utilizza Service , Angular lo crea un'istanza dietro le quinte con la "nuova" parola chiave. Per questo motivo, aggiungi proprietà a "this" e il servizio restituirà "this". Quando si passa il servizio al controller, le proprietà su "this" saranno ora disponibili su quel controller tramite il servizio.

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

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



Non TL; DR

1) Le Factory
Factory sono il modo più popolare per creare e configurare un servizio. Non c'è davvero molto di più di quello che ha detto il TL; DR. Basta creare un oggetto, aggiungere proprietà ad esso, quindi restituire lo stesso oggetto. Quindi quando passi la fabbrica nel tuo controller, quelle proprietà sull'oggetto saranno ora disponibili in quel controller attraverso la tua fabbrica. Un esempio più ampio è di seguito.

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

Ora, qualunque proprietà attribuiamo al "servizio" sarà disponibile per noi quando passiamo "myFactory" nel nostro controller.

Ora aggiungiamo alcune variabili 'private' alla nostra funzione di callback. Questi non saranno direttamente accessibili dal controller, ma alla fine imposteremo alcuni metodi getter / setter su 'service' per essere in grado di modificare queste variabili 'private' quando necessario.

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

Qui noterai che non stiamo associando tali variabili / funzioni al "servizio". Li stiamo semplicemente creando per usarli o modificarli in seguito.

  • baseUrl è l'URL di base richiesto dall'API di iTunes
  • _artista è l'artista che desideriamo cercare
  • _finalUrl è l'URL finale e completo a cui effettueremo la chiamata a iTunes makeUrl è una funzione che creerà e restituirà il nostro URL amichevole di iTunes.

Ora che le nostre variabili e funzioni helper / private sono presenti, aggiungiamo alcune proprietà all'oggetto 'service'. Qualunque cosa mettiamo in "servizio", saremo in grado di utilizzare direttamente in qualsiasi controller in cui passiamo "myFactory".

Creeremo metodi setArtist e getArtist che semplicemente restituiscono o impostano l'artista. Creeremo anche un metodo che chiamerà l'API di iTunes con il nostro URL creato. Questo metodo restituirà una promessa che manterrà una volta che i dati sono tornati dall'API di iTunes. Se non hai avuto molta esperienza nell'uso delle promesse in Angular, ti consiglio vivamente di fare un tuffo profondo su di esse.

Sotto setArtist accetta un artista e ti consente di impostare l'artista. getArtist restituisce l'artista callItunes prima chiama makeUrl () per creare l'URL che useremo con la nostra richiesta $ http. Quindi imposta un oggetto promessa, effettua una richiesta $ http con il nostro URL finale, quindi poiché $ http restituisce una promessa, siamo in grado di chiamare .success o .error dopo la nostra richiesta. Quindi risolviamo la nostra promessa con i dati di iTunes o la rifiutiamo con un messaggio che dice "Si è verificato un errore".

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

Ora la nostra fabbrica è completa. Ora siamo in grado di iniettare "myFactory" in qualsiasi controller e potremo quindi chiamare i nostri metodi che abbiamo collegato al nostro oggetto di servizio (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);
      })
  }
});

Nel controller sopra stiamo iniettando nel servizio "myFactory". Quindi impostiamo le proprietà sul nostro oggetto $ scope che provengono dai dati di 'myFactory'. L'unico codice di cui sopra è se non hai mai avuto a che fare con le promesse prima. Poiché callItunes sta restituendo una promessa, siamo in grado di utilizzare il metodo .then () e impostare $ scope.data.artistData solo dopo che la nostra promessa è stata rispettata con i dati di iTunes. Noterai che il nostro controller è molto "sottile". Tutta la nostra logica e i dati persistenti si trovano nel nostro servizio, non nel nostro controller.

2) Servizio
Forse la cosa più importante da sapere quando si ha a che fare con la creazione di un servizio è che è istanziato con la "nuova" parola chiave. Per voi guru di JavaScript questo dovrebbe darvi un grande suggerimento sulla natura del codice. Per quelli di voi con un background limitato in JavaScript o per quelli che non hanno familiarità con ciò che effettivamente fa la "nuova" parola chiave, passiamo in rassegna alcuni fondamentali di JavaScript che alla fine ci aiuteranno a comprendere la natura di un Servizio.

Per vedere davvero le modifiche che si verificano quando invochi una funzione con la parola chiave "nuova", creiamo una funzione e invocala con la parola chiave "nuova", quindi mostriamo cosa fa l'interprete quando vede la parola chiave "nuova". I risultati finali saranno entrambi uguali.

Per prima cosa creiamo il nostro costruttore.

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

Questa è una tipica funzione di costruzione JavaScript. Ora, ogni volta che invochiamo la funzione Persona usando la parola chiave "new", "this" verrà associato all'oggetto appena creato.

Ora aggiungiamo un metodo sul prototipo della nostra Persona in modo che sia disponibile su ogni istanza della nostra "classe".

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

Ora, poiché mettiamo la funzione sayName sul prototipo, ogni istanza di Person sarà in grado di chiamare la funzione sayName per avvisare il nome dell'istanza.

Ora che abbiamo la nostra funzione di costruzione Person e la nostra funzione sayName sul suo prototipo, creiamo effettivamente un'istanza di Person quindi chiamiamo la funzione sayName.

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

Quindi tutti insieme il codice per creare un costruttore Person, aggiungere una funzione al suo prototipo, creare un'istanza Person e quindi chiamare la funzione sul suo prototipo appare così.

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'

Ora diamo un'occhiata a ciò che sta realmente accadendo quando usi la "nuova" parola chiave in JavaScript. La prima cosa che dovresti notare è che dopo aver usato 'new' nel nostro esempio, siamo in grado di chiamare un metodo (sayName) su 'tyler' proprio come se fosse un oggetto - è perché lo è. Quindi, per prima cosa, sappiamo che il nostro costruttore Person sta restituendo un oggetto, che possiamo vedere o meno nel codice. In secondo luogo, sappiamo che poiché la nostra funzione sayName si trova sul prototipo e non direttamente sull'istanza Person, l'oggetto che la funzione Person sta restituendo deve delegare al suo prototipo in caso di ricerche non riuscite. In termini più semplici, quando chiamiamo tyler.sayName () l'interprete dice “OK, cercherò l'oggetto 'tyler' che abbiamo appena creato, localizziamo la funzione sayName, quindi la chiamiamo. Aspetta un minuto, non lo vedo qui - tutto ciò che vedo è il nome e l'età, fammi controllare il prototipo. Sì, sembra che sia sul prototipo, lascia che lo chiami. ”.

Di seguito è riportato il codice su come pensare a ciò che la "nuova" parola chiave sta effettivamente facendo in JavaScript. È fondamentalmente un esempio di codice del paragrafo precedente. Ho inserito la "vista dell'interprete" o il modo in cui l'interprete vede il codice all'interno delle note.

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

Ora avendo questa conoscenza di ciò che la "nuova" parola chiave fa realmente in JavaScript, la creazione di un servizio in angolare dovrebbe essere più facile da capire.

La cosa più importante da capire quando si crea un servizio è sapere che i servizi sono istanziati con la "nuova" parola chiave. Combinando questa conoscenza con i nostri esempi precedenti, ora dovresti riconoscere che alle tue proprietà e ai tuoi metodi collegherai direttamente "questo", che verrà quindi restituito dal Servizio stesso. Diamo un'occhiata a questo in azione.

A differenza di quello che abbiamo fatto inizialmente con l'esempio Factory, non abbiamo bisogno di creare un oggetto per restituirlo, perché, come menzionato molte volte in precedenza, abbiamo usato la parola chiave "new" in modo che l'interprete creerà quell'oggetto e ne faccia delegare è un prototipo, quindi restituiscilo per noi senza che dobbiamo fare il lavoro.

Per prima cosa, creiamo la nostra funzione "privata" e di supporto. Questo dovrebbe sembrare molto familiare poiché abbiamo fatto esattamente la stessa cosa con la nostra fabbrica. Non spiegherò cosa fa ogni riga qui perché l'ho fatto nell'esempio di fabbrica, se sei confuso, rileggi l'esempio di fabbrica.

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

Ora allegheremo tutti i nostri metodi che saranno disponibili nel nostro controller a "questo".

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

});

Proprio come nella nostra fabbrica, setArtist, getArtist e callItunes saranno disponibili in qualsiasi controller in cui passiamo myService. Ecco il controller myService (che è quasi identico al nostro controller di fabbrica).

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

Come ho detto prima, una volta che capisci davvero cosa fa il "nuovo", i Servizi sono quasi identici alle fabbriche in Angular.


12
Potresti voler fornire un link direttamente al tuo blog. tylermcginnis.com/angularjs-factory-vs-service-vs-provider Ho scoperto che è un po 'più facile da leggere.
Tyler Collier,

3
Niente di sbagliato nel ripetere qui il tuo blog, ma sono d'accordo che si tratta di un post sul blog di greta.
R Claven,

5
Una buona spiegazione dettagliata di ciò che ognuno fa sotto il cofano, ma non è ancora chiaro perché e quando qualcuno dovrebbe scegliere di utilizzare un servizio in una fabbrica. In altre parole, quando preferirò avere un oggetto nuovo rispetto a quello restituito da una fabbrica. Penso che questa sia la più grande confusione.
Demisx,

2
Fondamentalmente, se vuoi creare una connessione persistente a un servizio remoto, come l'API di iTunes menzionata nell'esempio con una connessione costante (stato della connessione, cronologia delle chiamate, archiviazione dei dati), puoi andare con Factory. Se lo implementi come servizio, ogni volta che desideri qualcosa dall'API dovrai ricreare la connessione e non è possibile archiviare nulla al suo interno. Perché ogni volta che si ricrea il servizio, si ottiene un oggetto vuoto / predefinito.
Meki,

4
Non penso sia corretto, @Aznim. Come altri hanno già detto, entrambi forniscono dei singoli.
Cryptovirus,

35

l'indizio è nel nome

Servizi e fabbriche sono simili tra loro. Entrambi produrranno un oggetto singleton che può essere iniettato in altri oggetti, e quindi sono spesso usati in modo intercambiabile.

Sono destinati a essere utilizzati semanticamente per implementare diversi modelli di progettazione.

I servizi sono per l'implementazione di un modello di servizio

Un modello di servizio è quello in cui l'applicazione viene suddivisa in unità di funzionalità logicamente coerenti. Un esempio potrebbe essere un accessor API o un set di logica aziendale.

Ciò è particolarmente importante in Angular perché i modelli Angular sono in genere solo oggetti JSON estratti da un server, e quindi abbiamo bisogno di un posto dove mettere la nostra logica aziendale.

Ecco un servizio Github per esempio. Sa parlare con Github. Conosce url e metodi. Possiamo iniettarlo in un controller e genererà e restituirà una 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);
      }
    });
  )();

Le fabbriche implementano un modello di fabbrica

Le fabbriche, d'altra parte, hanno lo scopo di implementare un modello di fabbrica. Un modello di fabbrica in uno in cui utilizziamo una funzione di fabbrica per generare un oggetto. In genere potremmo usarlo per costruire modelli. Ecco una fabbrica che restituisce un costruttore Autore:

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

Useremmo così:

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

Si noti che le fabbriche restituiscono anche singoli.

Le fabbriche possono restituire un costruttore

Poiché una factory restituisce semplicemente un oggetto, può restituire qualsiasi tipo di oggetto che ti piace, inclusa una funzione di costruzione, come vediamo sopra.

Le fabbriche restituiscono un oggetto; i servizi sono nuovi

Un'altra differenza tecnica sta nel modo in cui i servizi e le fabbriche sono composti. Una funzione di servizio verrà rinnovata per generare l'oggetto. Verrà chiamata una funzione di fabbrica che restituirà l'oggetto.

  • I servizi sono costruttori nuovi.
  • Le fabbriche vengono semplicemente chiamate e restituiscono un oggetto.

Ciò significa che in un servizio, aggiungiamo a "questo" che, nel contesto di un costruttore, punterà all'oggetto in costruzione.

Per illustrare questo, ecco lo stesso semplice oggetto creato utilizzando un servizio e una factory:

angular.module('app', [])
  .service('helloService', function() {
    this.sayHello = function() {
      return "Hello!";
    }
  })
  .factory('helloFactory', function() {
    return {
      sayHello: function() {
        return "Hello!";
      }
    }
  });

2
grande spiegazione, grazie! inoltre esiste un tipo nel codice di esempio Factory dove Authordovrebbe essere il parametro injector Person.
mikhail-t,

Grazie @ mik-T, ho corretto i refusi.
superluminario

1
L'uso del modello di servizio non è corretto - questo dovrebbe essere una fabbrica. Se chiami .factory () invece di .service () vedrai che funziona esattamente allo stesso modo. Lo schema di servizio deve essere fornito con una funzione di costruzione, non con una funzione che restituisce un nuovo oggetto. Le chiamate angolari (effettivamente) "nuove" sulla funzione del costruttore. L'unico motivo per cui il servizio funziona è che se si chiama "nuovo" su una funzione di costruzione che restituisce un oggetto, si ottiene effettivamente l'oggetto restituito anziché quello costruito. E le fabbriche possono essere utilizzate per creare tutto ciò che vuoi, non solo modelli.
Dan King,

27

Tutte le risposte qui sembrano riguardare il servizio e la fabbrica, ed è valido dal momento che era quello che veniva chiesto. Ma è anche importante tenere a mente che ci sono molti altri tra cui provider(), value(), e constant().

La chiave da ricordare è che ognuno è un caso speciale dell'altro. Ogni caso speciale lungo la catena ti consente di fare la stessa cosa con meno codice. Ognuno ha anche qualche limitazione aggiuntiva.

Per decidere quando usare quale vedi, quale ti permette di fare quello che vuoi con meno codice. Ecco un'immagine che illustra quanto sono simili:

inserisci qui la descrizione dell'immagine

Per una descrizione dettagliata e dettagliata di quando utilizzarli, puoi visitare il post sul blog da cui ho ricevuto questa immagine:

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


3
@jacob forse è così, ma penso che il concetto generale non solo di quando usarli, ma che siano essenzialmente variazioni della stessa cosa sia importante.
Luis Perez,

1
@LuisPerez Il link al tuo blog e al video che spiega la differenza è davvero fantastico. È più facile da capire con quegli esempi tratti dal video :)
Alin Ciocan,

24

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

Costruzione

Con le fabbriche, Angular invocherà la funzione per ottenere il risultato. È il risultato che viene memorizzato nella cache e iniettato.

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

Con i servizi, Angular invocherà la funzione di costruzione chiamando new . La funzione costruita viene memorizzata nella cache e iniettata.

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

Implementazione

Le fabbriche in genere restituiscono un oggetto letterale perché il valore restituito è ciò che viene iniettato in controller, blocchi di esecuzione, direttive, ecc.

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

Le funzioni di servizio in genere non restituiscono nulla. Al contrario, eseguono l'inizializzazione ed espongono le funzioni. Le funzioni possono anche fare riferimento a "questo" poiché sono state costruite usando "nuovo".

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

Conclusione

Quando si tratta di utilizzare fabbriche o servizi sono entrambi molto simili. Vengono iniettati in controller, direttive, blocco di esecuzione, ecc. E utilizzati nel codice client praticamente nello stesso modo. Sono anche entrambi singleton - il che significa che la stessa istanza è condivisa tra tutti i luoghi in cui viene iniettato il servizio / fabbrica.

Quindi quale dovresti preferire? Uno dei due - sono così simili che le differenze sono banali. Se scegli l'uno sull'altro, tieni presente come sono costruiti, in modo da poterli implementare correttamente.


Le funzioni di servizio non "non restituiscono nulla", implicitamente restituiscono l'oggetto costruito SE non si specifica la propria dichiarazione di ritorno (in quest'ultimo caso l'oggetto che si è restituito è ciò che verrà creato e memorizzato nella cache, simile a una factory).
Cryptovirus,

Penso che tu stia interpretando male ... Quando dico return, intendo dal punto di vista dell'implementazione della funzione di servizio
pixelbits,

sei sicuro che la fabbrica sia anche una città?
Martian2049

5

Ho trascorso un po 'di tempo a cercare di capire la differenza.

E penso che la funzione di fabbrica utilizzi il modello del modulo e la funzione di servizio usi il modello standard del costruttore di script java.


2

Il modello di fabbrica è più flessibile in quanto può restituire funzioni, valori e oggetti.

Non c'è molto senso nel modello di servizio IMHO, in quanto tutto ciò che puoi fare con una fabbrica è altrettanto semplice. Le eccezioni potrebbero essere:

  • Se ti interessa il tipo dichiarato del tuo servizio istanziato per qualche motivo - se usi il modello di servizio, il tuo costruttore sarà il tipo di nuovo servizio.
  • Se hai già una funzione di costruzione che stai utilizzando altrove che desideri utilizzare anche come servizio (anche se probabilmente non è molto utile se vuoi iniettare qualcosa in esso!).

Probabilmente, il modello di servizio è un modo leggermente migliore per creare un nuovo oggetto dal punto di vista della sintassi, ma è anche più costoso da istanziare. Altri hanno indicato che angular usa "nuovo" per creare il servizio, ma questo non è del tutto vero - non è in grado di farlo perché ogni costruttore di servizi ha un numero diverso di parametri. Ciò che effettivamente angolare fa è utilizzare internamente il modello di fabbrica per avvolgere la funzione del costruttore. Quindi fa un po 'di pokery intelligente per simulare il "nuovo" operatore javascript, invocando il tuo costruttore con un numero variabile di argomenti iniettabili - ma puoi tralasciare questo passaggio se usi semplicemente il modello di fabbrica direttamente, aumentando così leggermente l'efficienza del tuo codice.


I servizi sono più efficienti da costruire rispetto alle fabbriche poiché le fabbriche utilizzano chiusure relativamente costose e i servizi (classi) possono trarre vantaggio dal prototipo.
Jacob,

@jacob Non sai cosa intendi per chiusure? La factory è solo una funzione che restituisce un oggetto. Devi usare una chiusura solo se l'oggetto restituito richiede uno stato "privato". Dovresti comunque fare la stessa cosa se usi un costruttore (servizio). Prendo in considerazione il tuo prototipo, sebbene tu possa ancora farlo in una fabbrica se lo desideri.
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 } }); } Sebbene sia MyFactory che MyService utilizzino il prototipo, MyFactory subisce ancora un duro colpo in termini di prestazioni dovendo costruire l'oggetto che viene restituito. In entrambi gli esempi, hanno privati, ma in MyService non c'è relativamente differenza di prestazioni.
Jacob

1
Per me, la differenza è se voglio usare direttamente la fabbrica senza un metodo: MyFactory(someArgument)(ex $http()). Questo non è possibile con un servizio come si sarebbe fa riferimento il costruttore: MyService(someArgument).
Jacob

In fase di costruzione dell'oggetto, non vedo davvero come factory = {} sia un hit prestazionale, più che javascript che inizializza "questo" per te quando chiama il tuo costruttore? E penso che il più grande successo prestazionale sia dal lato angolare quando avvolge il costruttore in una fabbrica e quindi deve saltare attraverso i cerchi per simulare "nuovi" in modo da poter iniettare le tue dipendenze.
Dan King,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.