AngularJS:服务vs供应商vs工厂


Answers

JS小提琴演示

factory / service / provider “Hello world”示例:

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 src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="MyCtrl">
    {{hellos}}
</div>
</body>

Question

AngularJS中的ServiceProviderFactory有什么区别?




An additional clarification is that factories can create functions/primitives, while services cannot. Check out this jsFiddle based on Epokk's: http://jsfiddle.net/skeller88/PxdSP/1351/ .

The factory returns a function that can be invoked:

myApp.factory('helloWorldFromFactory', function() {
  return function() {
    return "Hello, World!";
  };
});

The factory can also return an object with a method that can be invoked:

myApp.factory('helloWorldFromFactory', function() {
  return {
    sayHello: function() {
      return "Hello, World!";
    }
  };
});

The service returns an object with a method that can be invoked:

myApp.service('helloWorldFromService', function() {
  this.sayHello = function() {
     return "Hello, World!";
  };
});

For more details, see a post I wrote on the difference: http://www.shanemkeller.com/tldr-services-vs-factories-in-angular/




This answer address the topic/question

how Factory, Service and Constant — are just syntactic sugar on top of a provider recipe?

要么

how factory ,servic and providers are simailar internally

basically what happens is

When you make a factory() it sets you function provided in second argument to provider's $get and return it( provider(name, {$get:factoryFn }) ), all you get is provider but there is no property/method other than $get of that provider (means you can't configure this)

Source code of factory

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

When making a service() it return you providing a factory() with a function that injects the constructor (return the instance of the constructor you provided in your service) and returns it

Source code of service

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

So basically in both cases you eventually get a providers $get set to your function you provided , but you can give anything extra than $get as you can originally provide in provider() for config block




As pointed out by several people here correctly a factory, provider, service, and even value and constant are versions of the same thing. You can dissect the more general provider into all of them. 像这样:

Here's the article this image is from:




Essentially, Provider, Factory, and Service are all Services. A Factory is a special case of a Service when all you need is a $get() function, allowing you to write it with less code.

The major differences among Services, Factories, and Providers are their complexities. Services are the simplest form, Factories are a little more robust, and Providers are configurable at runtime.

Here is a summary of when to use each:

Factory : The value you are providing needs to be calculated based on other data.

Service : You are returning an object with methods.

Provider : You want to be able to configure, during the config phase, the object that is going to be created before it's created. Use the Provider mostly in the app config, before the app has fully initialized.




Let's discuss the three ways of handling business logic in AngularJS in a simple way: ( Inspired by Yaakov's Coursera AngularJS course )

SERVICE :

句法:

app.js

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

 ServiceExampleController.$inject = ['NameOfTheService'] //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>

Features of Service:

  1. Lazily Instantiated : If it is not injected it won't be instantiated ever. So to use it will have to inject it to a module.
  2. Singleton : If injected to multiple modules, all will have access to only one particular instance. That is why very convenient to share data across different controllers.

FACTORY

First let's have a look at the syntax:

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

Now using the above two in the controller:

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

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

Features of Factory:

  1. Follows the factory design pattern. The factory is a central place that produces new objects or functions.
  2. Not only produces singleton, but customizable services.
  3. The .service() method is a factory that always produces the same type of service, which is a singleton, and without any easy way to configure it's behavior. That .service() method is usually used as a shortcut for something that doesn't require any configuration whatsoever.

PROVIDER

Let's again have a look at the Syntax first:

angular.module('ProviderModule', [])
.controller('ProviderModuleController', ProviderModuleController)
.provider('ServiceProvider', ServiceProvider)
.config(Config); //optional

Config.$inject = ['ServiceProvider'];
function Config(ServiceProvider) {
  ServiceProvider.defaults.maxItems = 10; //some default value
}


ProviderModuleController.$inject = ['ServiceProvider'];
function ProviderModuleController(ServiceProvider) {
  //some methods
}

function ServiceProvider() {
  var provider = this;

  provider.defaults = {
    maxItems: 10
  };

  provider.$get = function () {
    var someList = new someListService(provider.defaults.maxItems);

    return someList;
  };
}

}

Features of Provider:

  1. Provider is the most flexible method of creating services in Angular.
  2. Not only we can create a factory that's dynamically configurable, but at the time of using the factory, with the provider method, we could custom configure the factory just once at the bootstrapping of our entire application.
  3. The factory can then be used throughout the application with custom settings. In other words, we can configure this factory before the application starts. In fact in the angular documentation it is mentioned that the provider method is what actually gets executed behind the scenes when we configure our services with either .service or .factory methods.
  4. The $get is a function that is directly attached to the provider instance. That function is a factory function. In other words, it's just like the one that we use to provide to the .factory method. In that function, we create our own service. This $get property, that's a function, is what makes the provider a provider . AngularJS expects the provider to have a $get property whose value is a function that Angular will treat as a factory function. But what makes this whole provider setup very special, is the fact that we can provide some config object inside the service provider, and that usually comes with defaults that we can later overwrite in the step, where we can configure the entire application.



For me, the revelation came when I realized that they all work the same way: by running something once , storing the value they get, and then cough up that same stored value when referenced through dependency injection .

Say we have:

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

The difference between the three is that:

  1. a 's stored value comes from running fn .
  2. b 's stored value comes from new ing fn .
  3. c 's stored value comes from first getting an instance by new ing fn , and then running a $get method of the instance.

Which means there's something like a cache object inside AngularJS, whose value of each injection is only assigned once, when they've been injected the first time, and where:

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

This is why we use this in services, and define a this.$get in providers.




所有服务都是单身人士 ; 他们会在每个应用程序中实例化一次。 它们可以是任何类型的 ,不管它是原始的,对象文字,函数还是自定义类型的实例。

valuefactoryserviceconstantprovider方法都是提供者。 他们教喷油器如何实例化服务。

最详细的,也是最全面的一个是提供者配方。 其余四种配方类型 - 价值,工厂,服务和常量 - 只是提供者配方之上的语法糖

  • Value Recipe是最简单的情况,您可以自己实例化Service并将实例化的值提供给注入器。
  • Factory工厂配方为Injector提供了一个工厂函数,当它需要实例化服务时它会调用它。 调用时, 工厂函数创建并返回服务实例。 服务的依赖关系作为函数的参数被注入。 所以使用这个配方会增加以下功能:
    • 能够使用其他服务(具有依赖关系)
    • 服务初始化
    • 延迟/延迟初始化
  • 服务配方几乎与工厂配方相同,但这里的注入器使用新操作员而不是工厂功能调用构造函数。
  • 提供者配方通常是矫枉过正的 。 通过允许您配置工厂的创建,它增加了一个间接层。

    只有当您想要公开一个必须在应用程序启动之前进行的应用程序范围配置的API时,您应该使用Provider配方。 这通常仅适用于可重用服务,其行为可能需要在应用程序之间略有不同。

  • 常量配方就像值配方,除了它允许您定义在配置阶段可用的服务。 比使用Value配方创建的服务更快。 与值不同,它们不能使用decorator进行decorator
查看提供者文档




My understanding is very simple below.

Factory: You simply create an object inside of the factory and return it.

服务:

You just have a standard function that uses this keyword to define a function.

Provider:

There is a $get object that you define and it can be used to get the object that returns data.




After reading all these post It created more confuse for me.. But still all is worthfull information.. finally I found following table which will give information with simple comparision

  • The injector uses recipes to create two types of objects: services and special purpose objects
  • There are five recipe types that define how to create objects: Value, Factory, Service, Provider and Constant.
  • Factory and Service are the most commonly used recipes. The only difference between them is that the Service recipe works better for objects of a custom type, while the Factory can produce JavaScript primitives and functions.
  • The Provider recipe is the core recipe type and all the other ones are just syntactic sugar on it.
  • Provider is the most complex recipe type. You don't need it unless you are building a reusable piece of code that needs global configuration.
  • All special purpose objects except for the Controller are defined via Factory recipes.

And for beginner understand:- This may not correct use case but in high level this is what usecase for these three.

  1. If you want to use in angular module config function should created as provider

angular.module('myApp').config(function($testProvider){
$testProvider.someFunction();
})

  1. Ajax call or third party integrations needs to be service .
  2. For Data manipulations create it as factory

For basic scenarios factory&Service behaves same.




For me the best and the simplest way of understanding the difference is:

var service, factory;
service = factory = function(injection) {}

How AngularJS instantiates particular components (simplified):

// service
var angularService = new service(injection);

// factory
var angularFactory = factory(injection);

So, for the service, what becomes the AngularJS component is the object instance of the class which is represented by service declaration function. For the factory, it is the result returned from the factory declaration function. The factory may behave the same as the service:

var factoryAsService = function(injection) {
  return new function(injection) {
    // Service content
  }
}

The simplest way of thinking is the following one:

  • Service is an singleton object instance. Use services if you want to provide a singleton object for your code.
  • Factory is a class. Use factories if you want to provide custom classes for your code (can't be done with services because they are already instantiated).

The factory 'class' example is provided in the comments around, as well as provider difference.




Little late to the party. But I thought this is more helpful for who would like to learn (or have clarity) on developing Angular JS Custom Services using factory, service and provider methodologies.

I came across this video which explains clearly about factory, service and provider methodologies for developing AngularJS Custom Services:

https://www.youtube.com/watch?v=oUXku28ex-M

Source Code: http://www.techcbt.com/Post/353/Angular-JS-basics/how-to-develop-angularjs-custom-service

Code posted here is copied straight from the above source, to benefit readers.

The code for "factory" based custom service is as follows (which goes with both sync and async versions along with calling http service):

var app = angular.module("app", []);
app.controller('emp', ['$scope', 'calcFactory',
  function($scope, calcFactory) {
    $scope.a = 10;
    $scope.b = 20;

    $scope.doSum = function() {
      //$scope.sum = calcFactory.getSum($scope.a, $scope.b); //synchronous
      calcFactory.getSum($scope.a, $scope.b, function(r) { //aynchronous
        $scope.sum = r;
      });
    };

  }
]);

app.factory('calcFactory', ['$http', '$log',
  function($http, $log) {
    $log.log("instantiating calcFactory..");
    var oCalcService = {};

    //oCalcService.getSum = function(a,b){
    //	return parseInt(a) + parseInt(b);
    //};

    //oCalcService.getSum = function(a, b, cb){
    //	var s = parseInt(a) + parseInt(b);
    //	cb(s);
    //};

    oCalcService.getSum = function(a, b, cb) { //using http service

      $http({
        url: 'http://localhost:4467/Sum?a=' + a + '&b=' + b,
        method: 'GET'
      }).then(function(resp) {
        $log.log(resp.data);
        cb(resp.data);
      }, function(resp) {
        $log.error("ERROR occurred");
      });
    };

    return oCalcService;
  }
]);

The code for "service" methodology for Custom Services (this is pretty similar to 'factory', but different from syntax point of view):

var app = angular.module("app", []);
app.controller('emp', ['$scope', 'calcService', function($scope, calcService){
	$scope.a = 10;
	$scope.b = 20;

	$scope.doSum = function(){
		//$scope.sum = calcService.getSum($scope.a, $scope.b);
		
		calcService.getSum($scope.a, $scope.b, function(r){
			$scope.sum = r;
		});		
	};

}]);

app.service('calcService', ['$http', '$log', function($http, $log){
	$log.log("instantiating calcService..");
	
	//this.getSum = function(a,b){
	//	return parseInt(a) + parseInt(b);
	//};

	//this.getSum = function(a, b, cb){
	//	var s = parseInt(a) + parseInt(b);
	//	cb(s);
	//};

	this.getSum = function(a, b, cb){
		$http({
			url: 'http://localhost:4467/Sum?a=' + a + '&b=' + b,
			method: 'GET'
		}).then(function(resp){
			$log.log(resp.data);
			cb(resp.data);
		},function(resp){
			$log.error("ERROR occurred");
		});
	};

}]);

The code for "provider" methodology for Custom Services (this is necessary, if you would like to develop service which could be configured):

var app = angular.module("app", []);
app.controller('emp', ['$scope', 'calcService', function($scope, calcService){
	$scope.a = 10;
	$scope.b = 20;

	$scope.doSum = function(){
		//$scope.sum = calcService.getSum($scope.a, $scope.b);
		
		calcService.getSum($scope.a, $scope.b, function(r){
			$scope.sum = r;
		});		
	};

}]);

app.provider('calcService', function(){

	var baseUrl = '';

	this.config = function(url){
		baseUrl = url;
	};

	this.$get = ['$log', '$http', function($log, $http){
		$log.log("instantiating calcService...")
		var oCalcService = {};

		//oCalcService.getSum = function(a,b){
		//	return parseInt(a) + parseInt(b);
		//};

		//oCalcService.getSum = function(a, b, cb){
		//	var s = parseInt(a) + parseInt(b);
		//	cb(s);	
		//};

		oCalcService.getSum = function(a, b, cb){

			$http({
				url: baseUrl + '/Sum?a=' + a + '&b=' + b,
				method: 'GET'
			}).then(function(resp){
				$log.log(resp.data);
				cb(resp.data);
			},function(resp){
				$log.error("ERROR occurred");
			});
		};		

		return oCalcService;
	}];

});

app.config(['calcServiceProvider', function(calcServiceProvider){
	calcServiceProvider.config("http://localhost:4467");
}]);

Finally the UI which works with any of the above services:

<html>
<head>
	<title></title>
	<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js" ></script>
	<script type="text/javascript" src="t03.js"></script>
</head>
<body ng-app="app">
	<div ng-controller="emp">
		<div>
			Value of a is {{a}},
			but you can change
			<input type=text ng-model="a" /> <br>

			Value of b is {{b}},
			but you can change
			<input type=text ng-model="b" /> <br>

		</div>
		Sum = {{sum}}<br>
		<button ng-click="doSum()">Calculate</button>
	</div>
</body>
</html>




Syntactic Sugar is the difference . Only provider is needed. Or in other words only provider is the real angular, all other ones are derived(to reduce code). There is a simple version as well, called Value() which returns just the value, no calculation or function. Even Value is derived from provider!

So why such complications, why can't we just use provider and forget everything else? It is supposed to help us write code easily and communicate better. And toungue-in-cheek reply would be, the more complex it gets the better selling a framework will be.

  • A provider that can return value = Value
  • A provider that can just instantiate and return = Factory (+ Value)
  • A provider that can instantiate + do something = Service (+ Factory, + Value)
  • A provider = must contain a property called $get (+Factory, + Service, + Value)

Angular injection gives us the first hint in reaching this conclusion.

"$injector is used to retrieve object instances as defined by provider " not service, not factory but provider.

And a better answer would be this: "An Angular service is created by a service factory. These service factories are functions which, in turn, are created by a service provider. The service providers are constructor functions. When instantiated they must contain a property called $get, which holds the service factory function."

So master provider and injector and all will fall in place :) . And it gets interesting in Typescript when $get can be implemented in a provider by inheriting from IServiceProvider.




Here is some broilerplate code I've come up with as a code-template for object factory in AngularjS. I've used a Car/CarFactory as an example to illustrate. Makes for simple implementation code in the controller.

     <script>
        angular.module('app', [])
            .factory('CarFactory', function() {

                /**
                 * BroilerPlate Object Instance Factory Definition / Example
                 */
                this.Car = function() {

                    // initialize instance properties
                    angular.extend(this, {
                        color           : null,
                        numberOfDoors   : null,
                        hasFancyRadio   : null,
                        hasLeatherSeats : null
                    });

                    // generic setter (with optional default value)
                    this.set = function(key, value, defaultValue, allowUndefined) {

                        // by default,
                        if (typeof allowUndefined === 'undefined') {
                            // we don't allow setter to accept "undefined" as a value
                            allowUndefined = false;
                        }
                        // if we do not allow undefined values, and..
                        if (!allowUndefined) {
                            // if an undefined value was passed in
                            if (value === undefined) {
                                // and a default value was specified
                                if (defaultValue !== undefined) {
                                    // use the specified default value
                                    value = defaultValue;
                                } else {
                                    // otherwise use the class.prototype.defaults value
                                    value = this.defaults[key];
                                } // end if/else
                            } // end if
                        } // end if

                        // update 
                        this[key] = value;

                        // return reference to this object (fluent)
                        return this;

                    }; // end this.set()

                }; // end this.Car class definition

                // instance properties default values
                this.Car.prototype.defaults = {
                    color: 'yellow',
                    numberOfDoors: 2,
                    hasLeatherSeats: null,
                    hasFancyRadio: false
                };

                // instance factory method / constructor
                this.Car.prototype.instance = function(params) {
                    return new 
                        this.constructor()
                                .set('color',           params.color)
                                .set('numberOfDoors',   params.numberOfDoors)
                                .set('hasFancyRadio',   params.hasFancyRadio)
                                .set('hasLeatherSeats', params.hasLeatherSeats)
                    ;
                };

                return new this.Car();

            }) // end Factory Definition
            .controller('testCtrl', function($scope, CarFactory) {

                window.testCtrl = $scope;

                // first car, is red, uses class default for:
                // numberOfDoors, and hasLeatherSeats
                $scope.car1     = CarFactory
                                    .instance({
                                        color: 'red'
                                    })
                                ;

                // second car, is blue, has 3 doors, 
                // uses class default for hasLeatherSeats
                $scope.car2     = CarFactory
                                    .instance({
                                        color: 'blue',
                                        numberOfDoors: 3
                                    })
                                ;
                // third car, has 4 doors, uses class default for 
                // color and hasLeatherSeats
                $scope.car3     = CarFactory
                                    .instance({
                                        numberOfDoors: 4
                                    })
                                ;
                // sets an undefined variable for 'hasFancyRadio',
                // explicitly defines "true" as default when value is undefined
                $scope.hasFancyRadio = undefined;
                $scope.car3.set('hasFancyRadio', $scope.hasFancyRadio, true);

                // fourth car, purple, 4 doors,
                // uses class default for hasLeatherSeats
                $scope.car4     = CarFactory
                                    .instance({
                                        color: 'purple',
                                        numberOfDoors: 4
                                    });
                // and then explicitly sets hasLeatherSeats to undefined
                $scope.hasLeatherSeats = undefined;
                $scope.car4.set('hasLeatherSeats', $scope.hasLeatherSeats, undefined, true);

                // in console, type window.testCtrl to see the resulting objects

            });
    </script>

Here is a simpler example. I'm using a few third party libraries that expect a "Position" object exposing latitude and longitude, but via different object properties. I didn't want to hack the vendor code, so I adjusted the "Position" objects I was passing around.

    angular.module('app')
.factory('PositionFactory', function() {

    /**
     * BroilerPlate Object Instance Factory Definition / Example
     */
    this.Position = function() {

        // initialize instance properties 
        // (multiple properties to satisfy multiple external interface contracts)
        angular.extend(this, {
            lat         : null,
            lon         : null,
            latitude    : null,
            longitude   : null,
            coords: {
                latitude: null,
                longitude: null
            }
        });

        this.setLatitude = function(latitude) {
            this.latitude           = latitude;
            this.lat                = latitude;
            this.coords.latitude    = latitude;
            return this;
        };
        this.setLongitude = function(longitude) {
            this.longitude          = longitude;
            this.lon                = longitude;
            this.coords.longitude   = longitude;
            return this;
        };

    }; // end class definition

    // instance factory method / constructor
    this.Position.prototype.instance = function(params) {
        return new 
            this.constructor()
                    .setLatitude(params.latitude)
                    .setLongitude(params.longitude)
        ;
    };

    return new this.Position();

}) // end Factory Definition

.controller('testCtrl', function($scope, PositionFactory) {
    $scope.position1 = PositionFactory.instance({latitude: 39, longitude: 42.3123});
    $scope.position2 = PositionFactory.instance({latitude: 39, longitude: 42.3333});
}) // end controller

;




I noticed something interesting when playing around with providers.

Visibility of injectables is different for providers than it is for services and factories. If you declare an AngularJS "constant" (for example, myApp.constant('a', 'Robert'); ), you can inject it into services, factories, and providers.

But if you declare an AngularJS "value" (for example., myApp.value('b', {name: 'Jones'}); ), you can inject it into services and factories, but NOT into the provider-creating function. You can, however, inject it into the $get function that you define for your provider. This is mentioned in the AngularJS documentation, but it's easy to miss. You can find it on the %provide page in the sections on the value and constant methods.

http://jsfiddle.net/R2Frv/1/

<div ng-app="MyAppName">
    <div ng-controller="MyCtrl">
        <p>from Service: {{servGreet}}</p>
        <p>from Provider: {{provGreet}}</p>
    </div>
</div>
<script>
    var myApp = angular.module('MyAppName', []);

    myApp.constant('a', 'Robert');
    myApp.value('b', {name: 'Jones'});

    myApp.service('greetService', function(a,b) {
        this.greeter = 'Hi there, ' + a + ' ' + b.name;
    });

    myApp.provider('greetProvider', function(a) {
        this.firstName = a;
        this.$get = function(b) {
            this.lastName = b.name;
            this.fullName = this.firstName + ' ' + this.lastName;
            return this;
        };
    });

    function MyCtrl($scope, greetService, greetProvider) {
        $scope.servGreet = greetService.greeter;
        $scope.provGreet = greetProvider.fullName;
    }
</script>



Related