angular.service vs. angular.factory

1065

Widziałem zarówno angular.factory (), jak i angular.service () używane do deklarowania usług; nie mogę jednak znaleźć angular.service nigdzie w oficjalnej dokumentacji.

Jaka jest różnica między tymi dwiema metodami?
Którego należy użyć do czego (zakładając, że robią różne rzeczy)?

Jakub
źródło
25
możliwy duplikat zdezorientowanego serwisu i fabryki
Mark Rajcok
4
Szukałem „fabryki usług [angularjs]”, ale przypomniałem sobie również, że już było na to pytanie (ponieważ w pewnym momencie myślałem o napisaniu tego / tego pytania).
Mark Rajcok
2
Czy podczas wyszukiwania nawiasy kwadratowe oznaczają znacznik?
jacob
11
@ Kwadratowe wsporniki Jacoba zawężają wyszukiwanie. dyrektywy [angularjs] - będą wyszukiwać „dyrektywy” w przypadku pytań oznaczonych już angularjs.
Mahbub,
1
@Mahbub Innymi słowy „tak” :)
Brian

Odpowiedzi:

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

Miałem problem z owinięciem głowy tą koncepcją, dopóki nie przedstawiłem jej w ten sposób:

Usługa : funkcja , którą piszesz, będzie nowa :

  myInjectedService  <----  new myServiceFunction()

Fabryka : wywołana funkcja (konstruktor) zostanie wywołana :

  myInjectedFactory  <---  myFactoryFunction()

To, co z tym zrobisz, zależy od ciebie, ale istnieją pewne użyteczne wzorce ...

Na przykład napisanie funkcji usługi w celu ujawnienia publicznego interfejsu API:

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

Lub za pomocą funkcji fabrycznej w celu ujawnienia publicznego interfejsu API:

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

Lub użycie funkcji fabryki do zwrócenia konstruktora:

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

Którego użyć? ...

Możesz osiągnąć to samo za pomocą obu. Jednak w niektórych przypadkach fabryka zapewnia nieco większą elastyczność w tworzeniu wstrzyknięć z prostszą składnią. Dzieje się tak, ponieważ chociaż myInjectedService musi zawsze być obiektem, myInjectedFactory może być obiektem, odwołaniem do funkcji lub dowolną wartością. Na przykład, jeśli napisałeś usługę do stworzenia konstruktora (jak w poprzednim przykładzie powyżej), musiałaby zostać utworzona instancja w następujący sposób:

var myShinyNewObject = new myInjectedService.myFunction()

co jest prawdopodobnie mniej pożądane niż to:

var myShinyNewObject = new myInjectedFactory();

(Należy jednak przede wszystkim zachować ostrożność przy stosowaniu tego typu wzorca, ponieważ nowe obiekty w kontrolerach tworzą trudne do śledzenia zależności, które trudno wyśmiewać w celu przetestowania. Lepiej mieć usługę zarządzającą kolekcją obiektów dla niż używać new()chytrego).


Jeszcze jedno, wszyscy są Singletonami ...

Należy również pamiętać, że w obu przypadkach kątowe pomaga zarządzać singletonem. Bez względu na to, gdzie i ile razy wstrzykujesz swoją usługę lub funkcję, otrzymasz takie samo odniesienie do tego samego obiektu lub funkcji. (Z wyjątkiem sytuacji, gdy fabryka po prostu zwraca wartość taką jak liczba lub łańcuch. W takim przypadku zawsze otrzymasz tę samą wartość, ale nie odwołanie).

Gil Birman
źródło
2
Czy lepiej byłoby nazwać go konstruktorem obiektów niż Newable?
marksyzm
2
@ Hugo, pokazałem, że możesz skutecznie osiągnąć to samo z obiema, po prostu składnia będzie się różnić.
Gil Birman
105
Nie jestem pewien, ile razy będę musiał czytać o różnicy między serwisem a fabryką, zanim przekonam się, że oba są konieczne
DMac the Destroyer
10
Mamy już czasownik do powiedzenia „to new”, to „instantiate”. Tylko w celach informacyjnych. :)
sscarduzio
7
Fabryki są funkcjami, które są wywoływane, więc mogą zwracać wszystko. Z drugiej strony usługi są tworzone przez angular przez new fn(), więc muszą zwrócić instancję.
Gil Birman,
318

Po prostu…

// 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
źródło
169
Koleś, dziękuję. Nie chodzi o to, że szczegóły innych odpowiedzi są nieprawidłowe, ale czasami potrzebujesz wersji 10-sekundowej.
R Claven
4
Wystarczy, że funkcja usługi nic nie zwróci. This.name = ... wystarczy, aby pokazać, że jest wystawienie API.
pixelbits
3
Jednak jeśli zwrócisz i sprzeciwisz się, użyje tego zamiast tego. jsfiddle.net/Ne5P8/1221
MrB
@MrB, jest to normalna funkcja JavaScript, nie specyficzna dla Angulara lub kontekstu tego pytania.
Om Shankar,
@Om Shankar, powyższa odpowiedź pokazuje, że różnica polega na użyciu tego w porównaniu do zwróconego obiektu. Pokazałem, że „TO” jest wartością domyślną, która będzie używana z usługą, jednak jeśli zwrócisz wartość, będzie ona działać prawie dokładnie tak jak fabryka. Jednak z drugiej strony fabryka wydaje się wymagać wartości zwracanej, w przeciwnym razie wystąpi błąd - (pokazany w tym przykładzie - jsfiddle.net/hmoc0q3v/1 ).
MrB
247

Oto podstawowe różnice:

Usługi

Składnia: module.service( 'serviceName', function );

Wynik: deklarując serviceName jako argument do wstrzyknięcia, otrzymasz instancję funkcji przekazanej do module.service.

Użycie: Może być przydatny do udostępniania funkcji narzędziowych, które można wywołać, po prostu dołączając ( )do wstrzykniętego odwołania do funkcji. Można go również uruchomić z injectedArg.call( this )podobnym.

Fabryki

Składnia: module.factory( 'factoryName', function );

Wynik: deklarując nazwę fabryki jako argument do wstrzyknięcia, otrzymasz wartość zwracaną przez wywołanie przekazanego odwołania do funkcji module.factory.

Użycie: Może być przydatny do zwracania funkcji „klasy”, którą można następnie zmienić w celu utworzenia instancji.

Oto przykład korzystania z usług i fabryki . Przeczytaj więcej o AngularJS Service vs Factory .

Możesz także sprawdzić dokumentację AngularJS i podobne pytanie na temat przepełnienia stosu mylone z usługą a fabryką .

Manish Chhabra
źródło
27
Nie zgadzam się z twoim przykładowym wykorzystaniem fabryki. Zarówno usługi, jak i fabryki (zakładając, że funkcja jest zwracana. Może to być tylko wartość lub obiekt) mogą być nowe. W rzeczywistości usługa jest jedyną opcją, dla której można zagwarantować, że jest nowa, ponieważ otrzymujesz instancję funkcji. Powiedziałbym, że zaletą korzystania z FABRYKI nad USŁUGĄ jest to, że umożliwia ona pewną kontrolę dostępu do nieruchomości - prywatnych i publicznych jako takich, podczas gdy wszystkie właściwości usługi są z natury narażone. I myślę o dostawcy jako fabryce fabryki - tylko można go wstrzykiwać i konfigurować w czasie konfiguracji.
Drew R
1
@DrewR Dzięki za komentarz, znalazłem dobry przykład metod publicznych i prywatnych przy użyciu fabryki: stackoverflow.com/a/14904891/65025
edzillion
Właściwie muszę zgodzić się z @DrewR w tej sprawie. Używałem już fabryk do zwracania przedmiotów, ale szczerze mówiąc, w tym momencie warto po prostu korzystać $providerscały czas.
jedd.ahyoung
usługa automatycznie tworzy instancję konstruktora, prawda?
Martian2049,
1
@DrewR - Z mojego zrozumienia, prawdą jest, że można osiągnąć taki sam efekt, jaki można uzyskać w serwisie, jak w fabryce, ale nie do tego służy. Jego głównym celem jest, gdy chcesz po prostu zwrócić jakiś obiekt użytkowy, a do tego zapewnia bardziej odpowiednią składnię - możesz po prostu pisać this.myFunc = function(){}w swojej usłudze (oszczędza ci to pisania kodu, aby utworzyć obiekt tak, jakbyś miał do czynienia z fabryką ).
BornToCode,
137

TL; DR

1) Gdy używasz fabryki , tworzysz obiekt, dodajesz do niego właściwości, a następnie zwraca ten sam obiekt. Po przekazaniu tej fabryki do kontrolera właściwości tego obiektu będą teraz dostępne w tym kontrolerze za pośrednictwem fabryki.

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) Gdy używasz usługi , Angular tworzy ją za kulisami ze słowem kluczowym „new”. Z tego powodu dodasz właściwości do „tego”, a usługa zwróci „to”. Po przekazaniu usługi do kontrolera właściwości „this” będą teraz dostępne na tym kontrolerze za pośrednictwem usługi.

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)
Fabryki to najpopularniejszy sposób tworzenia i konfigurowania usługi. Naprawdę niewiele więcej niż mówi TL; DR. Wystarczy utworzyć obiekt, dodać do niego właściwości, a następnie zwrócić ten sam obiekt. Następnie, po przekazaniu fabryki do kontrolera, te właściwości obiektu będą teraz dostępne w tym kontrolerze przez fabrykę. Bardziej obszerny przykład znajduje się poniżej.

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

Teraz wszelkie właściwości, które dołączamy do „usługi”, będą dostępne, gdy przekażemy „myFactory” do naszego kontrolera.

Dodajmy teraz trochę „prywatnych” zmiennych do naszej funkcji zwrotnej. Nie będą one bezpośrednio dostępne z poziomu kontrolera, ale w końcu skonfigurujemy metody getter / setter dla „service”, aby móc zmienić te „prywatne” zmienne w razie potrzeby.

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

Tutaj zauważysz, że nie przypisujemy tych zmiennych / funkcji do „usługi”. Po prostu tworzymy je w celu późniejszego wykorzystania lub modyfikacji.

  • baseUrl to podstawowy adres URL wymagany przez interfejs API iTunes
  • _artist to artysta, którego chcemy szukać
  • _finalUrl to końcowy iw pełni zbudowany adres URL, do którego zadzwonimy do iTunes makeUrl to funkcja, która utworzy i zwróci nasz przyjazny adres URL dla iTunes.

Teraz, gdy nasze zmienne pomocnicze / prywatne i funkcja są już na miejscu, dodajmy pewne właściwości do obiektu „service”. Cokolwiek zastosujemy w „usłudze”, będziemy mogli bezpośrednio używać w dowolnym kontrolerze, w którym przekazujemy „myFactory”.

Stworzymy metody setArtist i getArtist, które po prostu zwrócą lub ustawią artystę. Stworzymy również metodę, która wywoła interfejs API iTunes z naszym utworzonym adresem URL. Ta metoda zwróci obietnicę, która spełni się, gdy dane wrócą z interfejsu API iTunes. Jeśli nie miałeś dużego doświadczenia w korzystaniu z obietnic w Angular, bardzo polecam głębokie zanurzenie się w nich.

Poniżej setArtist akceptuje wykonawcę i umożliwia ustawienie wykonawcy. getArtist zwraca artystę callItunes najpierw wywołuje makeUrl () w celu zbudowania adresu URL, którego będziemy używać z naszym żądaniem $ http. Następnie ustawia obiekt obietnicy, wysyła żądanie $ http z naszym ostatnim adresem URL, a ponieważ $ http zwraca obietnicę, jesteśmy w stanie wywołać .success lub .error po naszej prośbie. Następnie wypełniamy naszą obietnicę za pomocą danych iTunes lub odrzucamy ją komunikatem „Wystąpił błąd”.

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

Teraz nasza fabryka jest kompletna. Jesteśmy teraz w stanie wstrzyknąć „myFactory” do dowolnego kontrolera, a następnie będziemy mogli wywoływać nasze metody, które podłączyliśmy do naszego obiektu usługi (setArtist, getArtist i 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);
      })
  }
});

W powyższym kontrolerze wprowadzamy usługę „myFactory”. Następnie ustawiamy właściwości naszego obiektu $ scope, które pochodzą z danych z „myFactory”. Jedynym trudnym kodem powyżej jest to, że nigdy wcześniej nie dotykałeś obietnic. Ponieważ callItunes zwraca obietnicę, jesteśmy w stanie użyć metody .then () i ustawić wartość $ scope.data.artistData tylko wtedy, gdy nasza obietnica zostanie wypełniona danymi iTunes. Zauważysz, że nasz kontroler jest bardzo „cienki”. Wszystkie nasze logiczne i trwałe dane znajdują się w naszym serwisie, a nie w naszym kontrolerze.

2) Usługa
Być może największą rzeczą, o której należy wiedzieć podczas tworzenia Usługi, jest to, że jest ona tworzona za pomocą słowa kluczowego „new”. Dla ciebie guru JavaScript powinien dać ci dużą wskazówkę dotyczącą natury kodu. Dla tych z ograniczonym doświadczeniem w JavaScript lub dla tych, którzy nie są zbyt dobrze zaznajomieni z tym, co faktycznie robi „nowe” słowo kluczowe, przejrzyjmy niektóre podstawy JavaScript, które ostatecznie pomogą nam zrozumieć naturę Usługi.

Aby naprawdę zobaczyć zmiany, które następują po wywołaniu funkcji słowem kluczowym „new”, utwórzmy funkcję i wywołaj ją słowem kluczowym „new”, a następnie pokażmy, co robi tłumacz, gdy zobaczy słowo kluczowe „new”. Oba wyniki końcowe będą takie same.

Najpierw stwórzmy naszego Konstruktora.

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

Jest to typowa funkcja konstruktora JavaScript. Teraz za każdym razem, gdy wywołujemy funkcję Osoba za pomocą słowa kluczowego „new”, „to” zostanie powiązane z nowo utworzonym obiektem.

Dodajmy teraz metodę do prototypu naszej Osoby, aby była dostępna w każdej instancji naszej „klasy” Osoby.

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

Teraz, ponieważ umieszczamy funkcję sayName na prototypie, każda instancja Person będzie mogła wywołać funkcję sayName, aby zaalarmować nazwę tej instancji.

Teraz, gdy mamy prototyp konstruktora Person i funkcję sayName na jego prototypie, utwórzmy instancję Person, a następnie wywołaj funkcję sayName.

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

Cały kod do tworzenia konstruktora Person, dodawania funkcji do jego prototypu, tworzenia instancji Person, a następnie wywoływania funkcji na prototypie wygląda tak.

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'

Teraz spójrzmy na to, co się właściwie dzieje, gdy używasz słowa kluczowego „new” w JavaScript. Pierwszą rzeczą, którą powinieneś zauważyć, jest to, że po użyciu „new” w naszym przykładzie jesteśmy w stanie wywołać metodę (sayName) na „tyler” tak, jakby to był obiekt - to dlatego, że tak jest. Po pierwsze wiemy, że nasz konstruktor Person zwraca obiekt, bez względu na to, czy widzimy to w kodzie, czy nie. Po drugie, wiemy, że ponieważ nasza funkcja sayName znajduje się na prototypie, a nie bezpośrednio na instancji Person, obiekt, który zwraca funkcja Person, musi delegować do swojego prototypu podczas nieudanych wyszukiwań. Mówiąc prościej, kiedy wywołujemy tyler.sayName () interpreter mówi „OK, przejrzę właśnie utworzony obiekt„ tyler ”, zlokalizuj funkcję sayName, a następnie wywołaj ją. Chwileczkę, nie widzę tego tutaj - widzę tylko imię i wiek, pozwól mi sprawdzić prototyp. Tak, wygląda na to, że jest na prototypie, nazwijmy to ”.

Poniżej znajduje się kod wyjaśniający, co tak naprawdę robi „nowe” słowo kluczowe w JavaScript. Jest to w zasadzie przykład kodu powyższego akapitu. Umieściłem „widok tłumacza” lub sposób, w jaki tłumacz widzi kod w notatkach.

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

Mając już wiedzę o tym, co tak naprawdę robi „nowe” słowo kluczowe w JavaScript, tworzenie Usługi w Angular powinno być łatwiejsze do zrozumienia.

Najważniejszą rzeczą do zrozumienia podczas tworzenia Usługi jest świadomość, że Usługi są tworzone za pomocą słowa kluczowego „new”. Łącząc tę ​​wiedzę z naszymi powyższymi przykładami, powinieneś teraz wiedzieć, że będziesz przypisywać swoje właściwości i metody bezpośrednio do „tego”, który następnie zostanie zwrócony z samej Usługi. Spójrzmy na to w akcji.

W przeciwieństwie do tego, co pierwotnie zrobiliśmy z przykładem Factory, nie musimy tworzyć obiektu, a następnie zwracać go, ponieważ, jak wspomniano wiele razy wcześniej, użyliśmy słowa kluczowego „new”, aby interpreter utworzył ten obiekt, zlecając mu to prototyp, a następnie zwróć go nam bez konieczności wykonywania pracy.

Po pierwsze, stwórzmy naszą funkcję „prywatną” i pomocniczą. Powinno to wyglądać bardzo znajomo, ponieważ zrobiliśmy dokładnie to samo z naszą fabryką. Nie wyjaśnię, co robi tutaj każda linia, ponieważ zrobiłem to w przykładzie fabrycznym. Jeśli jesteś zdezorientowany, przeczytaj ponownie przykład fabryczny.

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

Teraz dołączymy wszystkie nasze metody, które będą dostępne w naszym kontrolerze do „tego”.

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

});

Teraz, podobnie jak w naszej fabryce, setArtist, getArtist i callItunes będą dostępne w dowolnym kontrolerze, do którego przekazujemy myService. Oto kontroler myService (który jest prawie dokładnie taki sam jak nasz kontroler fabryczny).

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

Jak wspomniałem wcześniej, kiedy naprawdę zrozumiesz, co robi „nowość”, usługi są prawie identyczne z fabrykami w Angular.

Tyler McGinnis
źródło
12
Możesz podać link bezpośrednio do swojego bloga. tylermcginnis.com/angularjs-factory-vs-service-vs-provider Stwierdziłem, że trochę łatwiej go czytać.
Tyler Collier
3
Nie ma nic złego w powtarzaniu bloga tutaj, ale zgadzam się, że jest to post na blogu greta.
R Claven
5
Dobre szczegółowe wyjaśnienie tego, co każdy robi pod maską, ale wciąż nie jest jasne, dlaczego i kiedy ktoś zdecyduje się skorzystać z Usługi w Fabryce. Innymi słowy, kiedy wolę mieć nowy obiekt niż ten zwrócony przez fabrykę. Myślę, że to największe zamieszanie.
demisx
2
Zasadniczo, jeśli chcesz utworzyć trwałe połączenie ze zdalną usługą, taką jak iTunes API wspomniane w przykładzie ze stałym połączeniem (stan połączenia, historia połączeń, przechowywanie danych), możesz przejść do Factory. Jeśli zaimplementujesz to jako usługę, to za każdym razem, gdy będziesz czegoś potrzebować z interfejsu API, ponownie utworzysz połączenie i nie będziesz mógł w nim niczego przechowywać. Ponieważ za każdym razem, gdy ponownie tworzysz usługę, otrzymasz pusty / domyślny obiekt.
Meki
4
Nie sądzę, że to prawda, @Aznim. Jak inni powiedzieli, oba zapewniają singletony.
Cryptovirus
35

Wskazówka jest w nazwie

Usługi i fabryki są do siebie podobne. Oba dadzą pojedynczy obiekt, który można wstrzyknąć w inne obiekty, dlatego często są używane zamiennie.

Są przeznaczone do stosowania semantycznego do implementacji różnych wzorców projektowych.

Usługi służą do wdrażania wzorca usługi

Wzorzec usługi to taki, w którym aplikacja jest podzielona na logicznie spójne jednostki funkcjonalności. Przykładem może być akcesorium API lub zestaw logiki biznesowej.

Jest to szczególnie ważne w Angular, ponieważ modele Angular są zazwyczaj tylko obiektami JSON pobranymi z serwera, dlatego potrzebujemy miejsca, w którym można umieścić logikę biznesową.

Oto na przykład usługa Github. Wie, jak rozmawiać z Githubem. Wie o adresach URL i metodach. Możemy wstrzyknąć go do kontrolera, który wygeneruje i zwróci obietnicę.

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

Fabryki wdrażają wzór fabryczny

Z drugiej strony fabryki mają wdrożyć wzór fabryczny. Wzorzec fabryczny w tym, w którym używamy funkcji fabrycznej do generowania obiektu. Zwykle możemy tego użyć do budowy modeli. Oto fabryka, która zwraca konstruktora autora:

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

Wykorzystamy to w następujący sposób:

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

Pamiętaj, że fabryki również zwracają singletony.

Fabryki mogą zwrócić konstruktor

Ponieważ fabryka po prostu zwraca obiekt, może zwrócić dowolny typ obiektu, w tym funkcję konstruktora, jak widzimy powyżej.

Fabryki zwracają obiekt; usługi są odnawialne

Kolejną różnicą techniczną jest sposób, w jaki składają się usługi i fabryki. Zostanie dodana funkcja usługi do generowania obiektu. Wywołana zostanie funkcja fabryczna i zwróci obiekt.

  • Usługi są konstruktorami typu neible.
  • Fabryki są po prostu wywoływane i zwracają obiekt.

Oznacza to, że w usłudze dołączamy do „tego”, który w kontekście konstruktora będzie wskazywał na obiekt w budowie.

Aby to zilustrować, oto ten sam prosty obiekt utworzony za pomocą usługi i fabryki:

angular.module('app', [])
  .service('helloService', function() {
    this.sayHello = function() {
      return "Hello!";
    }
  })
  .factory('helloFactory', function() {
    return {
      sayHello: function() {
        return "Hello!";
      }
    }
  });
superluminarny
źródło
2
świetne wyjaśnienie, dziękuję! istnieje również typ w przykładowym kodzie fabryki, w którym Authorpowinien znajdować się parametr wtryskiwacza Person.
mikhail-t
Dzięki @ mik-T, poprawiłem literówki.
superluminarny
1
Twoje użycie wzorca usługi jest nieprawidłowe - powinna to być fabryka. Jeśli wywołasz funkcję .factory () zamiast .service (), zobaczysz, że działa dokładnie tak samo. Wzorzec usługi ma być dostarczany z funkcją konstruktora, a nie funkcją zwracającą nowy obiekt. Angular (skutecznie) wywołuje „new” w funkcji konstruktora. Jedynym powodem, dla którego usługa działa, jest to, że jeśli wywołasz funkcję „new” w funkcji konstruktora, która zwraca obiekt, faktycznie zwracany jest obiekt, a nie obiekt skonstruowany. Za pomocą fabryk można tworzyć dowolne produkty, a nie tylko modele.
Dan King
27

Wszystkie odpowiedzi tutaj wydają się dotyczyć serwisu i fabryki, i jest to ważne, ponieważ o to pytano. Ale ważne jest też, aby pamiętać, że istnieje wiele innych, w tym provider(), value()i constant().

Kluczem do zapamiętania jest to, że każdy z nich jest szczególnym przypadkiem drugiego. Każdy specjalny przypadek w dół łańcucha pozwala robić to samo z mniejszym kodem. Każdy z nich ma także dodatkowe ograniczenia.

Aby zdecydować, kiedy użyć, który po prostu widzisz, który pozwala ci robić, co chcesz, w mniejszym stopniu kodu. Oto obraz ilustrujący ich podobieństwo:

wprowadź opis zdjęcia tutaj

Aby uzyskać pełny podział krok po kroku i szybki przegląd tego, kiedy użyć każdego z nich, możesz odwiedzić wpis na blogu, z którego otrzymałem ten obraz:

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

Luis Perez
źródło
3
@jacob może tak, ale myślę, że ogólna koncepcja nie tylko kiedy używać każdego, ale że są to zasadniczo odmiany tego samego, jest ważna.
Luis Perez,
1
@LuisPerez Link do twojego bloga i wideo wyjaśniający różnicę, że jest naprawdę świetny. Łatwiej jest zrozumieć te przykłady z filmu :)
Alin Ciocan
24

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

Budowa

W przypadku fabryk Angular wywoła funkcję, aby uzyskać wynik. Jest to wynik, który jest buforowany i wstrzykiwany.

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

W przypadku usług Angular wywoła funkcję konstruktora, wywołując new . Skonstruowana funkcja jest buforowana i wstrzykiwana.

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

Realizacja

Fabryki zwykle zwracają literał obiektu, ponieważ zwracana wartość jest wprowadzana do kontrolerów, bloków uruchamiania, dyrektyw itp.

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

Funkcje serwisowe zazwyczaj niczego nie zwracają. Zamiast tego wykonują inicjalizację i udostępniają funkcje. Funkcje mogą również odwoływać się do „tego”, ponieważ został on utworzony przy użyciu „nowego”.

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

Wniosek

Jeśli chodzi o korzystanie z fabryk lub usług, oba są bardzo podobne. Są one wstrzykiwane do kontrolerów, dyrektyw, bloków uruchomieniowych itp. I używane w kodzie klienta w podobny sposób. Oba są również singletonami - co oznacza, że ​​ta sama instancja jest dzielona między wszystkie miejsca, w których wstrzykiwana jest usługa / fabryka.

Więc co wolisz? Oba - są tak podobne, że różnice są banalne. Jeśli wybierzesz jedną z drugiej, pamiętaj tylko, jak są one zbudowane, abyś mógł je odpowiednio zaimplementować.

piksele
źródło
Funkcje serwisowe „niczego nie zwracają”, domyślnie zwracają skonstruowany obiekt JEŻELI nie podasz własnej instrukcji return (w tym drugim przypadku zwracany obiekt jest tym, co zostanie utworzone i buforowane, podobnie jak fabryka).
Cryptovirus
Myślę, że źle to interpretujesz ... Kiedy mówię return, mam na myśli z punktu widzenia implementacji funkcji usługi
pixelbits
czy jesteś pewien, że fabryka to jedno miasto?
Martian2049,
5

Spędziłem trochę czasu próbując zrozumieć różnicę.

I myślę, że funkcja fabryki używa wzorca modułu, a funkcja usługi używa standardowego wzorca konstruktora skryptu Java.

ps.
źródło
2

Wzorzec fabryczny jest bardziej elastyczny, ponieważ może zwracać funkcje i wartości oraz obiekty.

Wzorzec serwisowy IMHO nie ma większego sensu, ponieważ wszystko, co robi, można równie łatwo zrobić w fabryce. Wyjątkami mogą być:

  • Jeśli z jakiegoś powodu zależy Ci na zadeklarowanym typie instancji usługi - jeśli użyjesz wzorca usługi, Twój konstruktor będzie typem nowej usługi.
  • Jeśli masz już funkcję konstruktora, której używasz w innym miejscu, którego również chcesz użyć jako usługi (choć prawdopodobnie niewiele z niej skorzystasz, jeśli chcesz coś do niej wstrzyknąć!).

Prawdopodobnie wzorzec usługi jest nieco lepszym sposobem na utworzenie nowego obiektu z punktu widzenia składni, ale jego utworzenie jest również bardziej kosztowne. Inni wskazywali, że angular używa „nowego” do utworzenia usługi, ale nie jest to do końca prawdą - nie jest w stanie tego zrobić, ponieważ każdy konstruktor usługi ma inną liczbę parametrów. To, co faktycznie robi kąt, to użycie wewnętrznego wzorca fabrycznego do zawinięcia funkcji konstruktora. Następnie wykonuje sprytną manipulację, symulując „nowy” operator javascript, wywołując konstruktora ze zmienną liczbą argumentów do wstrzyknięcia - ale możesz pominąć ten krok, jeśli bezpośrednio użyjesz wzorca fabrycznego, co bardzo nieznacznie zwiększy wydajność twojego kod.

Dan King
źródło
Usługi są bardziej wydajne w budowie niż fabryki, ponieważ fabryki używają stosunkowo drogich zamknięć, a usługi (klasy) mogą skorzystać z prototypu.
jacob
@jacob Nie wiesz, co masz na myśli mówiąc o zamknięciach? Fabryka jest tylko funkcją zwracającą obiekt. Musisz użyć zamknięcia tylko wtedy, gdy zwrócony obiekt wymaga stanu „prywatnego”. Nadal będziesz musiał zrobić to samo, jeśli użyjesz konstruktora (usługi). Biorę swój punkt o prototypie chociaż - choć mogłyby nadal to zrobić w fabryce, jeśli chciał.
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 } }); } Podczas gdy zarówno MyFactory, jak i MyService używają prototypu, MyFactory nadal ma wpływ na wydajność, ponieważ musi skonstruować zwracany obiekt. W obu przykładach mają osoby prywatne, ale w MyService nie ma względnie żadnej różnicy w wydajności.
jacob
1
Dla mnie różnica polega na tym, czy chcę korzystać z fabryki bezpośrednio bez metody: MyFactory(someArgument)(np $http().). Nie jest to możliwe z usługą jak chcesz być odwołującego konstruktora: MyService(someArgument).
jacob
Jeśli chodzi o czas budowy obiektu, tak naprawdę nie widzę, jak fabryka = {} jest hitem wydajności, bardziej niż javascript inicjujący „to” dla ciebie, gdy wywołuje on twojego konstruktora? I myślę, że większy wpływ na wydajność ma strona kątowa, gdy otacza ona konstruktora fabryką, a następnie musi przeskakiwać obręcze, aby symulować „nowy”, aby mógł wstrzyknąć zależności.
Dan King,