AngularJS:何时使用服务而不是工厂

Posted

技术标签:

【中文标题】AngularJS:何时使用服务而不是工厂【英文标题】:AngularJS : When to use service instead of factory 【发布时间】:2013-09-27 04:42:04 【问题描述】:

请在这里忍耐一下。我知道还有其他答案,例如: AngularJS: Service vs provider vs factory

但是我仍然不知道你什么时候会使用服务而不是工厂。

据我所知,工厂通常用于创建可以被多个控制器调用的“通用”函数:Creating common controller functions

Angular 文档似乎更喜欢工厂而不是服务。他们甚至在使用工厂时提到“服务”,这更加令人困惑! http://docs.angularjs.org/guide/dev_guide.services.creating_services

那么什么时候会使用服务?

是否有一些事情只有通过服务才能实现或更容易完成?

幕后有什么不同吗?性能/内存差异?

这是一个例子。除了声明的方法之外,它们似乎相同,我不知道为什么我会做一个与另一个。 http://jsfiddle.net/uEpkE/

更新: 从 Thomas 的回答看来,服务用于更简单的逻辑,工厂用于使用私有方法的更复杂逻辑,所以我更新了下面的小提琴代码,似乎两者都能够支持私有函数?

myApp.factory('fooFactory', function() 
    var fooVar;
    var addHi = function(foo) fooVar = 'Hi '+foo; 

    return 
        setFoobar: function(foo)
            addHi(foo);
        ,
        getFoobar:function()
            return fooVar;
        
    ;
);
myApp.service('fooService', function() 
    var fooVar;
    var addHi = function(foo) fooVar = 'Hi '+foo;

    this.setFoobar = function(foo)
        addHi(foo);
    
    this.getFoobar = function()
        return fooVar;
    
);

function MyCtrl($scope, fooService, fooFactory) 
    fooFactory.setFoobar("fooFactory");
    fooService.setFoobar("fooService");
    //foobars = "Hi fooFactory, Hi fooService"
    $scope.foobars = [
        fooFactory.getFoobar(),
        fooService.getFoobar()
    ];

【问题讨论】:

当然服务支持私有,但如果你没看错我的帖子,它纯粹是代码风格:我们还可以利用新的词法范围来模拟“私有”变量。这是“模拟” 我觉得这个讨论很有用***.com/questions/15666048/… 还有some good answers here 【参考方案1】:

说明

你在这里得到了不同的东西:

第一:

如果您使用服务,您将获得函数实例(“this” 关键词)。 如果您使用工厂,您将获得 返回的值 调用函数引用(工厂中的 return 语句)。

参考: angular.service vs angular.factory

第二:

请记住,AngularJS 中的所有提供者(值、常量、服务、工厂)都是单例的!

第三:

使用其中一个(服务或工厂)与代码风格有关。 但是,AngularJS 中的常用方法 是使用factory

为什么?

因为 “工厂方法是获取对象到AngularJS依赖注入系统中最常见的方式。它非常灵活,可以包含复杂的创建逻辑。由于工厂是常规函数,我们还可以利用新的词法范围来模拟“私有”变量。这非常有用,因为我们可以隐藏给定服务的实现细节。”

参考:http://www.amazon.com/Mastering-Web-Application-Development-AngularJS/dp/1782161821)。


用法

Service : 可以通过简单地将() 附加到注入的函数引用来共享有用的实用函数。也可以使用injectedArg.call(this) 或类似名称运行。

Factory : 可以用于返回一个“类”函数,然后可以通过 new`ed 来创建实例。

因此,当您的服务中有复杂的逻辑时使用工厂并且您不想暴露这种复杂性

在其他情况下如果您想返回服务实例,只需使用服务

但随着时间的推移,你会发现我认为在 80% 的情况下你会使用工厂。

更多详情:http://blog.manishchhabra.com/2013/09/angularjs-service-vs-factory-with-example/


更新:

这里的优秀帖子: http://iffycan.blogspot.com.ar/2013/05/angular-service-or-factory.html

“如果您希望您的函数像普通函数一样调用,请使用 工厂。如果你希望你的函数用新的实例化 运营商,使用服务。如果您不知道区别,请使用工厂。”


更新:

AngularJS 团队做他的工作并给出解释: http://docs.angularjs.org/guide/providers

从这个页面:

“Factory 和 Service 是最常用的配方。它们之间的唯一区别是 Service recipe 更适用于自定义类型的对象,而 Factory 可以生成 javascript 原语和函数。”

【讨论】:

Re First:我到处都读过,但我不明白它的实际含义。我想从您的回答中“大部分”没有实际区别?感谢您的书参考! 如果你的服务真的很复杂,你需要私有方法和对象使用工厂,这很简单 我注意到您添加了“如果您想返回服务实例,请使用服务”。我的后续问题是您想何时返回服务实例?我试图在这里找到一个特定的用例。 “由于工厂是常规函数,我们还可以利用新的词法范围来模拟“私有”变量。” - 这不是特定于工厂的,你可以对服务做同样的事情.. 似乎谷歌团队更喜欢服务而不是工厂,这让事情变得更加混乱! google-styleguide.googlecode.com/svn/trunk/…【参考方案2】:

allernhwkim 最初在这个问题上发布了an answer,链接到his blog,但是版主删除了它。这是我发现的唯一一篇文章,它不仅告诉您如何对服务、提供商和工厂做同样的事情,而且还告诉您使用提供商可以做哪些工厂无法做到的事情,以及一个你无法提供服务的工厂。

直接来自他的博客:

app.service('CarService', function() 
   this.dealer="Bad";
    this.numCylinder = 4;
);

app.factory('CarFactory', function() 
    return function(numCylinder) 
      this.dealer="Bad";
        this.numCylinder = numCylinder
    ;
);

app.provider('CarProvider', function() 
    this.dealerName = 'Bad';
    this.$get = function() 
        return function(numCylinder) 
            this.numCylinder = numCylinder;
            this.dealer = this.dealerName;
        
    ;
    this.setDealerName = function(str) 
      this.dealerName = str;
          
);

这显示了 CarService 将如何始终生产具有 4 个汽缸的汽车,您无法为个别汽车更改它。而 CarFactory 返回一个函数,因此您可以在控制器中执行new CarFactory,并传入特定于该汽车的多个气缸。你不能这样做 new CarService 因为 CarService 是一个对象而不是一个函数。

工厂不能这样工作的原因:

app.factory('CarFactory', function(numCylinder) 
      this.dealer="Bad";
      this.numCylinder = numCylinder
);

并自动返回一个函数供你实例化,是因为你不能这样做(向原型/等添加东西):

app.factory('CarFactory', function() 
    function Car(numCylinder) 
        this.dealer="Bad";
        this.numCylinder = numCylinder
    ;
    Car.prototype.breakCylinder = function() 
        this.numCylinder -= 1;
    ;
    return Car;
);

看看它实际上是一家生产汽车的工厂。

他博客的结论很不错:

总之,

---------------------------------------------------  
| Provider| Singleton| Instantiable | Configurable|
---------------------------------------------------  
| Factory | Yes      | Yes          | No          |
---------------------------------------------------  
| Service | Yes      | No           | No          |
---------------------------------------------------  
| Provider| Yes      | Yes          | Yes         |       
---------------------------------------------------  

    当您只需要一个简单的对象(例如 Hash)时,请使用 Service,例如 example foo;1, bar:2 很容易编码,但不能实例化 它。

    当你需要实例化一个对象时使用工厂,即新的 Customer()、new Comment() 等

    在需要配置时使用 Provider。即测试网址,质量检查网址, 生产网址。

如果你发现你只是在工厂返回一个对象,你可能应该使用服务。

不要这样做:

app.factory('CarFactory', function() 
    return 
        numCylinder: 4
    ;
);

改用服务:

app.service('CarService', function() 
    this.numCylinder = 4;
);

【讨论】:

这对我很有帮助。 +1 比较表 如果你定义了一个参数numCylinder的服务函数,那么它将具有与工厂方法相同的灵活性 去阅读博文,浪费你的时间试图弄清楚角度,如果你在阅读这篇文章后了解javascript,你就会完全理解这之间的区别。 非常惊讶!您在这里指的是一个博客,两者都在说完全相反的事情。你说:Factory - Instantiable - Yes 博客说:Factory - Instantiable - No 我同意@Devesh。我认为您将实例混淆了。来自博客文章:“只有工厂,您无法实现这一点,因为工厂无法实例化”。【参考方案3】:

所有这些提供者的概念比最初看起来要简单得多。如果你剖析你的提供者并抽出不同的部分,它就会变得非常清楚。

简单地说,这些提供程序中的每一个都是另一个的专用版本,按以下顺序:provider > factory > value / constant / service

只要提供程序尽您所能,您就可以在链的更下游使用提供程序,这将导致编写更少的代码。如果它没有完成你想要的,你可以继续往上走,你只需要编写更多的代码。

这张图片说明了我的意思,在这张图片中,您将看到提供程序的代码,突出显示的部分向您展示了提供程序的哪些部分可用于创建工厂、值等。

(来源:simplygoodcode.com)

有关更多详细信息和示例,请参阅我从中获取图片的博客文章:http://www.simplygoodcode.com/2015/11/the-difference-between-service-provider-and-factory-in-angularjs/

【讨论】:

【参考方案4】:

工厂和服务都会产生单例对象,这些对象可以由提供者配置并注入到控制器和运行块中。从被注入者的角度来看,对象是来自工厂还是来自服务,完全没有区别。

那么,什么时候使用工厂,什么时候使用服务呢?它归结为您的编码偏好,仅此而已。如果你喜欢模块化 JS 模式,那就去工厂吧。如果你喜欢构造函数(“类”)风格,那就去服务吧。请注意,这两种样式都支持私有成员。

服务的优势可能在于它从 OOP 的角度来看更直观:创建一个“类”,并与提供者一起,跨模块重用相同的代码,并改变实例化对象的行为只需在配置块中向构造函数提供不同的参数即可。

【讨论】:

您能否通过在配置块中向构造函数提供不同的参数来举例说明您的意思?如果只是服务或工厂,如何提供参数。 “与提供商一起”是什么意思?能够配置它让我觉得我的很多对象应该是提供者而不是工厂或服务。【参考方案5】:

与服务相比,工厂没有什么不能做或做得更好。反之亦然。工厂似乎更受欢迎。这样做的原因是它在处理私人/公共成员方面很方便。在这方面,服务会更加笨拙。 在编写服务时,您倾向于通过“this”关键字公开对象成员,并且可能会突然发现这些公共成员对私有方法(即内部函数)不可见。

var Service = function()

  //public
  this.age = 13;

  //private
  function getAge()

    return this.age; //private does not see public

  

  console.log("age: " + getAge());

;

var s = new Service(); //prints 'age: undefined'

Angular 使用“new”关键字为您创建服务,因此 Angular 传递给控制器​​的实例将具有相同的缺点。 当然你可以通过使用这个/那个来解决这个问题:

var Service = function()

  var that = this;

  //public
  this.age = 13;

  //private
  function getAge()

    return that.age;

  

  console.log("age: " + getAge());

;

var s = new Service();// prints 'age: 13'  

但是,如果 Service 常量很大,this\that-ing 会使代码的可读性变差。 此外,服务原型不会看到私有成员——他们只能使用公共成员:

var Service = function()

  var name = "George";

;

Service.prototype.getName = function()

  return this.name; //will not see a private member

;

var s = new Service();
console.log("name: " + s.getName());//prints 'name: undefined'

总结一下,使用Factory更方便。 As Factory 没有这些缺点。我建议默认使用它。

【讨论】:

这个答案有几个问题。首先,这篇文章演示了 Javascript 的词法作用域概念,而不是 AngularJS 服务的工作方式。其次,调用myapp.service(...) 的上下文完全丢失了。 new Service()应该在哪里被调用,在服务函数中或者在Service被注入的地方。在myapp.service ('Service', function() ...) 的上下文中,第三个列表根本不可能。【参考方案6】:

即使他们说所有服务和工厂都是单例的,我也不是 100% 同意这一点。我会说工厂不是单身,这就是我回答的重点。我真的会考虑定义每个组件(服务/工厂)的名称,我的意思是:

工厂因为不是单例,你可以在注入时创建任意数量的对象,因此它的工作方式类似于对象工厂。您可以创建域实体的工厂,并更舒适地使用这些对象,这些对象可能类似于您的模型对象。当您检索多个对象时,您可以将它们映射到此对象中,它可以充当 DDBB 和 AngularJs 模型之间的另一层。您可以向对象添加方法,以便您在 AngularJs 应用程序中更多地面向对象。

同时一个服务是一个单例,所以我们只能创建一个,也许不能创建,但是当我们注入控制器时我们只有一个实例,所以服务提供更像是一个对控制器的公共服务(休息调用、功能..)。

从概念上讲,您可以认为服务提供服务,工厂可以创建一个类的多个实例(对象)

【讨论】:

【参考方案7】:

服务

语法:module.service('serviceName', function ); 结果:将 serviceName 声明为可注入参数时,您将获得传递给 module.service 的实际函数引用。

用法:通过简单地将 () 附加到注入的函数引用来共享有用的实用函数可能很有用。也可以使用 injectArg.call( this ) 或类似方法运行。

工厂

语法:module.factory('factoryName', function);

结果:当将 factoryName 声明为可注入参数时,您将获得通过调用传递给 module.factory 的函数引用返回的值。

用法:对于返回“类”函数很有用,然后可以通过新函数创建实例。

提供者

语法:module.provider('providerName', function);

结果:当将 providerName 声明为可注入参数时,您将获得通过调用传递给 module.provider 的函数引用的 $get 方法返回的值。

用法:对于返回一个“类”函数可能很有用,该函数可以被新建以创建实例,但在注入之前需要某种配置。也许对跨项目可重用的类有用?这个还是有点朦胧。

【讨论】:

【参考方案8】:

可以以您想要的方式使用这两种方法:无论是创建对象还是j都可以访问函数


您可以从服务中创建新对象

app.service('carservice', function() 
    this.model = function()
        this.name = Math.random(22222);
        this.price = 1000;
        this.colour = 'green';
        this.manufacturer = 'bmw';
    
);

.controller('carcontroller', function ($scope,carservice)  
    $scope = new carservice.model();
)

注意:

服务默认返回对象而不是构造函数。 这就是为什么将构造函数设置为 this.model 属性的原因。 由于此服务将返回对象,但该对象内部将是构造函数,用于创建新对象;

您可以从工厂创建新对象

app.factory('carfactory', function() 
    var model = function()
        this.name = Math.random(22222);
        this.price = 1000;
        this.colour = 'green';
        this.manufacturer = 'bmw';
    
    return model;
);

.controller('carcontroller', function ($scope,carfactory)  
    $scope = new carfactory();
)

注意:

工厂默认返回构造函数而不是对象。 这就是为什么可以使用构造函数创建新对象的原因。

创建仅用于访问简单功能的服务

app.service('carservice', function () 
   this.createCar = function () 
       console.log('createCar');
   ;
   this.deleteCar = function () 
       console.log('deleteCar');
   ;
);

.controller('MyService', function ($scope,carservice)  
    carservice.createCar()
)

创建仅用于访问简单函数的工厂

app.factory('carfactory', function () 
    var obj =  
        obj.createCar = function () 
            console.log('createCar');
        ;
       obj.deleteCar = function () 
       console.log('deleteCar');
    ;
);

.controller('MyService', function ($scope,carfactory)  
    carfactory.createCar()
)

结论:

无论是创建新对象还是 只是为了访问简单的功能 使用一个对另一个不会有任何性能损失 两者都是单例对象,每个应用只创建一个实例。 在每个传递引用的地方都只有一个实例。 在 Angular 文档中,工厂被称为服务,而且服务被称为服务

【讨论】:

【参考方案9】:

Factory 和 Service 是最常用的方法。它们之间的唯一区别是,Service 方法更适用于需要继承层次结构的对象,而 Factory 可以生成 JavaScript 原语和函数。

Provider 函数是核心方法,其他的只是语法糖。只有在构建需要全局配置的可重用代码时才需要它。

有五种方法可以创建服务:Value、Factory、Service、Provider 和 Constant。您可以在这里angular service 了解更多信息,本文通过实际演示示例解释了所有这些方法。

.

【讨论】:

以上是关于AngularJS:何时使用服务而不是工厂的主要内容,如果未能解决你的问题,请参考以下文章

为啥我应该在 AngularJS 中选择使用服务而不是工厂? [复制]

angular js:是不是需要在模块文件中加载所有控制器和工厂/服务

AngularJS:为啥在说“服务”时使用“工厂”?

服务和工厂之间的区别[重复]

什么时候使用抽象工厂模式?

何时使用工厂方法模式?