Myliłem się co do usługi kontra fabryka

618

Jak rozumiem, w fabryce zwracam przedmiot, który zostaje wstrzyknięty do kontrolera. W usłudze mam do czynienia z obiektem, thisktóry niczego nie zwraca i nie zwraca.

Zakładałem, że usługa zawsze była singletonem i że do każdego sterownika wstrzykiwany jest nowy obiekt fabryczny . Jak się jednak okazuje, obiekt fabryczny też jest singletonem?

Przykładowy kod do zademonstrowania:

var factories = angular.module('app.factories', []);
var app = angular.module('app',  ['ngResource', 'app.factories']);

factories.factory('User', function () {
  return {
    first: 'John',
    last: 'Doe'
  };
});

app.controller('ACtrl', function($scope, User) {
  $scope.user = User;
});

app.controller('BCtrl', function($scope, User) {
  $scope.user = User;
});

Po zmianie user.firstw ACtrlOkazuje się, że user.firstw BCtrljest również zmienione, np Userjest pojedyncza?

Moje założenie było takie, że nowa instancja została wstrzyknięta do kontrolera z fabryką?

JvdBerg
źródło
4
Obok „module.service” i „module.factory” istnieją jeszcze 2 sposoby tworzenia usług w AngularJS. Aby uzyskać więcej informacji, sprawdź wpis na blogu: „ Jak tworzyć (singleton) usługi AngularJS na 4 różne sposoby
Emil van Galen
Możliwy duplikat angular.service vs. angular.factory
Kaushal28

Odpowiedzi:

600

Wszystkie usługi kątowe są singletonami :

Dokumenty (patrz Usługi jako singletony ): https://docs.angularjs.org/guide/services

Na koniec należy pamiętać, że wszystkie usługi Angular są singletonami aplikacji. Oznacza to, że na wtryskiwacz przypada tylko jedna instancja danej usługi.

Zasadniczo różnica między usługą a fabryką jest następująca:

app.service('myService', function() {

  // service is just a constructor function
  // that will be called with 'new'

  this.sayHello = function(name) {
     return "Hi " + name + "!";
  };
});

app.factory('myFactory', function() {

  // factory returns an object
  // you can run some code before

  return {
    sayHello : function(name) {
      return "Hi " + name + "!";
    }
  }
});

Sprawdź tę prezentację na temat $ zapewniają: http://slides.wesalvaro.com/20121113/#/

Te slajdy zostały użyte podczas jednego ze spotkań AngularJs: http://blog.angularjs.org/2012/11/more-angularjs-meetup-videos.html

matys84pl
źródło
13
Zobacz także stackoverflow.com/questions/15666048/..., który omawia różnice między usługą, fabryką i świadczeniem.
Mark Rajcok
31
Oficjalny dokument pośrednio [sic! nie dość jasne] oznacza, że ​​nawet jeśli zdefiniujesz usługę w fabryce, zostanie ona utworzona tylko raz. Innymi słowy, NIE został ponownie utworzony zgodnie z odniesieniem (punktem iniekcji) - jakkolwiek to nazwiesz. Oba sposoby powodują wystąpienie pojedynczego wystąpienia dla każdego wtryskiwacza.
honzajde
3
Mówisz, że „service to tylko funkcja konstruktora, która będzie wywoływana z„ new ””, ale myślę, że to wprowadza w błąd. Nie sądzę, żeby to się nazywało za kulisami, myślę, że deweloper jest odpowiedzialny za wywołanie newtego.
Tim Kindberg,
5
@nfiniteloop, sprawdź kod źródłowy w pobliżu linii 3574. Fabryki są metodą $ get dostawcy, a usługi generują fabryki za pomocą metody, która wywołuje $ injector.instantiate na podanej funkcji, która następnie wywołuje nową. ( Zobacz dokumenty )
obywatelstwo
14
Miałem wrażenie, że usługa była tak samo singleton, jak ty, wykorzystując odniesienie do niej. I że fabryka była singletonem, który za każdym razem zwracał nowy obiekt. Oznacza to, że usługa da ci jeden „samochód” i wszystko w twoim projekcie wykorzysta ten samochód. Podczas gdy fabryka dawałaby ci nowy samochód za każdym razem, gdy wywołujesz fabrykę. Jeden był singletonem, który zwrócił singletona, a drugi był singletonem, który zwrócił obiekt. Czy ktoś może wyjaśnić? Nazywanie wszystkiego singletonem nie pomaga, ponieważ może odnosić się do wielu rzeczy.
user2483724,
380

Dla mnie objawienie pojawiło się, gdy zdałem sobie sprawę, że wszystkie działają w ten sam sposób: uruchamiając coś raz , przechowując otrzymaną wartość, a następnie odkrztuszając tę samą przechowywaną wartość, gdy odwołuje się do niej poprzez Dependency Injection.

Powiedzmy, że mamy:

app.factory('a', fn);
app.service('b', fn);
app.provider('c', fn);

Różnica między tymi trzema polega na tym, że:

  1. aprzechowywana wartość pochodzi z działania fn, innymi słowy:fn()
  2. bprzechowywana wartość pochodzi z newing fn, innymi słowy:new fn()
  3. cZapisana wartość pochodzi z pierwszego pobrania instancji przez newing fn, a następnie uruchomienia $getmetody instancji

co oznacza, że ​​wewnątrz kąta znajduje się coś w rodzaju obiektu pamięci podręcznej, którego wartość każdego zastrzyku jest przypisywana tylko raz, gdy zostały wstrzyknięte po raz pierwszy i gdzie:

cache.a = fn()
cache.b = new fn()
cache.c = (new fn()).$get()

Dlatego używamy thisw usługach i definiujemy this.$getdostawców.

Mam nadzieję że to pomoże.

Lucia
źródło
54
wreszcie rozsądne wyjaśnienie. Angular jest szalony i tak bardzo zły, że boli.
Ozyrys
8
To powinna być zaakceptowana odpowiedź, ponieważ faktycznie odpowiada na pytanie DLACZEGO fabryki, usługi i dostawcy zwracają wartości singletonów. Pozostałe odpowiedzi wyjaśniają różnicę między fabrykami, usługami i dostawcami, ale nigdy nie dotykają aspektu singletonu.
wmock,
3
Lubię to ... Kiedy czytam tysiące wierszy zdania z innego blogera ... rozumiem tylko fabrykę. Ale przeczytałem to ... rozumiem wszystkie 3.
tsohtan
@osiris Zgadzam się. Nie podoba mi się Po prostu czuję się tak cholernie blisko siebie, że sprawia, że ​​zęby mi zgrzytają.
Thomas
2
więc musisz podać implementację $ get podczas korzystania z usługodawców?
Victor
95

przykład na żywo

przykład „witaj świecie”

z factory/ service/ provider:

var myApp = angular.module('myApp', []);

//service style, probably the simplest one
myApp.service('helloWorldFromService', function() {
    this.sayHello = function() {
        return "Hello, World!"
    };
});

//factory style, more involved but more sophisticated
myApp.factory('helloWorldFromFactory', function() {
    return {
        sayHello: function() {
            return "Hello, World!"
        }
    };
});

//provider style, full blown, configurable version     
myApp.provider('helloWorld', function() {
    // In the provider function, you cannot inject any
    // service or factory. This can only be done at the
    // "$get" method.

    this.name = 'Default';

    this.$get = function() {
        var name = this.name;
        return {
            sayHello: function() {
                return "Hello, " + name + "!"
            }
        }
    };

    this.setName = function(name) {
        this.name = name;
    };
});

//hey, we can configure a provider!            
myApp.config(function(helloWorldProvider){
    helloWorldProvider.setName('World');
});


function MyCtrl($scope, helloWorld, helloWorldFromFactory, helloWorldFromService) {

    $scope.hellos = [
        helloWorld.sayHello(),
        helloWorldFromFactory.sayHello(),
        helloWorldFromService.sayHello()];
}​
EpokK
źródło
57

Istnieje również sposób na zwrócenie funkcji konstruktora, dzięki czemu można zwrócić klasy neible w fabrykach, takie jak to:

function MyObjectWithParam($rootScope, name) {
  this.$rootScope = $rootScope;
  this.name = name;
}
MyObjectWithParam.prototype.getText = function () {
  return this.name;
};

App.factory('MyObjectWithParam', function ($injector) {
  return function(name) { 
    return $injector.instantiate(MyObjectWithParam,{ name: name });
  };
}); 

Możesz to zrobić w kontrolerze, który używa MyObjectWithParam:

var obj = new MyObjectWithParam("hello"),

Zobacz tutaj pełny przykład:
http://plnkr.co/edit/GKnhIN?p=preview

A tutaj strony grupy google, na których omawiano:
https://groups.google.com/forum/#!msg/angular/56sdORWEoqg/b8hdPskxZXsJ

JustGoscha
źródło
Mam problem z minifikacją na twoim przykładzie. Czy wiesz, jak powinienem to zanotować?
Pål
2
Tak, istnieje skrócona notacja dla Angulara. Powinno to być coś takiego: App.factory('MyObjectWithParam', ['$injector', function ($injector) { return function(name) { return $injector.instantiate(MyObjectWithParam,{ name: name }); }; }]); Przeczytaj więcej na ten temat tutaj: docs.quarejs.org/tutorial/step_05
JustGoscha
4
dlaczego chcesz to zrobić, jeśli możesz .servicezamiast tego użyć ?
flup
miałem taką samą myśl @flup. @ justgoscha, czy jest jakaś korzyść ( postrzegana? ) używania .factoryw przeciwieństwie do .service?
xandercoded
5
Myślę, że ponieważ usługa jest singletonem . To, co tutaj zbudowałem, jest w zasadzie klasą, której nie można pominąć. Więc można mieć coś w fabryce Car Service, a następnie dokonać new Car('BMW')i new Car('Ford')a oni nie mają tych samych zmiennych i wszystko.
JustGoscha
51

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 dodają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.

Sprawdź także dokumentację AngularJS i podobne pytanie na temat przepełnienia stosu, mylone co do usługi kontra fabryka .

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

Manish Chhabra
źródło
6
To ma dla mnie sens. Fabryka zwraca plan tworzenia nowych obiektów.
27

Dodając do pierwszej odpowiedzi, myślę, że .service () jest dla ludzi, którzy napisali swój kod w stylu bardziej obiektowym (C # / Java) (używając tego słowa kluczowego i instancji obiektu za pomocą funkcji prototypu / konstruktora).

Fabryka przeznaczona jest dla programistów, którzy piszą kod bardziej naturalny dla javascript / funkcjonalnego stylu kodowania.

Spójrz na kod źródłowy .service i .factory metody wewnątrz angular.js - wewnętrznie wszystkie one wywołują metodę dostawcy:

  function provider(name, provider_) {
    if (isFunction(provider_)) {
      provider_ = providerInjector.instantiate(provider_);
    }
    if (!provider_.$get) {
      throw Error('Provider ' + name + ' must define $get factory method.');
    }
    return providerCache[name + providerSuffix] = provider_;
  }

  function factory(name, factoryFn) { \
    return provider(name, { $get: factoryFn }); 
  }

  function service(name, constructor) {
    return factory(name, ['$injector', function($injector) {
      return $injector.instantiate(constructor);
    }]);
  }
Anand
źródło
25

Bardzo prosto:

.service - zarejestrowana funkcja zostanie wywołana jako konstruktor (alias „newed”)

.factory - zarejestrowana funkcja zostanie wywołana jako funkcja prosta

Oba są wywoływane raz, co powoduje powstanie obiektu singleton, który zostaje wstrzyknięty do innych składników aplikacji.

brzęczy
źródło
6
tak. nie komplikujmy rzeczy tak, jak naprawdę są
flup
20

Wszyscy dostawcy działają w ten sam sposób. Różne metody service, factory, providertylko pozwalają osiągnąć to samo w mniej kodu.

PS Jest też valuei constant.

Każdy specjalny przypadek w dół łańcucha, zaczynający się provideri kończący na, valuema dodatkowe ograniczenie. Aby więc zdecydować między nimi, musisz zadać sobie pytanie, co pozwoli ci osiągnąć to, czego chcesz, przy mniejszym kodowaniu.

Oto zdjęcie, które pokazuje, co mam na myśli:

wprowadź opis zdjęcia tutaj

Możesz podzielić przewodnik i przewodnik referencyjny na blogu, który dostałem ten obraz z:

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

Luis Perez
źródło
Mówi się, że usługi są singletonowe, ale dlaczego to jest singleton, jeśli tworzona jest nowa instancja za każdym razem, gdy ją wstrzykiwam?
Ankur Marwaha,
1
@AnkurMarwaha Nowa instancja nie jest tworzona za każdym razem, jest tworzona tylko raz i buforowana przez AngularJS. Dzieje się tak niezależnie od tego, czy korzystasz z usługodawcy, fabryki, usługi itp. Możesz to potwierdzić za pomocą console.log()i wstrzykiwanie do wielu kontrolerów.
Luis Perez,
Luis, Twój komentarz jest sprzeczny z przyjętą odpowiedzią, jak to mówi - Na koniec ważne jest, aby zdać sobie sprawę, że wszystkie usługi Angular są singletonami aplikacji. Oznacza to, że na wtryskiwacz przypada tylko jedna instancja danej usługi.
Ankur Marwaha,
@AnkurMarwaha może coś nie rozumiem. Cytujesz „ważne jest, aby zdawać sobie sprawę, że wszystkie usługi Angular są singletonami aplikacji” - fakt, że są singletonami, oznacza, że ​​są tworzone tylko raz. Tak powiedziałem „Nowa instancja nie jest tworzona za każdym razem, jest tworzona tylko raz i jest buforowana…”. Czy możesz bardziej szczegółowo wskazać, gdzie widzisz konflikt?
Luis Perez,
1
Ach, widzę zamieszanie. „Wtryskiwacz” to obiekt kątowy. Odpowiada za „zastrzyk”. Przykład: przy pierwszym uruchomieniu sterownika „wtryskiwacz” sprawdza parametry i wstrzykuje każdy z nich. Jest tylko jeden „wtryskiwacz” dla całej aplikacji. Gdy wtryskiwacz utworzy określoną fabrykę lub usługę, zachowuje instancję i wykorzystuje ją ponownie - stąd singleton. Jest więc tylko jeden wtryskiwacz na aplikację i tylko jedna instancja danej usługi na wtryskiwacz. Większość aplikacji Angular ma tylko jedną aplikację, a zatem jeden wtryskiwacz, a zatem jedno wystąpienie dowolnej usługi, kontrolera itp.
Luis Perez
13

Oto kilka innych przykładów usług w porównaniu z fabrykami, które mogą być przydatne w dostrzeżeniu różnicy między nimi. Zasadniczo usługa wywołała „nowe ...”, jest już utworzona. Fabryka nie jest tworzona automatycznie.

Podstawowe przykłady

Zwraca obiekt klasy, który ma jedną metodę

Oto usługa, która ma jedną metodę:

angular.service('Hello', function () {
  this.sayHello = function () { /* ... */ };
});

Oto fabryka, która zwraca obiekt za pomocą metody:

angular.factory('ClassFactory', function () {
  return {
    sayHello: function () { /* ... */ }
  };
});

Zwróć wartość

Fabryka, która zwraca listę liczb:

angular.factory('NumberListFactory', function () {
  return [1, 2, 3, 4, 5];
});

console.log(NumberListFactory);

Usługa, która zwraca listę liczb:

angular.service('NumberLister', function () {
  this.numbers = [1, 2, 3, 4, 5];
});

console.log(NumberLister.numbers);

Dane wyjściowe w obu przypadkach są takie same, lista liczb.

Zaawansowane przykłady

Zmienne „klasy” wykorzystujące fabryki

W tym przykładzie definiujemy CounterFactory, który zwiększa lub zmniejsza licznik i możesz uzyskać bieżącą liczbę lub liczbę obiektów CounterFactory, które zostały utworzone:

angular.factory('CounterFactory', function () {
  var number_of_counter_factories = 0; // class variable

  return function () {
    var count = 0; // instance variable
    number_of_counter_factories += 1; // increment the class variable

    // this method accesses the class variable
    this.getNumberOfCounterFactories = function () {
      return number_of_counter_factories;
    };

    this.inc = function () {
      count += 1;
    };
    this.dec = function () {
      count -= 1;
    };
    this.getCount = function () {
      return count;
    };
  }

})

Używamy CounterFactorydo tworzenia wielu liczników. Możemy uzyskać dostęp do zmiennej klasy, aby zobaczyć, ile liczników zostało utworzonych:

var people_counter;
var places_counter;

people_counter = new CounterFactory();
console.log('people', people_counter.getCount());
people_counter.inc();
console.log('people', people_counter.getCount());

console.log('counters', people_counter.getNumberOfCounterFactories());

places_counter = new CounterFactory();
console.log('places', places_counter.getCount());

console.log('counters', people_counter.getNumberOfCounterFactories());
console.log('counters', places_counter.getNumberOfCounterFactories());

Dane wyjściowe tego kodu to:

people 0
people 1
counters 1
places 0
counters 2
counters 2

źródło
to przydatny przykład, liczba_fabryk jest jak meta atrybut klasy CounterFactory, prawda? Rozumiem, że ten przykład można powielać w usłudze (powiedz mi, jeśli się mylę), jaka byłaby różnica semantyczna w tym przypadku?
geoom
Przydatny przykład! Oznacza to w zasadzie, że w fabryce możesz mieć dodatkową warstwę abstrakcji, która nie byłaby dostępna w serwisie. Cokolwiek zostanie zwrócone, nowa instancja zostanie zwrócona za każdym razem, gdy zostanie użyte „nowe”. Wszelkie zmienne, które nie są zadeklarowane w bloku zwrotnym, będą singletonami. Czy dobrze to zrozumiałem?
Swanidhi
@Swanidhi w zasadzie tak, możesz zadeklarować zmienne, które są singletonami w fabryce. Dlatego nazwałem je zmiennymi „klasowymi”.
13

„Fabryka” i „Serwis” to różne sposoby wykonywania DI (wstrzykiwanie zależności) pod kątem.

Kiedy więc definiujemy DI za pomocą „usługi”, jak pokazano w poniższym kodzie. To tworzy nową GLOBALNĄ instancję obiektu „Logger” i wstrzykuje ją do funkcji.

app.service("Logger", Logger); // Injects a global object

Kiedy definiujesz DI za pomocą „fabryki”, nie tworzy instancji. Po prostu przechodzi tę metodę, a później konsument musi wewnętrznie wykonywać połączenia z fabryką dla instancji obiektów.

app.factory("Customerfactory", CreateCustomer);

Poniżej znajduje się prosty obraz, który pokazuje wizualnie, w jaki sposób proces DI dla „Usługi” różni się od „Fabryki”.

wprowadź opis zdjęcia tutaj

Fabryka powinna być używana, gdy chcemy tworzyć różne typy obiektów w zależności od scenariuszy. Na przykład w zależności od scenariusza chcemy utworzyć prosty obiekt „Klient” lub „Klient” z obiektem „Adres” lub „Klient” z obiektem „Telefon”. Oto szczegółowe wyjaśnienie tego akapitu

Z usługi należy korzystać, gdy mamy do dyspozycji zastrzyk narzędzi lub funkcji wspólnych, takich jak Narzędzie, Rejestrator, Obsługa błędów itp.

Shivprasad Koirala
źródło
Każda odpowiedź, jaką widziałem na to pytanie i podobne, określa różnicę w mechanice i składni. Ta odpowiedź podaje prawdziwy powód, dla którego wybrałbyś jedno z nich. Jest to kwestia semantyki i patrzenie na nazwę, usługę lub fabrykę, komunikuje ich cel i sposób ich użycia.
Joe Mayo
8

Styl usługi : ( prawdopodobnie najprostszy ) zwraca rzeczywistą funkcję: Przydatny do współdzielenia funkcji narzędziowych, które można wywołać, po prostu dodając () do wstrzykniętego odwołania do funkcji.

Usługa w AngularJS to pojedynczy obiekt JavaScript, który zawiera zestaw funkcji

var myModule = angular.module("myModule", []);

myModule.value  ("myValue"  , "12345");

function MyService(myValue) {
    this.doIt = function() {
        console.log("done: " + myValue;
    }
}

myModule.service("myService", MyService);
myModule.controller("MyController", function($scope, myService) {

    myService.doIt();

});

Styl fabryczny : ( bardziej zaangażowany, ale bardziej wyrafinowany ) zwraca wartość zwracaną przez funkcję: tworzy obiekt w stylu jak nowy obiekt () w java.

Fabryka to funkcja tworząca wartości. Gdy usługa, sterownik itp. Potrzebuje wartości wprowadzonej z fabryki, fabryka tworzy wartość na żądanie. Po utworzeniu wartość jest ponownie wykorzystywana dla wszystkich usług, kontrolerów itp., Które wymagają jej wstrzyknięcia.

var myModule = angular.module("myModule", []);

myModule.value("numberValue", 999);

myModule.factory("myFactory", function(numberValue) {
    return "a value: " + numberValue;
})  
myModule.controller("MyController", function($scope, myFactory) {

    console.log(myFactory);

});

Styl dostawcy : ( pełna wersja, konfigurowalna wersja ) zwraca wynik funkcji $ get function: Configurable.

Dostawcy w AngularJS to najbardziej elastyczna forma fabryki, jaką możesz stworzyć. Rejestrujesz dostawcę w module tak, jak robisz to w usłudze lub fabryce, tyle że zamiast tego używasz funkcji dostawca ().

var myModule = angular.module("myModule", []);

myModule.provider("mySecondService", function() {
    var provider = {};
    var config   = { configParam : "default" };

    provider.doConfig = function(configParam) {
        config.configParam = configParam;
    }

    provider.$get = function() {
        var service = {};

        service.doService = function() {
            console.log("mySecondService: " + config.configParam);
        }

        return service;
    }

    return provider;
});

myModule.config( function( mySecondServiceProvider ) {
    mySecondServiceProvider.doConfig("new config param");
});

myModule.controller("MyController", function($scope, mySecondService) {

    $scope.whenButtonClicked = function() {
        mySecondService.doIt();
    }

});

src jenkov

<!DOCTYPE html>
    <html ng-app="app">
    <head>
    	<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.1/angular.min.js"></script>
    	<meta charset=utf-8 />
    	<title>JS Bin</title>
    </head>
    <body ng-controller="MyCtrl">
    	{{serviceOutput}}
    	<br/><br/>
    	{{factoryOutput}}
    	<br/><br/>
    	{{providerOutput}}
    
    	<script>
    
    		var app = angular.module( 'app', [] );
    
    		var MyFunc = function() {
    
    			this.name = "default name";
    
    			this.$get = function() {
    				this.name = "new name"
    				return "Hello from MyFunc.$get(). this.name = " + this.name;
    			};
    
    			return "Hello from MyFunc(). this.name = " + this.name;
    		};
    
    		// returns the actual function
    		app.service( 'myService', MyFunc );
    
    		// returns the function's return value
    		app.factory( 'myFactory', MyFunc );
    
    		// returns the output of the function's $get function
    		app.provider( 'myProv', MyFunc );
    
    		function MyCtrl( $scope, myService, myFactory, myProv ) {
    
    			$scope.serviceOutput = "myService = " + myService;
    			$scope.factoryOutput = "myFactory = " + myFactory;
    			$scope.providerOutput = "myProvider = " + myProv;
    
    		}
    
    	</script>
    
    </body>
    </html>

jsbin

<!DOCTYPE html>
<html ng-app="myApp">
<head>
	<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.1/angular.min.js"></script>
	<meta charset=utf-8 />
	<title>JS Bin</title>
</head>
<body>
<div ng-controller="MyCtrl">
    {{hellos}}
</div>
	<script>

	var myApp = angular.module('myApp', []);

//service style, probably the simplest one
myApp.service('helloWorldFromService', function() {
    this.sayHello = function() {
        return "Hello, World!"
    };
});

//factory style, more involved but more sophisticated
myApp.factory('helloWorldFromFactory', function() {
    return {
        sayHello: function() {
            return "Hello, World!"
        }
    };
});
    
//provider style, full blown, configurable version     
myApp.provider('helloWorld', function() {

    this.name = 'Default';

    this.$get = function() {
        var name = this.name;
        return {
            sayHello: function() {
                return "Hello, " + name + "!"
            }
        }
    };

    this.setName = function(name) {
        this.name = name;
    };
});

//hey, we can configure a provider!            
myApp.config(function(helloWorldProvider){
    helloWorldProvider.setName('World');
});
        

function MyCtrl($scope, helloWorld, helloWorldFromFactory, helloWorldFromService) {
    
    $scope.hellos = [
        helloWorld.sayHello(),
        helloWorldFromFactory.sayHello(),
        helloWorldFromService.sayHello()];
}
	</script>

</body>
</html>

jsfiddle

Premraj
źródło
2

Podstawowa różnica polega na tym, że dostawca pozwala na ustawienie prymitywnych (niebędących obiektami), tablicowych lub funkcji zwrotnych wartości w fabrycznie zadeklarowanej zmiennej, a zatem jeśli zwraca obiekt, musi być jawnie zadeklarowany i zwrócony.

Z drugiej strony usługi można użyć tylko do ustawienia zmiennej zadeklarowanej dla obiektu na obiekt, dzięki czemu możemy uniknąć jawnego tworzenia i zwracania obiektów, z drugiej strony pozwala to na użycie tego słowa kluczowego.

Lub w skrócie: „ dostawca jest bardziej ogólną formą, podczas gdy usługa jest ograniczona tylko do obiektów”.

yoel halb
źródło
2

W ten sposób zrozumiałem różnicę między nimi pod względem wzorców projektowych:

Usługa : zwraca typ, który zostanie dodany w celu utworzenia obiektu tego typu. Jeśli używana jest analogia Java, usługa zwraca definicję klasy Java .

Fabryka : zwraca konkretny obiekt, z którego można natychmiast skorzystać. W analogii Java fabryka zwraca obiekt Java .

Część, która często myli ludzi (w tym mnie), polega na tym, że kiedy wstrzykujesz do kodu usługę lub fabrykę, można z nich korzystać w ten sam sposób, co dostajesz w kodzie w obu przypadkach, jest konkretnym obiektem, który możesz natychmiast wywołać. Co oznacza, że ​​w przypadku Usługi połączenia kątowe są „nowe” w deklaracji usługi w Twoim imieniu. Myślę, że to zawiła koncepcja.

Hisham
źródło
1

To byłaby najlepsza i krótka odpowiedź na zrozumienie dostawcy Vs Factory Vs Provider

Źródło : https://groups.google.com/forum/#!msg/angular/56sdORWEoqg/HuZsOsMvKv4J

Oto, co mówi Ben z demo http://jsbin.com/ohamub/1/edit?html,output

„W kodzie znajdują się komentarze ilustrujące podstawowe różnice, ale tutaj trochę je rozwinę. Uwaga: po prostu zastanawiam się nad tym, więc jeśli powiem coś złego, daj mi znać.

Usługi

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

Wynik : deklarując serviceName jako argument do wstrzyknięcia, otrzymasz rzeczywiste odwołanie do funkcji przekazane do module.service.

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

Fabryki

Składnia : module.factory ('nazwa_fabryki', funkcja);

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

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

Dostawcy

Składnia : module.provider ('dostawcaName', funkcja);

Wynik : deklarując nazwę dostawcy jako argument do wstrzyknięcia, otrzymasz wartość zwracaną przez wywołanie metody $ get odwołania do funkcji przekazanej do module.provider.

Użycie : Może być przydatny do zwracania funkcji „klasy”, którą można następnie zmienić w celu utworzenia instancji, ale która wymaga pewnej konfiguracji przed wstrzyknięciem. Być może przydatne w przypadku klas, które można wielokrotnie wykorzystywać w różnych projektach? Wciąż trochę niejasno w tej sprawie. ”Ben

Syed Ekram Uddin
źródło
1

Przez jakiś czas miałem to zamieszanie i staram się jak najlepiej wyjaśnić tutaj. Mam nadzieję, że to pomoże!

angular .factoryi angular .serviceoba służą do inicjalizacji usługi i pracy w ten sam sposób.

Jedyna różnica polega na tym, jak chcesz zainicjować usługę.

Oba są singletonami


var app = angular.module('app', []);


Fabryka

app.factory ( <service name>, <function with a return value>)

Jeśli chcesz zainicjować usługę z funkcji, którą masz z wartością zwracaną , musisz użyć tej factorymetody.

na przykład

function myService() {
  //return what you want
  var service = {
    myfunc: function (param) { /* do stuff */ }
  }
  return service;
}

app.factory('myService', myService);

Podczas wstrzykiwania tej usługi (np. Do kontrolera):

  • Kątowa będzie nazywać swoją daną funkcję (jako myService()), aby powrócić obiektu
  • Singleton - wywoływany tylko raz, zapisywany i przekazuje ten sam obiekt.


Usługa

app.service ( <service name>, <constructor function>)

Jeśli chcesz zainicjować usługę z funkcji konstruktora (używając thissłowa kluczowego), musisz użyć tej servicemetody.

na przykład

function myService() {
  this.myfunc: function (param) { /* do stuff */ }
}

app.service('myService', myService);

Podczas wstrzykiwania tej usługi (np. Do kontrolera):

  • Angular wykona newpodaną funkcję (as new myService()), aby zwrócić obiekt
  • Singleton - wywoływany tylko raz, zapisywany i przekazuje ten sam obiekt.


UWAGA: Jeśli używasz factoryz <constructor function>lub servicez <function with a return value>, to nie będzie działać.


Przykłady - DEMO

Bezpieczniejszy Hussain
źródło
1

To pomogło mi zrozumieć różnicę dzięki postowi na blogu Pascala Prechta.

Usługa to metoda w module, która przyjmuje nazwę i funkcję, która definiuje usługę. Możesz wstrzykiwać i używać tej konkretnej usługi w innych komponentach, takich jak kontrolery, dyrektywy i filtry. Fabryka jest metodą na module, a także wymaga nazwy i funkcji, która definiuje fabrykę. Możemy również wstrzykiwać i używać go w taki sam sposób, jak w przypadku usługi.

Obiekty utworzone przy użyciu nowego używają wartości właściwości prototypowej funkcji konstruktora jako ich prototypu, więc znalazłem kod Angular, który wywołuje Object.create (), który moim zdaniem jest funkcją konstruktora usług, gdy zostanie utworzona instancja. Jednak funkcja fabryczna jest tak naprawdę tylko funkcją, która jest wywoływana, dlatego musimy zwrócić literał obiektu dla fabryki.

Oto kod kątowy 1.5, który znalazłem dla fabryki:

var needsRecurse = false;
    var destination = copyType(source);

    if (destination === undefined) {
      destination = isArray(source) ? [] : Object.create(getPrototypeOf(source));
      needsRecurse = true;
    }

Kątowy fragment kodu źródłowego dla funkcji factory ():

 function factory(name, factoryFn, enforce) {
    return provider(name, {
      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
    });
  }

Pobiera przekazaną nazwę i funkcję fabryczną i zwraca dostawcę o tej samej nazwie, który ma metodę $ get, która jest naszą funkcją fabryczną. Ilekroć pytasz wtryskiwacza o konkretną zależność, w zasadzie pyta on odpowiedniego dostawcę o instancję tej usługi, wywołując metodę $ get (). Dlatego $ get () jest wymagany podczas tworzenia dostawców.

Oto kątowy kod 1.5 dla usługi.

function service(name, constructor) {
    return factory(name, ['$injector', function($injector) {
      return $injector.instantiate(constructor);
    }]);
  }

Okazuje się, że kiedy wywołujemy service (), tak naprawdę wywołuje factory ()! Jednak nie tylko przekazuje fabryczną funkcję konstruktora usług. Przekazuje także funkcję, która prosi wtryskiwacz o utworzenie obiektu przez dany konstruktor.

Innymi słowy, jeśli wstrzykniemy gdzieś MyService, to co dzieje się w kodzie to:

MyServiceProvider.$get(); // return the instance of the service

Aby ponownie utworzyć go ponownie, usługa wywołuje fabrykę, która jest metodą $ get () na odpowiednim dostawcy. Ponadto $ injector.instantiate () jest metodą, która ostatecznie wywołuje Object.create () z funkcją konstruktora. Dlatego używamy „tego” w usługach.

W przypadku ES5 nie ma znaczenia, z którego korzystamy: service () lub factory (), zawsze jest wywoływana fabryka, która tworzy dostawcę naszych usług.

Możesz zrobić dokładnie to samo z usługami. Usługa jest jednak funkcją konstruktora, która nie uniemożliwia nam zwracania literałów obiektowych. Abyśmy mogli pobrać nasz kod serwisowy i napisać go w taki sposób, że zasadniczo robi dokładnie to samo, co nasza fabryka, lub innymi słowy, możesz napisać usługę jako fabryka, aby zwrócić obiekt.

Dlaczego większość ludzi zaleca korzystanie z fabryk zamiast usług? To najlepsza odpowiedź, jaką widziałem, pochodząca z książki Pawła Kozłowskiego: Mastering Web Application Development with AngularJS.

Metoda fabryczna jest najczęstszym sposobem wprowadzania obiektów do systemu wstrzykiwania zależności AngularJS. Jest bardzo elastyczny i może zawierać wyrafinowaną logikę tworzenia. Ponieważ fabryki są funkcjami regularnymi, możemy również skorzystać z nowego zakresu leksykalnego do symulacji zmiennych „prywatnych”. Jest to bardzo przydatne, ponieważ możemy ukryć szczegóły implementacji danej usługi. ”

James Drinkard
źródło
1
  • Dzięki fabryce faktycznie tworzysz obiekt wewnątrz fabryki i oddajesz go.
  • Dzięki usłudze masz tylko standardową funkcję, która używa thissłowa kluczowego do zdefiniowania funkcji.
  • W przypadku dostawcy istnieje $getzdefiniowany przez Ciebie obiekt, który można wykorzystać do uzyskania obiektu, który zwraca dane.
Oskar
źródło
1

Istnieją trzy sposoby obsługi logiki biznesowej w AngularJS: ( zainspirowane kursem Yaakova Coursera AngularJS ):

  1. Usługa
  2. Fabryka
  3. Dostawca

Tutaj będziemy rozmawiać tylko o usłudze vs fabryka

SERWIS :

Składnia:

app.js

 var app = angular.module('ServiceExample',[]);
 var serviceExampleController =
              app.controller('ServiceExampleController', ServiceExampleController);
 var serviceExample = app.service('NameOfTheService', NameOfTheService);

 ServiceExampleController.$inject = ['NameOfTheService'] //very important as this protects from minification of js files

function ServiceExampleController(NameOfTheService){
     serviceExampleController = this;
     serviceExampleController.data = NameOfTheService.getSomeData();
 }

function NameOfTheService(){
     nameOfTheService = this;
     nameOfTheService.data = "Some Data";
     nameOfTheService.getSomeData = function(){
           return nameOfTheService.data;
     }     
}

index.html

<div ng-controller = "ServiceExampleController as serviceExample">
   {{serviceExample.data}}
</div>

Główne cechy Usługi:

  1. Instancja Lazily : jeśli usługa nie zostanie wstrzyknięta, nigdy nie zostanie utworzona. Aby go użyć, musisz wstrzyknąć go do modułu.

  2. Singleton : Jeśli zostanie wstrzyknięty do wielu modułów, wszystkie będą miały dostęp tylko do jednego konkretnego wystąpienia. Dlatego bardzo wygodne jest udostępnianie danych między różnymi kontrolerami.

FABRYKA

Porozmawiajmy teraz o fabryce w AngularJS

Najpierw spójrzmy na składnię :

app.js :

var app = angular.module('FactoryExample',[]);
var factoryController = app.controller('FactoryController', FactoryController);
var factoryExampleOne = app.factory('NameOfTheFactoryOne', NameOfTheFactoryOne);
var factoryExampleTwo = app.factory('NameOfTheFactoryTwo', NameOfTheFactoryTwo);

//first implementation where it returns a function
function NameOfTheFactoryOne(){
   var factory = function(){
      return new SomeService();
    }
   return factory;
}

//second implementation where an object literal would be returned
function NameOfTheFactoryTwo(){
   var factory = {
      getSomeService : function(){
          return new SomeService();
       }
    };
   return factory;
}

Teraz używając dwóch powyższych w kontrolerze:

 var factoryOne = NameOfTheFactoryOne() //since it returns a function
 factoryOne.someMethod();

 var factoryTwo = NameOfTheFactoryTwo.getSomeService(); //accessing the object
 factoryTwo.someMethod();

Funkcje fabryki:

  1. Tego rodzaju usługi są zgodne z fabrycznym wzorcem projektowym . Fabrykę można traktować jako centralne miejsce, w którym tworzone są nowe obiekty lub metody.

  2. To nie tylko produkuje singleton, ale także dostosowywane usługi.

  3. .service()Metoda jest fabryka , która zawsze daje ten sam rodzaj usługi, która jest pojedyncza. Nie ma łatwego sposobu skonfigurowania jego zachowania. Ta .service()metoda jest zwykle używana jako skrót do czegoś, co nie wymaga żadnej konfiguracji.

Pritam Banerjee
źródło
0

Z tą analogią możesz zrozumieć różnicę - rozważ różnicę między funkcją normalną, która zwróci pewną wartość, a funkcją konstruktora, która zostanie utworzona za pomocą nowego słowa kluczowego. Tak więc tworzenie fabryki jest podobne do tworzenia normalnej funkcji, która zwróci pewną wartość (prymitywne lub obiekt), podczas gdy tworzenie usługi przypomina tworzenie funkcji konstruktora (klasa OO), dla której możemy utworzyć instancję za pomocą nowego słowa kluczowego. Jedyną rzeczą, na którą należy zwrócić uwagę, jest to, że kiedy używamy metody Service do tworzenia usług, automatycznie tworzy jej instancję za pomocą mechanizmu wstrzykiwania zależności obsługiwanego przez AngularJS

Sourabh Ranka
źródło