angle.service vs angle.factory

1065

Ich habe sowohl angle.factory () als auch angle.service () gesehen, die zum Deklarieren von Diensten verwendet wurden. Ich kann jedoch angular.service nirgendwo in der offiziellen Dokumentation finden.

Was ist der Unterschied zwischen den beiden Methoden?
Welche sollten für was verwendet werden (vorausgesetzt, sie machen verschiedene Dinge)?

Jacob
quelle
25
Mögliches Duplikat von verwirrt über Service gegen Fabrik
Mark Rajcok
4
Ich suchte nach "[anglejs] service factory", aber ich hatte mich auch daran erinnert, dass es bereits eine Frage dazu gab (weil ich darüber nachdachte, diese / diese Frage einmal selbst zu schreiben).
Mark Rajcok
2
Bedeuten die eckigen Klammern bei einer Suche ein Tag?
Jacob
11
@Jacob Square Brackets schränken Ihre Suche ein. [anglejs] Direktiven - sucht nach 'Direktiven' für Fragen, die bereits mit angularjs markiert sind.
Mahbub
1
@ Mahbub Mit anderen Worten, "Ja" :)
Brian

Antworten:

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

Ich hatte Probleme, mich mit diesem Konzept zu beschäftigen, bis ich es mir so vorstellte:

Service : Die Funktion , die Sie schreiben, wird neu erstellt :

  myInjectedService  <----  new myServiceFunction()

Factory : Die von Ihnen geschriebene Funktion (Konstruktor) wird aufgerufen :

  myInjectedFactory  <---  myFactoryFunction()

Was Sie damit machen, liegt bei Ihnen, aber es gibt einige nützliche Muster ...

Beispiel: Schreiben einer Servicefunktion , um eine öffentliche API verfügbar zu machen:

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

Oder verwenden Sie eine Factory- Funktion, um eine öffentliche API verfügbar zu machen:

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

Oder verwenden Sie eine Factory- Funktion, um einen Konstruktor zurückzugeben:

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

Welches zu verwenden? ...

Mit beiden können Sie dasselbe erreichen. In einigen Fällen bietet Ihnen die Factory jedoch etwas mehr Flexibilität, um ein Injectable mit einer einfacheren Syntax zu erstellen. Das liegt daran, dass myInjectedService immer ein Objekt sein muss, myInjectedFactory jedoch ein Objekt, eine Funktionsreferenz oder ein beliebiger Wert sein kann. Wenn Sie beispielsweise einen Dienst zum Erstellen eines Konstruktors geschrieben haben (wie im letzten Beispiel oben), muss dieser wie folgt instanziiert werden:

var myShinyNewObject = new myInjectedService.myFunction()

was wohl weniger wünschenswert ist als dies:

var myShinyNewObject = new myInjectedFactory();

(Sie sollten jedoch in erster Linie vorsichtig sein, wenn Sie diese Art von Muster verwenden, da neue Objekte in Ihren Controllern schwer zu verfolgende Abhängigkeiten erzeugen, die zum Testen schwer zu verspotten sind. Besser, ein Dienst verwaltet eine Sammlung von Objekten für Sie als new()wily-nilly verwenden.)


Eine weitere Sache, sie sind alle Singletons ...

Denken Sie auch daran, dass Angular Ihnen in beiden Fällen bei der Verwaltung eines Singletons hilft. Unabhängig davon, wo oder wie oft Sie Ihren Dienst oder Ihre Funktion einspeisen, erhalten Sie denselben Verweis auf dasselbe Objekt oder dieselbe Funktion. (Mit der Ausnahme, dass eine Factory einfach einen Wert wie eine Zahl oder eine Zeichenfolge zurückgibt. In diesem Fall erhalten Sie immer den gleichen Wert, jedoch keine Referenz.)

Gil Birman
quelle
2
Wäre es besser, es als Objektkonstruktor zu bezeichnen als Newable?
Marksyzm
2
@Hugo, ich habe gezeigt, dass Sie mit beiden effektiv dasselbe erreichen können. Es ist nur so, dass sich die Syntax unterscheidet.
Gil Birman
105
Ich bin mir nicht sicher, wie oft ich über den Unterschied zwischen Service und Fabrik lesen muss, bevor ich überzeugt bin, dass beide notwendig sind
DMac the Destroyer
10
Wir haben bereits ein Verb, das "zu neu" sagt, es ist "instanziieren". Nur als Referenz. :)
sscarduzio
7
Fabriken sind Funktionen, die aufgerufen werden, sodass sie alles zurückgeben können. Auf der anderen Seite werden Dienste durch Winkel-Via instanziiert new fn(), sodass sie eine Instanz zurückgeben müssen.
Gil Birman
318

Einfach gesagt ..

// 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
quelle
169
Alter, danke. Nicht dass die Details der anderen Antworten nicht gültig wären, aber manchmal benötigen Sie die 10-Sekunden-Version.
R Claven
4
Lassen Sie die Servicefunktion einfach nichts zurückgeben. Der this.name = ... reicht aus, um anzuzeigen , dass eine API verfügbar gemacht wird.
Pixelbits
3
Wenn Sie jedoch zurückkehren und Einwände erheben, wird dies stattdessen verwendet. jsfiddle.net/Ne5P8/1221
MrB
@MrB, das ist eine normale JavaScript-Funktion, die nicht spezifisch für Angular oder den Kontext dieser Frage ist.
Om Shankar
@Om Shankar, Die obige Antwort zeigt, dass der Unterschied in der Verwendung dieses Objekts gegenüber einem zurückgegebenen Objekt besteht. Ich habe gezeigt, dass "THIS" der Standardwert ist, der für einen Dienst verwendet wird. Wenn Sie jedoch einen Wert zurückgeben, verhält er sich fast genau wie eine Fabrik. Auf der anderen Seite scheint eine Fabrik jedoch einen zurückgegebenen Wert zu erfordern, da sonst ein Fehler auftritt - (in diesem Beispiel gezeigt - jsfiddle.net/hmoc0q3v/1 ).
MrB
247

Hier sind die Hauptunterschiede:

Dienstleistungen

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

Ergebnis: Wenn Sie serviceName als injizierbares Argument deklarieren, erhalten Sie die Instanz einer Funktion, an die übergeben wird module.service.

Verwendung: Kann nützlich sein, um Dienstprogrammfunktionen freizugeben, die durch einfaches Anhängen ( )an die Referenz der injizierten Funktion aufgerufen werden können. Könnte auch mit injectedArg.call( this )oder ähnlich ausgeführt werden.

Fabriken

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

Ergebnis: Wenn factoryName als injizierbare Argument deklarieren Sie mit dem zur Verfügung gestellt werden Wert, der durch den Aufruf der Funktion Referenz zurückgegeben wird weitergegeben module.factory.

Verwendung: Kann nützlich sein, um eine ' Klassen' - Funktion zurückzugeben, die dann neu erstellt werden kann, um Instanzen zu erstellen.

Hier ist ein Beispiel für die Verwendung von Services und Factory . Lesen Sie mehr über AngularJS Service vs Factory .

Sie können auch die AngularJS-Dokumentation und ähnliche Fragen zum Stackoverflow überprüfen, die hinsichtlich Service und Factory verwirrt sind .

Manish Chhabra
quelle
27
Ich bin mit Ihrer beispielhaften Verwendung einer Fabrik nicht einverstanden. Sowohl Services als auch Fabriken (vorausgesetzt, eine Funktion wird zurückgegeben. Es kann sich nur um einen Wert oder ein Objekt handeln) können neu erstellt werden. Tatsächlich ist ein Dienst die einzige Option, die garantiert neu verfügbar ist, wenn Sie eine Funktionsinstanz erhalten. Ich würde sagen, der Vorteil der Verwendung einer FABRIK gegenüber einem DIENST besteht darin, dass sie eine gewisse Kontrolle über den Zugang zu Immobilien ermöglicht - privat und öffentlich an sich, während alle Eigenschaften des Dienstes von Natur aus exponiert sind. Und ich stelle mir einen Anbieter als eine Fabrik einer Fabrik vor - nur ist er zur Konfigurationszeit injizierbar und konfigurierbar.
Drew R
1
@DrewR Vielen Dank für Ihren Kommentar, ich habe ein gutes Beispiel für öffentliche und private Methoden mit einer Factory gefunden: stackoverflow.com/a/14904891/65025
edzillion
Ich muss @DrewR in diesem Punkt tatsächlich zustimmen. Ich habe bereits Fabriken verwendet, um Objekte zurückzugeben, aber ehrlich gesagt könnte es sich an dieser Stelle lohnen, nur die $providersganze Zeit zu verwenden.
jedd.ahyoung
Service instanziiert den Konstruktor automatisch, oder?
Martian2049
1
@DrewR - Nach meinem Verständnis ist es wahr, dass Sie mit dem Service den gleichen neuen Effekt erzielen können wie mit einer Fabrik, aber es ist nicht das, wofür es gedacht ist. Das Hauptziel ist, wenn Sie nur ein Dienstprogrammobjekt zurückgeben möchten und dafür eine geeignetere Syntax bereitstellen - Sie können einfach this.myFunc = function(){}in Ihren Dienst schreiben (erspart Ihnen das Schreiben von Code, um das Objekt zu erstellen, wie Sie es mit einer Factory tun müssten ).
BornToCode
137

TL; DR

1) Wenn Sie eine Factory verwenden , erstellen Sie ein Objekt, fügen Eigenschaften hinzu und geben dasselbe Objekt zurück. Wenn Sie diese Factory an Ihren Controller übergeben, sind diese Eigenschaften für das Objekt jetzt in diesem Controller über Ihre Factory verfügbar.

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) Wenn Sie Service verwenden , instanziiert Angular ihn hinter den Kulissen mit dem Schlüsselwort 'new'. Aus diesem Grund fügen Sie 'this' Eigenschaften hinzu, und der Dienst gibt 'this' zurück. Wenn Sie den Dienst an Ihren Controller übergeben, sind diese Eigenschaften für "this" jetzt über Ihren Dienst auf diesem Controller verfügbar.

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

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



Nicht TL; DR

1) Factory
Factories sind die beliebteste Methode zum Erstellen und Konfigurieren eines Dienstes. Es gibt wirklich nicht viel mehr als das, was die TL gesagt hat; DR. Sie erstellen einfach ein Objekt, fügen ihm Eigenschaften hinzu und geben dasselbe Objekt zurück. Wenn Sie dann die Factory an Ihren Controller übergeben, sind diese Eigenschaften für das Objekt jetzt in diesem Controller über Ihre Factory verfügbar. Ein ausführlicheres Beispiel finden Sie unten.

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

Jetzt stehen uns alle Eigenschaften zur Verfügung, die wir an 'service' anhängen, wenn wir 'myFactory' an unseren Controller übergeben.

Fügen wir nun unserer Rückruffunktion einige 'private' Variablen hinzu. Auf diese kann vom Controller aus nicht direkt zugegriffen werden, aber wir werden eventuell einige Getter / Setter-Methoden für 'service' einrichten, um diese 'privaten' Variablen bei Bedarf ändern zu können.

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

Hier werden Sie feststellen, dass wir diese Variablen / Funktionen nicht an 'service' anhängen. Wir erstellen sie einfach, um sie später entweder zu verwenden oder zu ändern.

  • baseUrl ist die Basis-URL, die die iTunes-API benötigt
  • _artist ist der Künstler, nach dem wir suchen möchten
  • _finalUrl ist die endgültige und vollständig erstellte URL, zu der wir iTunes aufrufen. makeUrl ist eine Funktion, mit der unsere iTunes-freundliche URL erstellt und zurückgegeben wird.

Nachdem unsere Hilfs- / privaten Variablen und Funktionen vorhanden sind, fügen wir dem 'service'-Objekt einige Eigenschaften hinzu. Was auch immer wir auf "Service" setzen, wir können es direkt in dem Controller verwenden, an den wir "myFactory" übergeben.

Wir werden setArtist- und getArtist-Methoden erstellen, die den Künstler einfach zurückgeben oder festlegen. Wir werden auch eine Methode erstellen, die die iTunes-API mit unserer erstellten URL aufruft. Diese Methode wird ein Versprechen zurückgeben, das erfüllt wird, sobald die Daten von der iTunes-API zurückgegeben wurden. Wenn Sie nicht viel Erfahrung mit Versprechungen in Angular haben, empfehle ich Ihnen dringend, sich eingehend mit ihnen zu befassen.

Unten akzeptiert setArtist einen Künstler und ermöglicht es Ihnen, den Künstler festzulegen . getArtist gibt den Künstler zurück. callItunes ruft zuerst makeUrl () auf, um die URL zu erstellen, die wir für unsere $ http-Anfrage verwenden. Anschließend wird ein Versprechungsobjekt eingerichtet, eine $ http-Anfrage mit unserer endgültigen URL gestellt. Da $ http ein Versprechen zurückgibt, können wir nach unserer Anfrage .success oder .error aufrufen. Wir lösen dann unser Versprechen mit den iTunes-Daten oder lehnen es mit der Meldung "Es ist ein Fehler aufgetreten" ab.

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

Jetzt ist unsere Fabrik fertig. Wir können jetzt 'myFactory' in jeden Controller einfügen und dann unsere Methoden aufrufen, die wir an unser Serviceobjekt angehängt haben (setArtist, getArtist und 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);
      })
  }
});

Im obigen Controller injizieren wir den 'myFactory'-Dienst. Wir legen dann Eigenschaften für unser $ scope-Objekt fest, die aus Daten von 'myFactory' stammen. Der einzige schwierige Code oben ist, wenn Sie sich noch nie mit Versprechungen befasst haben. Da callItunes ein Versprechen zurückgibt, können wir die .then () -Methode verwenden und $ scope.data.artistData erst festlegen, wenn unser Versprechen mit den iTunes-Daten erfüllt ist. Sie werden feststellen, dass unser Controller sehr dünn ist. Alle unsere logischen und persistenten Daten befinden sich in unserem Service, nicht in unserem Controller.

2) Service
Das vielleicht Wichtigste, was Sie beim Erstellen eines Dienstes wissen müssen, ist, dass er mit dem Schlüsselwort "Neu" instanziiert wird. Für Ihre JavaScript-Gurus sollte dies einen großen Hinweis auf die Art des Codes geben. Für diejenigen unter Ihnen mit eingeschränktem Hintergrund in JavaScript oder für diejenigen, die nicht genau wissen, was das 'neue' Schlüsselwort tatsächlich bewirkt, lassen Sie uns einige JavaScript-Grundlagen überprüfen, die uns letztendlich helfen, die Natur eines Dienstes zu verstehen.

Um die Änderungen wirklich zu sehen, die auftreten, wenn Sie eine Funktion mit dem Schlüsselwort 'new' aufrufen, erstellen wir eine Funktion und rufen sie mit dem Schlüsselwort 'new' auf. Zeigen wir dann, was der Interpreter tut, wenn er das Schlüsselwort 'new' sieht. Die Endergebnisse sind beide gleich.

Lassen Sie uns zuerst unseren Konstruktor erstellen.

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

Dies ist eine typische JavaScript-Konstruktorfunktion. Wenn wir nun die Person-Funktion mit dem Schlüsselwort 'new' aufrufen, wird 'this' an das neu erstellte Objekt gebunden.

Fügen wir nun dem Prototyp unserer Person eine Methode hinzu, damit sie für jede Instanz unserer Personenklasse verfügbar ist.

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

Da wir nun die Funktion sayName auf den Prototyp setzen, kann jede Instanz von Person die Funktion sayName aufrufen, um den Namen dieser Instanz zu warnen.

Nachdem wir nun unsere Person-Konstruktorfunktion und unsere sayName-Funktion in ihrem Prototyp haben, erstellen wir tatsächlich eine Instanz von Person und rufen dann die sayName-Funktion auf.

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

Insgesamt sieht der Code zum Erstellen eines Personenkonstruktors, zum Hinzufügen einer Funktion zum Prototyp, zum Erstellen einer Personeninstanz und zum anschließenden Aufrufen der Funktion für den Prototyp folgendermaßen aus.

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'

Schauen wir uns nun an, was tatsächlich passiert, wenn Sie das Schlüsselwort 'new' in JavaScript verwenden. Das erste, was Sie beachten sollten, ist, dass wir nach der Verwendung von 'new' in unserem Beispiel eine Methode (sayName) für 'tyler' aufrufen können, als wäre es ein Objekt - das liegt daran, dass es so ist. Zuerst wissen wir also, dass unser Personenkonstruktor ein Objekt zurückgibt, ob wir das im Code sehen können oder nicht. Zweitens wissen wir, dass das Objekt, das die Person-Funktion zurückgibt, bei fehlgeschlagenen Suchvorgängen an ihren Prototyp delegieren muss, da sich unsere sayName-Funktion auf dem Prototyp und nicht direkt auf der Person-Instanz befindet. Einfacher ausgedrückt, wenn wir tyler.sayName () aufrufen, sagt der Interpreter: „OK, ich werde das soeben erstellte 'tyler'-Objekt betrachten, die Funktion sayName suchen und es dann aufrufen. Moment mal, ich sehe es hier nicht - alles was ich sehe ist Name und Alter, Lassen Sie mich den Prototyp überprüfen. Ja, es sieht so aus, als wäre es auf dem Prototyp, lass es mich nennen. “

Unten finden Sie Code, wie Sie darüber nachdenken können, was das 'neue' Schlüsselwort in JavaScript tatsächlich tut. Es ist im Grunde ein Codebeispiel für den obigen Absatz. Ich habe die 'Interpreter-Ansicht' oder die Art und Weise, wie der Interpreter den Code sieht, in Notizen eingefügt.

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

Wenn Sie nun wissen, was das 'neue' Schlüsselwort in JavaScript wirklich bewirkt, sollte es einfacher sein, einen Dienst in Angular zu erstellen.

Das Wichtigste, was Sie beim Erstellen eines Dienstes verstehen müssen, ist zu wissen, dass Dienste mit dem Schlüsselwort "Neu" instanziiert werden. Wenn Sie dieses Wissen mit unseren obigen Beispielen kombinieren, sollten Sie jetzt erkennen, dass Sie Ihre Eigenschaften und Methoden direkt an 'this' anhängen, das dann vom Service selbst zurückgegeben wird. Schauen wir uns das in Aktion an.

Im Gegensatz zu dem, was wir ursprünglich mit dem Factory-Beispiel gemacht haben, müssen wir kein Objekt erstellen und dann dieses Objekt zurückgeben, da wir, wie bereits mehrfach erwähnt, das Schlüsselwort 'new' verwendet haben, damit der Interpreter dieses Objekt erstellt und es delegieren lässt Es ist ein Prototyp und wird dann für uns zurückgegeben, ohne dass wir die Arbeit erledigen müssen.

Lassen Sie uns zuerst unsere 'private' und Hilfsfunktion erstellen. Dies sollte sehr vertraut aussehen, da wir mit unserer Fabrik genau dasselbe gemacht haben. Ich werde hier nicht erklären, was jede Zeile tut, da ich das im Fabrikbeispiel getan habe. Wenn Sie verwirrt sind, lesen Sie das Fabrikbeispiel erneut.

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

Jetzt werden wir alle unsere Methoden, die in unserem Controller verfügbar sein werden, an 'this' anhängen.

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

});

Genau wie in unserer Factory sind setArtist, getArtist und callItunes jetzt in jedem Controller verfügbar, an den wir myService übergeben. Hier ist der myService-Controller (der fast genau dem unseres werkseitigen Controllers entspricht).

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

Wie ich bereits erwähnt habe, sind Services, sobald Sie wirklich verstanden haben, was "neu" macht, fast identisch mit Fabriken in Angular.

Tyler McGinnis
quelle
12
Möglicherweise möchten Sie einen Link direkt zu Ihrem Blog bereitstellen. tylermcginnis.com/angularjs-factory-vs-service-vs-provider Ich fand das etwas einfacher zu lesen.
Tyler Collier
3
Nichts ist falsch daran, dein Blog hier zu wiederholen, aber ich stimme zu, dass dies ein Greta-Blog-Beitrag ist.
R Claven
5
Gute detaillierte Erklärung, was jeder unter der Haube tut, aber immer noch nicht klar, warum und wann jemand einen Service über eine Fabrik nutzen würde. Mit anderen Worten, wann möchte ich lieber ein neues Objekt als das von einer Fabrik zurückgegebene haben? Ich denke, das ist die größte Verwirrung.
Demisx
2
Wenn Sie eine dauerhafte Verbindung zu einem Remotedienst herstellen möchten, wie beispielsweise die im Beispiel erwähnte iTunes-API mit einer konstanten Verbindung (Verbindungsstatus, Anrufverlauf, Datenspeicherung), können Sie Factory verwenden. Wenn Sie es als Service implementieren, müssen Sie jedes Mal, wenn Sie etwas von der API möchten, die Verbindung neu erstellen und können nichts darin speichern. Denn jedes Mal, wenn Sie den Dienst neu erstellen, erhalten Sie ein leeres / Standardobjekt.
Meki
4
Ich denke nicht, dass das richtig ist, @Aznim. Wie andere gesagt haben, bieten beide Singletons.
Cryptovirus
35

Der Hinweis ist im Namen

Dienstleistungen und Fabriken ähneln sich. Beide ergeben ein Singleton-Objekt, das in andere Objekte injiziert werden kann und daher häufig austauschbar verwendet wird.

Sie sollen semantisch verwendet werden, um verschiedene Entwurfsmuster zu implementieren.

Services dienen zum Implementieren eines Servicemusters

Ein Servicemuster ist eines, bei dem Ihre Anwendung in logisch konsistente Funktionseinheiten unterteilt ist. Ein Beispiel könnte ein API-Accessor oder eine Reihe von Geschäftslogiken sein.

Dies ist in Angular besonders wichtig, da Angular-Modelle normalerweise nur JSON-Objekte sind, die von einem Server abgerufen werden. Daher müssen wir unsere Geschäftslogik irgendwo platzieren.

Hier ist zum Beispiel ein Github-Service. Es weiß, wie man mit Github spricht. Es kennt URLs und Methoden. Wir können es in einen Controller einspeisen, und es wird ein Versprechen generieren und zurückgeben.

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

Fabriken implementieren ein Fabrikmuster

Fabriken hingegen sollen ein Fabrikmuster implementieren. Ein Factory-Muster, in dem wir eine Factory-Funktion verwenden, um ein Objekt zu generieren. Normalerweise können wir dies zum Erstellen von Modellen verwenden. Hier ist eine Factory, die einen Author-Konstruktor zurückgibt:

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

Wir würden dies so nutzen:

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

Beachten Sie, dass Fabriken auch Singletons zurückgeben.

Fabriken können einen Konstruktor zurückgeben

Da eine Factory einfach ein Objekt zurückgibt, kann sie jeden beliebigen Objekttyp zurückgeben, einschließlich einer Konstruktorfunktion, wie oben dargestellt.

Fabriken geben ein Objekt zurück; Dienstleistungen sind neu

Ein weiterer technischer Unterschied besteht in der Zusammensetzung der Dienstleistungen und Fabriken. Eine Servicefunktion wird neu generiert, um das Objekt zu generieren. Eine Factory-Funktion wird aufgerufen und gibt das Objekt zurück.

  • Services sind neue Konstruktoren.
  • Fabriken werden einfach aufgerufen und geben ein Objekt zurück.

Dies bedeutet, dass wir in einem Dienst an "dies" anhängen, das im Kontext eines Konstruktors auf das im Bau befindliche Objekt verweist.

Um dies zu veranschaulichen, ist hier dasselbe einfache Objekt, das mit einem Service und einer Factory erstellt wurde:

angular.module('app', [])
  .service('helloService', function() {
    this.sayHello = function() {
      return "Hello!";
    }
  })
  .factory('helloFactory', function() {
    return {
      sayHello: function() {
        return "Hello!";
      }
    }
  });
überleuchtet
quelle
2
tolle Erklärung, danke! Es gibt auch einen Typ im Factories- Beispielcode, in dem sich der AuthorInjektorparameter befinden sollte Person.
mikhail-t
Danke @ mik-T, ich habe die Tippfehler behoben.
Superleuchte
1
Ihre Verwendung des Servicemusters ist falsch - dies sollte eine Fabrik sein. Wenn Sie .factory () anstelle von .service () aufrufen, werden Sie feststellen, dass es genauso funktioniert. Das Servicemuster soll mit einer Konstruktorfunktion geliefert werden, nicht mit einer Funktion, die ein neues Objekt zurückgibt. Angular (effektiv) ruft "new" für Ihre Konstruktorfunktion auf. Der einzige Grund, warum Ihr Dienst funktioniert, besteht darin, dass Sie beim Aufrufen von "new" für eine Konstruktorfunktion, die ein Objekt zurückgibt, das zurückgegebene Objekt und nicht das konstruierte Objekt zurückerhalten. Und Fabriken können verwendet werden, um alles zu erstellen, was Sie wollen, nicht nur Modelle.
Dan King
27

Alle Antworten hier scheinen sich auf Service und Fabrik zu beziehen, und das ist gültig, da danach gefragt wurde. Aber es ist auch wichtig , im Auge zu behalten , dass es mehrere andere , einschließlich provider(), value()und constant().

Der Schlüssel zum Erinnern ist, dass jeder ein Sonderfall des anderen ist. Jeder Sonderfall in der Kette ermöglicht es Ihnen, dasselbe mit weniger Code zu tun. Jeder hat auch eine zusätzliche Einschränkung.

Um zu entscheiden, wann Sie welche verwenden möchten, sehen Sie nur, welche Sie mit weniger Code tun können, was Sie wollen. Hier ist ein Bild, das zeigt, wie ähnlich sie sind:

Geben Sie hier die Bildbeschreibung ein

Für eine vollständige schrittweise Aufschlüsselung und eine schnelle Referenz, wann sie verwendet werden sollen, können Sie den Blog-Beitrag besuchen, von dem ich dieses Bild erhalten habe:

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

Luis Perez
quelle
3
@jacob vielleicht so, aber ich denke, das Gesamtkonzept, nicht nur wann jeder verwendet werden soll, sondern dass es sich bei allen um im Wesentlichen Variationen derselben Sache handelt, ist wichtig.
Luis Perez
1
@LuisPerez Der Link zu deinem Blog und das Video, das den Unterschied erklärt, ist wirklich großartig. Es ist einfacher mit diesen Beispielen aus dem Video zu verstehen :)
Alin Ciocan
24

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

Konstruktion

Bei Fabriken ruft Angular die Funktion auf, um das Ergebnis zu erhalten. Es ist das Ergebnis, das zwischengespeichert und injiziert wird.

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

Bei Diensten ruft Angular die Konstruktorfunktion auf, indem er new aufruft . Die konstruierte Funktion wird zwischengespeichert und injiziert.

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

Implementierung

Fabriken typischerweise ein Objektliteral zurück , weil der Rückgabewert ist , was in Controller, führen Blöcke, Richtlinien injiziert ist, usw.

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

Servicefunktionen geben normalerweise nichts zurück. Stattdessen führen sie eine Initialisierung durch und legen Funktionen offen. Funktionen können auch auf "dies" verweisen, da es mit "neu" erstellt wurde.

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

Fazit

Wenn es um die Nutzung von Fabriken oder Dienstleistungen geht, sind sich beide sehr ähnlich. Sie werden in Controller, Direktiven, Ausführungsblöcke usw. eingefügt und im Client-Code auf die gleiche Weise verwendet. Sie sind auch beide Singletons - das heißt, dieselbe Instanz wird von allen Stellen gemeinsam genutzt, an denen der Service / die Factory injiziert wird.

Also was solltest du bevorzugen? Entweder eins - sie sind sich so ähnlich, dass die Unterschiede trivial sind. Wenn Sie eine über die andere wählen, achten Sie einfach darauf, wie sie aufgebaut sind, damit Sie sie richtig implementieren können.

Pixelbits
quelle
Servicefunktionen geben "nichts zurück", sondern implizit das erstellte Objekt zurück, wenn Sie keine eigene return-Anweisung angeben (im letzteren Fall wird das zurückgegebene Objekt ähnlich wie bei einer Factory erstellt und zwischengespeichert).
Cryptovirus
Ich denke, Sie interpretieren es falsch ... Wenn ich return sage, meine ich aus der Sicht der Implementierung der Servicefunktion
Pixelbits
Sind Sie sicher, dass die Fabrik auch eine einzelne Stadt ist?
Martian2049
5

Ich habe einige Zeit damit verbracht, den Unterschied herauszufinden.

Und ich denke, die Factory-Funktion verwendet das Modulmuster und die Servicefunktion verwendet das Standard-Java-Skriptkonstruktormuster.

ps.
quelle
2

Das Factory-Muster ist flexibler, da es Funktionen und Werte sowie Objekte zurückgeben kann.

Das Servicemuster hat meiner Meinung nach nicht viel Sinn, da alles, was es tut, genauso einfach mit einer Fabrik gemacht werden kann. Die Ausnahmen könnten sein:

  • Wenn Sie sich aus irgendeinem Grund für den deklarierten Typ Ihres instanziierten Dienstes interessieren - wenn Sie das Dienstmuster verwenden, ist Ihr Konstruktor der Typ des neuen Dienstes.
  • Wenn Sie bereits eine Konstruktorfunktion haben, die Sie an anderer Stelle verwenden und die Sie auch als Dienst verwenden möchten (obwohl dies wahrscheinlich nicht viel nützt, wenn Sie etwas in sie einfügen möchten!).

Das Servicemuster ist wahrscheinlich eine etwas bessere Möglichkeit, ein neues Objekt aus syntaktischer Sicht zu erstellen, aber es ist auch teurer, es zu instanziieren. Andere haben angegeben, dass Angular "new" verwendet, um den Service zu erstellen, aber dies ist nicht ganz richtig - dies ist nicht möglich, da jeder Servicekonstruktor eine andere Anzahl von Parametern hat. Was Angular tatsächlich tut, ist, das Factory-Muster intern zu verwenden, um Ihre Konstruktorfunktion zu verpacken. Dann simuliert es den "neuen" Operator von Javascript, indem es Ihren Konstruktor mit einer variablen Anzahl injizierbarer Argumente aufruft. Sie können diesen Schritt jedoch weglassen, wenn Sie nur das Factory-Muster direkt verwenden, wodurch die Effizienz Ihres Operators geringfügig erhöht wird Code.

Dan King
quelle
Services sind effizienter zu konstruieren als Fabriken, da Fabriken relativ teure Schließungen verwenden und Services (Klassen) den Prototyp nutzen können.
Jacob
@jacob Nicht sicher, was du mit Schließungen meinst? Die Factory ist nur eine Funktion, die ein Objekt zurückgibt. Sie müssen nur dann einen Abschluss verwenden, wenn für Ihr zurückgegebenes Objekt der Status "privat" erforderlich ist. Sie müssten immer noch dasselbe tun, wenn Sie einen Konstruktor (Service) verwenden würden. Ich verstehe Ihren Standpunkt zum Prototyp - obwohl Sie dies immer noch in einer Fabrik tun könnten, wenn Sie wollten.
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 } }); } Während sowohl MyFactory als auch MyService Prototypen verwenden, muss MyFactory immer noch einen Leistungseinbruch hinnehmen, um das zurückgegebene Objekt zu erstellen. In beiden Beispielen haben sie Privaten, aber in MyService gibt es relativ keinen Leistungsunterschied.
Jacob
1
Für mich ist der Unterschied, ob ich die Fabrik direkt ohne eine Methode verwenden möchte: MyFactory(someArgument)(ex $http()). Dies ist mit einem Dienst nicht möglich, da Sie auf den Konstruktor verweisen würden : MyService(someArgument).
Jacob
In der Zeit der Objektkonstruktion sehe ich nicht wirklich, wie factory = {} ein Leistungseinbruch ist, mehr als Javascript, das "dies" für Sie initialisiert, wenn es Ihren Konstruktor aufruft? Und ich denke, der größere Leistungseinbruch liegt auf der Winkelseite, wenn er Ihren Konstruktor in eine Fabrik einwickelt und dann durch Reifen springen muss, um "neu" zu simulieren, damit er Ihre Abhängigkeiten einspeisen kann.
Dan King