angular.service vs angular.factory


1065

Tôi đã thấy cả angular.factory ()angular.service () được sử dụng để khai báo các dịch vụ; tuy nhiên, tôi không thể tìm thấy angular.service bất cứ nơi nào trong tài liệu chính thức.

Sự khác biệt giữa hai phương pháp là gì?
Nên dùng cái gì để làm gì (giả sử họ làm những việc khác nhau)?



4
Tôi đã tìm kiếm "[angularjs] nhà máy dịch vụ", nhưng tôi cũng đã nhớ rằng đã có một câu hỏi về nó (bởi vì tôi đã nghĩ về việc tự viết / câu hỏi này tại một thời điểm).
Mark Rajcok

2
Trong một tìm kiếm, dấu ngoặc vuông có nghĩa là một thẻ không?
jacob

11
@Jacob Square Brackets đang thu hẹp tìm kiếm của bạn. Chỉ thị [angularjs] - sẽ tìm kiếm 'chỉ thị' cho các câu hỏi đã được gắn thẻ với angularjs.
Mahbub

1
@Mahbub Nói cách khác, "có" :)
Brian

Câu trả lời:


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

Tôi gặp khó khăn trong việc xoay quanh khái niệm này cho đến khi tôi tự đặt nó theo cách này:

Dịch vụ : chức năng mà bạn viết sẽ là mới -ed:

  myInjectedService  <----  new myServiceFunction()

Factory : hàm (constructor) mà bạn viết sẽ được gọi :

  myInjectedFactory  <---  myFactoryFunction()

Những gì bạn làm với điều đó là tùy thuộc vào bạn, nhưng có một số mẫu hữu ích ...

Chẳng hạn như viết một hàm dịch vụ để hiển thị API công khai:

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

Hoặc sử dụng chức năng của nhà máy để hiển thị API công khai:

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

Hoặc sử dụng hàm nhà máy để trả về hàm tạo:

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

Nên dùng cái nào? ...

Bạn có thể hoàn thành điều tương tự với cả hai. Tuy nhiên, trong một số trường hợp, nhà máy cho phép bạn linh hoạt hơn một chút để tạo ra một mũi tiêm với cú pháp đơn giản hơn. Đó là bởi vì trong khi myInjectionService phải luôn là một đối tượng, myInjectionFactory có thể là một đối tượng, tham chiếu hàm hoặc bất kỳ giá trị nào. Ví dụ: nếu bạn đã viết một dịch vụ để tạo một hàm tạo (như trong ví dụ cuối cùng ở trên), thì nó sẽ phải được khởi tạo như vậy:

var myShinyNewObject = new myInjectedService.myFunction()

được cho là ít mong muốn hơn thế này:

var myShinyNewObject = new myInjectedFactory();

(Nhưng bạn nên cảnh giác về việc sử dụng kiểu mẫu này ngay từ đầu bởi vì các đối tượng mới trong bộ điều khiển của bạn tạo ra các phụ thuộc khó theo dõi, rất khó để thử nghiệm để kiểm tra. bạn sử dụng new()wily-nilly.)


Một điều nữa, tất cả họ đều là người Singletons ...

Cũng nên nhớ rằng trong cả hai trường hợp, góc cạnh đang giúp bạn quản lý một singleton. Bất kể nơi nào hoặc bao nhiêu lần bạn tiêm dịch vụ hoặc chức năng của mình, bạn sẽ nhận được cùng một tham chiếu đến cùng một đối tượng hoặc chức năng. (Ngoại trừ khi một nhà máy chỉ trả về một giá trị như một số hoặc chuỗi. Trong trường hợp đó, bạn sẽ luôn nhận được cùng một giá trị, nhưng không phải là một tham chiếu.)


2
Sẽ tốt hơn nếu gọi nó là một hàm tạo đối tượng hơn Newable?
marksyzm

2
@Hugo, tôi đã chứng minh rằng bạn có thể thực hiện một cách hiệu quả điều tương tự với cả hai, chỉ là cú pháp sẽ khác nhau.
Gil Birman

105
Tôi không chắc mình sẽ phải đọc bao nhiêu lần về sự khác biệt giữa dịch vụ và nhà máy trước khi tôi tin rằng cả hai đều cần thiết
DMac the Kẻ hủy diệt

10
Chúng tôi đã có một động từ để nói "với mới", đó là "khởi tạo". Chỉ để tham khảo. :)
sscarduzio 27/1/2015

7
Các nhà máy là các chức năng được gọi, vì vậy chúng có thể trả về bất cứ thứ gì. Mặt khác, các dịch vụ được khởi tạo bởi góc thông qua new fn(), vì vậy chúng phải trả về một thể hiện.
Gil Birman

318

Chỉ cần đặt ..

// 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'));


169
Anh bạn, cảm ơn bạn. Không phải là chi tiết của các câu trả lời khác không hợp lệ, nhưng đôi khi bạn cần phiên bản 10 giây.
R Claven

4
Chỉ cần có chức năng dịch vụ trả lại không có gì. Các this.name = ... là đủ để chứng minh rằng nó được phơi bày một API.
pixelbits

3
Tuy nhiên nếu bạn quay trở lại và phản đối nó sẽ sử dụng nó thay vì điều này. jsfiddle.net/Ne5P8/1221
MrB

@MrB, đó là một tính năng JavaScript bình thường, không dành riêng cho bối cảnh của Angular hoặc câu hỏi này.
Om Shankar

@Om Shankar, Câu trả lời ở trên cho thấy sự khác biệt là việc sử dụng này so với đối tượng được trả về. Tôi đã chỉ ra rằng "NÀY" là giá trị mặc định sẽ được sử dụng với một dịch vụ tuy nhiên nếu bạn trả về một giá trị thì nó sẽ hoạt động gần như chính xác như một nhà máy. Tuy nhiên, về mặt trái, một nhà máy dường như yêu cầu một giá trị trả về khác sẽ xảy ra lỗi - (hiển thị trong ví dụ này - jsfiddle.net/hmoc0q3v/1 ).
MrB

247

Dưới đây là những khác biệt chính:

Dịch vụ

Cú pháp: module.service( 'serviceName', function );

Kết quả: Khi khai báo serviceName dưới dạng đối số có thể tiêm, bạn sẽ được cung cấp ví dụ của hàm được truyền cho module.service.

Cách sử dụng: Có thể hữu ích cho việc chia sẻ các chức năng tiện ích hữu ích để gọi bằng cách thêm ( )vào tham chiếu hàm được chèn. Cũng có thể được chạy với injectedArg.call( this )hoặc tương tự.

Các nhà máy

Cú pháp: module.factory( 'factoryName', function );

Kết quả: Khi khai báo FactoryName là một đối số có thể tiêm, bạn sẽ được cung cấp giá trị được trả về bằng cách gọi tham chiếu hàm được truyền đến module.factory.

Cách sử dụng: Có thể hữu ích cho việc trả về một hàm 'lớp' mà sau đó có thể được tạo mới để tạo các thể hiện.

Dưới đây là ví dụ sử dụng dịch vụ và nhà máy . Tìm hiểu thêm về AngularJS Service vs Factory .

Bạn cũng có thể kiểm tra tài liệu AngularJS và câu hỏi tương tự trên stackoverflow nhầm lẫn về dịch vụ so với nhà máy .


27
Tôi không đồng ý với cách sử dụng ví dụ của bạn về một nhà máy. Cả hai dịch vụ và nhà máy (giả sử một chức năng được trả về. Nó chỉ có thể là một giá trị hoặc một đối tượng) có thể được tạo mới. Trong thực tế, một dịch vụ là tùy chọn duy nhất được đảm bảo là mới vì bạn được cung cấp một thể hiện chức năng. Tôi muốn nói rằng lợi ích của việc sử dụng NHÀ MÁY đối với DỊCH VỤ là nó cho phép một số quyền kiểm soát quyền truy cập vào các thuộc tính - riêng tư & công khai trong khi tất cả các thuộc tính của dịch vụ đều được phơi bày. Và tôi nghĩ về một nhà cung cấp như một nhà máy của một nhà máy - chỉ có nó là có thể tiêm và cấu hình tại thời điểm cấu hình.
vẽ

1
@DrewR Cảm ơn bình luận của bạn, tôi đã tìm thấy một ví dụ hay về phương pháp công khai và riêng tư bằng cách sử dụng Factory: stackoverflow.com/a/14904891/65025
edzillion

Tôi phải đồng ý với @DrewR về điều này, thực sự. Tôi đã sử dụng các nhà máy để trả lại đồ vật trước đây, nhưng thành thật mà nói tại thời điểm này có thể đáng để sử dụng $providersmọi lúc.
jedd.ahyoung

dịch vụ là tự động khởi tạo các nhà xây dựng, phải không?
Sao Hỏa2049

1
@DrewR - Theo hiểu biết của tôi, sự thật là bạn có thể đạt được hiệu quả tương tự mới từ dịch vụ như bạn có thể làm với một nhà máy, nhưng đó không phải là ý nghĩa của nó. Mục tiêu chính của nó là khi bạn chỉ muốn trả về một số đối tượng tiện ích và vì nó cung cấp một cú pháp phù hợp hơn - bạn có thể chỉ cần viết this.myFunc = function(){}vào dịch vụ của mình (tiết kiệm cho bạn từ việc viết mã để tạo đối tượng như bạn phải làm với một nhà máy ).
Sinh ra ToCode

137

TL; DR

1) Khi bạn đang sử dụng Factory, bạn tạo một đối tượng, thêm thuộc tính cho nó, sau đó trả về cùng một đối tượng đó. Khi bạn chuyển nhà máy này vào bộ điều khiển của mình, các thuộc tính đó trên đối tượng sẽ có sẵn trong bộ điều khiển đó thông qua nhà máy của bạn.

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) Khi bạn đang sử dụng Dịch vụ , Angular sẽ khởi tạo nó phía sau hậu trường với từ khóa 'mới'. Do đó, bạn sẽ thêm thuộc tính vào 'cái này' và dịch vụ sẽ trả về 'cái này'. Khi bạn chuyển dịch vụ vào bộ điều khiển của mình, các thuộc tính đó trên 'this' sẽ có sẵn trên bộ điều khiển đó thông qua dịch vụ của bạn.

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

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



Không TL; DR

1) Nhà máy của
nhà máy là cách phổ biến nhất để tạo và định cấu hình dịch vụ. Thực sự không có nhiều hơn những gì TL; DR nói. Bạn chỉ cần tạo một đối tượng, thêm thuộc tính cho nó, sau đó trả lại cùng một đối tượng đó. Sau đó, khi bạn chuyển nhà máy vào bộ điều khiển của mình, các thuộc tính đó trên đối tượng sẽ có sẵn trong bộ điều khiển đó thông qua nhà máy của bạn. Một ví dụ rộng hơn là dưới đây.

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

Bây giờ, bất kỳ thuộc tính nào chúng tôi đính kèm vào 'dịch vụ' sẽ có sẵn cho chúng tôi khi chúng tôi chuyển 'myFactory' vào bộ điều khiển của chúng tôi.

Bây giờ, hãy thêm một số biến 'riêng tư' vào chức năng gọi lại của chúng tôi. Chúng không thể truy cập trực tiếp từ bộ điều khiển, nhưng cuối cùng chúng tôi sẽ thiết lập một số phương thức getter / setter trên 'dịch vụ' để có thể thay đổi các biến 'riêng tư' này khi cần.

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

Ở đây bạn sẽ nhận thấy chúng tôi không gắn các biến / chức năng đó vào 'dịch vụ'. Chúng tôi chỉ đơn giản là tạo chúng để sử dụng hoặc sửa đổi chúng sau này.

  • baseUrl là URL cơ sở mà API iTunes yêu cầu
  • _artist là nghệ sĩ chúng tôi muốn tìm kiếm
  • _finalUrl là URL cuối cùng và được xây dựng đầy đủ để chúng tôi thực hiện cuộc gọi tới iTunes makeUrl là một chức năng sẽ tạo và trả lại URL thân thiện với iTunes của chúng tôi.

Bây giờ, các biến và hàm trợ giúp / riêng tư của chúng ta đã sẵn sàng, hãy thêm một số thuộc tính vào đối tượng 'dịch vụ'. Bất cứ điều gì chúng tôi đưa vào 'dịch vụ', chúng tôi sẽ có thể sử dụng trực tiếp bất kỳ bộ điều khiển nào chúng tôi chuyển 'myFactory' vào.

Chúng ta sẽ tạo các phương thức setArtist và getArtist chỉ đơn giản là trả về hoặc đặt nghệ sĩ. Chúng tôi cũng sẽ tạo một phương thức sẽ gọi API iTunes bằng URL được tạo của chúng tôi. Phương pháp này sẽ trả lại một lời hứa sẽ thực hiện khi dữ liệu đã quay trở lại từ API iTunes. Nếu bạn chưa có nhiều kinh nghiệm sử dụng lời hứa trong Angular, tôi khuyên bạn nên thực hiện một cuộc lặn sâu vào chúng.

Dưới đây setArtist chấp nhận một nghệ sĩ và cho phép bạn đặt nghệ sĩ. getArtist trả về cuộc gọi nghệ sĩ. Các cuộc gọi đầu tiên makeUrl () để tạo URL chúng tôi sẽ sử dụng với yêu cầu $ http của chúng tôi. Sau đó, nó thiết lập một đối tượng lời hứa, thực hiện một yêu cầu $ http với url cuối cùng của chúng tôi, sau đó vì $ http trả lại một lời hứa, chúng tôi có thể gọi .success hoặc .error sau yêu cầu của chúng tôi. Sau đó, chúng tôi giải quyết lời hứa của chúng tôi với dữ liệu iTunes hoặc chúng tôi từ chối nó với thông báo 'Có lỗi'.

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

Bây giờ nhà máy của chúng tôi đã hoàn thành. Bây giờ chúng tôi có thể đưa 'myFactory' vào bất kỳ bộ điều khiển nào và sau đó chúng tôi sẽ có thể gọi các phương thức mà chúng tôi đã gắn vào đối tượng dịch vụ của mình (setArtist, getArtist và 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);
      })
  }
});

Trong bộ điều khiển ở trên, chúng tôi đang tiêm dịch vụ 'myFactory'. Sau đó, chúng tôi đặt các thuộc tính trên đối tượng $ scope xuất phát từ dữ liệu từ 'myFactory'. Mã khó khăn duy nhất ở trên là nếu bạn chưa bao giờ thực hiện lời hứa trước đó. Vì callItunes đang trả lại một lời hứa, chúng tôi có thể sử dụng phương thức .then () và chỉ đặt $ scope.data.artistData sau khi lời hứa của chúng tôi được thực hiện với dữ liệu iTunes. Bạn sẽ nhận thấy bộ điều khiển của chúng tôi rất "mỏng". Tất cả dữ liệu logic và liên tục của chúng tôi được đặt trong dịch vụ của chúng tôi, không phải trong bộ điều khiển của chúng tôi.

2) Dịch vụ
Có lẽ điều lớn nhất cần biết khi xử lý việc tạo Dịch vụ là nó được khởi tạo với từ khóa 'mới'. Đối với bạn các chuyên gia JavaScript, điều này sẽ cung cấp cho bạn một gợi ý lớn về bản chất của mã. Đối với những người bạn có nền tảng hạn chế về JavaScript hoặc cho những người không quá quen thuộc với từ khóa 'mới' thực sự làm gì, hãy xem xét một số nguyên tắc cơ bản về JavaScript cuối cùng sẽ giúp chúng tôi hiểu bản chất của Dịch vụ.

Để thực sự thấy những thay đổi xảy ra khi bạn gọi một hàm với từ khóa 'mới', hãy tạo một hàm và gọi nó bằng từ khóa 'mới', sau đó hãy cho biết trình thông dịch làm gì khi thấy từ khóa 'mới'. Kết quả cuối cùng sẽ giống nhau.

Trước tiên hãy tạo Trình xây dựng của chúng tôi.

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

Đây là một hàm xây dựng JavaScript điển hình. Bây giờ, bất cứ khi nào chúng ta gọi hàm Person bằng từ khóa 'mới', 'this' sẽ bị ràng buộc với đối tượng mới được tạo.

Bây giờ, hãy thêm một phương thức vào nguyên mẫu của Người của chúng tôi để nó sẽ có sẵn trên mọi phiên bản của 'Người' của chúng tôi.

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

Bây giờ, vì chúng tôi đặt hàm sayName trên nguyên mẫu, mọi phiên bản của Person sẽ có thể gọi hàm sayName để cảnh báo tên của cá thể đó.

Bây giờ chúng ta có hàm xây dựng Person và hàm sayName trên nguyên mẫu của nó, hãy thực sự tạo một thể hiện của Person sau đó gọi hàm sayName.

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

Vì vậy, tất cả các mã cùng nhau để tạo một hàm tạo Person, thêm một hàm vào nguyên mẫu của nó, tạo một cá thể Person, và sau đó gọi hàm trên nguyên mẫu của nó trông như thế này.

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'

Bây giờ hãy xem điều gì đang thực sự xảy ra khi bạn sử dụng từ khóa 'mới' trong JavaScript. Điều đầu tiên bạn cần lưu ý là sau khi sử dụng 'new' trong ví dụ của chúng tôi, chúng tôi có thể gọi một phương thức (sayName) trên 'tyler' giống như đó là một đối tượng - đó là vì nó là. Vì vậy, trước tiên, chúng ta biết rằng hàm tạo Person của chúng ta đang trả về một đối tượng, cho dù chúng ta có thể thấy điều đó trong mã hay không. Thứ hai, chúng ta biết rằng vì hàm sayName của chúng ta nằm trên nguyên mẫu và không trực tiếp trên cá thể Person, nên đối tượng mà hàm Person đang trả về phải được ủy quyền cho nguyên mẫu của nó khi tra cứu thất bại. Nói một cách đơn giản hơn, khi chúng ta gọi tyler.sayName () trình thông dịch nói rằng OK OK, tôi sẽ xem xét đối tượng 'tyler' mà chúng ta vừa tạo, định vị hàm sayName, sau đó gọi nó. Đợi một chút, tôi không thấy nó ở đây - tất cả những gì tôi thấy là tên và tuổi, Hãy để tôi kiểm tra nguyên mẫu. Yup, có vẻ như nó trên nguyên mẫu, hãy để tôi gọi nó.

Dưới đây là mã cho cách bạn có thể nghĩ về những gì từ khóa 'mới' thực sự đang làm trong JavaScript. Về cơ bản, đây là một ví dụ mã của đoạn văn trên. Tôi đã đặt 'chế độ xem trình thông dịch' hoặc cách trình thông dịch nhìn thấy mã bên trong các ghi chú.

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

Bây giờ có kiến ​​thức về những gì từ khóa 'mới' thực sự làm trong JavaScript, việc tạo một Dịch vụ trong Angular sẽ dễ hiểu hơn.

Điều lớn nhất cần hiểu khi tạo Dịch vụ là biết rằng Dịch vụ được khởi tạo với từ khóa 'mới'. Kết hợp kiến ​​thức đó với các ví dụ của chúng tôi ở trên, giờ đây bạn sẽ nhận ra rằng bạn sẽ trực tiếp đính kèm các thuộc tính và phương thức của mình vào 'cái này' mà sau đó sẽ được trả về từ chính Dịch vụ. Chúng ta hãy xem điều này trong hành động.

Không giống như những gì chúng ta đã làm với ví dụ Factory, chúng ta không cần tạo một đối tượng sau đó trả về đối tượng đó bởi vì, như đã đề cập nhiều lần trước đó, chúng ta đã sử dụng từ khóa 'mới' để trình thông dịch sẽ tạo đối tượng đó, ủy thác cho nó đó là nguyên mẫu, sau đó trả lại cho chúng tôi mà không cần chúng tôi phải thực hiện công việc.

Trước tiên, hãy tạo chức năng 'riêng tư' và trợ giúp của chúng tôi. Điều này sẽ trông rất quen thuộc vì chúng tôi đã làm điều tương tự chính xác với nhà máy của chúng tôi. Tôi sẽ không giải thích mỗi dòng làm gì ở đây vì tôi đã làm điều đó trong ví dụ về nhà máy, nếu bạn bối rối, hãy đọc lại ví dụ về nhà máy.

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

Bây giờ, chúng tôi sẽ đính kèm tất cả các phương thức sẽ có trong bộ điều khiển của chúng tôi vào 'cái này'.

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

});

Bây giờ giống như trong nhà máy của chúng tôi, setArtist, getArtist và callItunes sẽ có sẵn trong bất kỳ bộ điều khiển nào chúng tôi chuyển myService vào. Đây là bộ điều khiển myService (gần giống hệt như bộ điều khiển nhà máy của chúng tôi).

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

Giống như tôi đã đề cập trước đây, một khi bạn thực sự hiểu những gì 'mới', Dịch vụ gần như giống hệt với các nhà máy ở Angular.


12
Bạn có thể muốn cung cấp một liên kết trực tiếp đến blog của bạn. tylermcginnis.com/angularjs-factory-vs-service-vs-provider Tôi thấy rằng dễ đọc hơn một chút.
Tyler Collier

3
Nothin sai khi lặp lại blog của bạn ở đây, nhưng tôi đồng ý rằng đó là một bài viết trên blog greta.
R Claven

5
Giải thích chi tiết về những gì mỗi người làm dưới mui xe, nhưng vẫn không rõ tại sao và khi nào ai đó sẽ chọn sử dụng Dịch vụ qua Nhà máy. Nói cách khác, khi nào tôi muốn có một đối tượng mới hơn so với đối tượng được trả lại bởi một nhà máy. Tôi nghĩ rằng đây là sự nhầm lẫn lớn nhất.
demisx

2
Về cơ bản, nếu bạn muốn tạo kết nối liên tục đến dịch vụ từ xa, như API iTunes được đề cập trong ví dụ với kết nối không đổi (trạng thái kết nối, lịch sử cuộc gọi, lưu trữ dữ liệu), bạn có thể đi với Factory. Nếu bạn triển khai nó như một Dịch vụ, thì mỗi khi bạn muốn thứ gì đó từ API, bạn sẽ tạo lại kết nối và thực sự không thể lưu trữ bất cứ thứ gì trong đó. Bởi vì mỗi khi bạn tạo lại dịch vụ, bạn sẽ nhận được một đối tượng trống / mặc định.
Meki

4
Tôi không nghĩ đó là chính xác, @Aznim. Giống như những người khác đã nói, cả hai đều cung cấp singletons.
Cryptovirus

35

Manh mối nằm trong tên

Dịch vụ và nhà máy tương tự nhau. Cả hai sẽ mang lại một đối tượng đơn lẻ có thể được tiêm vào các đối tượng khác và do đó thường được sử dụng thay thế cho nhau.

Chúng được dự định sẽ được sử dụng về mặt ngữ nghĩa để thực hiện các mẫu thiết kế khác nhau.

Các dịch vụ dành cho việc triển khai một mẫu dịch vụ

Mẫu dịch vụ là một mẫu trong đó ứng dụng của bạn được chia thành các đơn vị chức năng nhất quán hợp lý. Một ví dụ có thể là một trình truy cập API hoặc một bộ logic nghiệp vụ.

Điều này đặc biệt quan trọng trong Angular vì các mô hình Angular thường chỉ là các đối tượng JSON được kéo từ máy chủ và vì vậy chúng ta cần một nơi nào đó để đưa logic kinh doanh của mình.

Đây là một dịch vụ Github chẳng hạn. Nó biết cách nói chuyện với Github. Nó biết về url và phương pháp. Chúng ta có thể đưa nó vào một bộ điều khiển, và nó sẽ tạo ra và trả lại một lời hứa.

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

Các nhà máy thực hiện mô hình nhà máy

Các nhà máy, mặt khác được dự định để thực hiện một mô hình nhà máy. Một mô hình nhà máy trong đó chúng ta sử dụng hàm nhà máy để tạo một đối tượng. Thông thường chúng ta có thể sử dụng điều này để xây dựng mô hình. Đây là một nhà máy trả về một hàm tạo của Tác giả:

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

Chúng tôi sẽ sử dụng điều này như vậy:

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

Lưu ý rằng các nhà máy cũng trả lại singletons.

Các nhà máy có thể trả về một nhà xây dựng

Bởi vì một nhà máy chỉ đơn giản trả về một đối tượng, nó có thể trả về bất kỳ loại đối tượng nào bạn thích, bao gồm cả hàm xây dựng, như chúng ta thấy ở trên.

Các nhà máy trả lại một đối tượng; dịch vụ là mới

Một sự khác biệt kỹ thuật là trong cách các dịch vụ và nhà máy được sáng tác. Một chức năng dịch vụ sẽ được tạo mới để tạo đối tượng. Một chức năng của nhà máy sẽ được gọi và sẽ trả về đối tượng.

  • Dịch vụ là các nhà xây dựng mới.
  • Các nhà máy chỉ đơn giản được gọi và trả lại một đối tượng.

Điều này có nghĩa là trong một dịch vụ, chúng tôi nối thêm "cái này", trong bối cảnh của một nhà xây dựng, sẽ trỏ đến đối tượng đang xây dựng.

Để minh họa điều này, đây là cùng một đối tượng đơn giản được tạo bằng cách sử dụng dịch vụ và nhà máy:

angular.module('app', [])
  .service('helloService', function() {
    this.sayHello = function() {
      return "Hello!";
    }
  })
  .factory('helloFactory', function() {
    return {
      sayHello: function() {
        return "Hello!";
      }
    }
  });

2
giải thích tuyệt vời, cảm ơn bạn! cũng có một loại trong mã mẫu Factories trong đó Authortham số kim phun nên có Person.
mikhail-t

Cảm ơn @ mik-T, tôi đã sửa lỗi chính tả.
siêu sáng

1
Việc bạn sử dụng mẫu dịch vụ là không chính xác - đây phải là một nhà máy. Nếu bạn gọi .factory () thay vì .service () bạn sẽ thấy rằng nó hoạt động giống hệt nhau. Mẫu dịch vụ có nghĩa là được cung cấp với hàm xây dựng, không phải là hàm trả về một đối tượng mới. Angular (có hiệu quả) gọi "mới" trên hàm tạo của bạn. Lý do duy nhất mà dịch vụ của bạn hoạt động là vì nếu bạn gọi "mới" trên hàm xây dựng trả về một đối tượng, bạn thực sự lấy lại đối tượng được trả về chứ không phải đối tượng được xây dựng. Và các nhà máy có thể được sử dụng để tạo ra bất cứ thứ gì bạn muốn, không chỉ là các mô hình.
Dan King

27

Tất cả các câu trả lời ở đây dường như xoay quanh dịch vụ và nhà máy, và điều đó hợp lệ vì đó là những gì được hỏi về. Nhưng nó cũng quan trọng cần lưu ý rằng có một số người khác bao gồm provider(), value()constant() .

Chìa khóa cần nhớ là mỗi cái là một trường hợp đặc biệt của cái kia. Mỗi trường hợp đặc biệt xuống chuỗi cho phép bạn làm điều tương tự với ít mã hơn. Mỗi người cũng có một số hạn chế bổ sung.

Để quyết định khi nào nên sử dụng cái nào bạn chỉ cần xem cái nào cho phép bạn làm những gì bạn muốn với ít mã hơn. Đây là một hình ảnh minh họa chúng giống nhau như thế nào:

nhập mô tả hình ảnh ở đây

Để hoàn thành từng bước phân tích và tham khảo nhanh về thời điểm sử dụng từng bạn có thể truy cập bài đăng blog nơi tôi có hình ảnh này từ:

http://www.simplygoodcode.com/2015/11/the-difference-b between-service-cung cấp-and-factory-in-angularjs /


3
@jacob có thể là như vậy, nhưng tôi nghĩ rằng khái niệm chung về không chỉ khi sử dụng mỗi cái mà tất cả chúng đều là những biến thể cơ bản của cùng một thứ là một điều quan trọng.
Luis Perez

1
@LuisPerez Liên kết đến blog của bạn và video giải thích sự khác biệt nó thực sự tuyệt vời. Dễ hiểu hơn với những ví dụ từ video :)
Alin Ciocan

24

app.factory ('fn', fn) so với app.service ('fn', fn)

Xây dựng

Với các nhà máy, Angular sẽ gọi hàm để có kết quả. Đó là kết quả được lưu trữ và tiêm.

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

Với các dịch vụ, Angular sẽ gọi hàm xây dựng bằng cách gọi mới . Các chức năng được xây dựng được lưu trữ và tiêm.

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

Thực hiện

Các nhà máy thường trả về một đối tượng theo nghĩa đen bởi vì giá trị trả về những gì được đưa vào bộ điều khiển, khối chạy, chỉ thị, v.v.

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

Các chức năng dịch vụ thường không trả lại bất cứ điều gì. Thay vào đó, họ thực hiện các chức năng khởi tạo và phơi bày. Các chức năng cũng có thể tham chiếu 'cái này' vì nó được xây dựng bằng cách sử dụng 'mới'.

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

Phần kết luận

Khi nói đến việc sử dụng các nhà máy hoặc dịch vụ, cả hai đều rất giống nhau. Chúng được đưa vào bộ điều khiển, chỉ thị, khối chạy, v.v. và được sử dụng trong mã máy khách theo cách tương tự. Chúng cũng là cả hai singletons - có nghĩa là cùng một thể hiện được chia sẻ giữa tất cả các nơi mà dịch vụ / nhà máy được tiêm.

Vì vậy, bạn nên thích cái nào? Hoặc là một - chúng giống nhau đến mức sự khác biệt là tầm thường. Nếu bạn chọn cái này hơn cái kia, chỉ cần lưu ý cách chúng được xây dựng, để bạn có thể thực hiện chúng đúng cách.


Các hàm dịch vụ không "không trả lại bất cứ thứ gì", chúng hoàn toàn trả về đối tượng được xây dựng NẾU bạn không chỉ định câu lệnh trả về của riêng bạn (trong trường hợp sau, đối tượng bạn trả về là thứ sẽ được tạo và lưu vào bộ nhớ cache, tương tự như một nhà máy).
Cryptovirus

Tôi nghĩ rằng bạn đang hiểu sai về nó ... Khi tôi nói trở lại, ý tôi là từ quan điểm từ việc triển khai chức năng dịch vụ
pixelbits

Bạn có chắc nhà máy cũng là một thị trấn?
Martian2049

5

Tôi đã dành một chút thời gian để cố gắng tìm ra sự khác biệt.

Và tôi nghĩ rằng chức năng của nhà máy sử dụng mô hình mô đun và chức năng dịch vụ sử dụng mô hình xây dựng tập lệnh java tiêu chuẩn.


2

Mẫu nhà máy linh hoạt hơn vì nó có thể trả về các hàm và giá trị cũng như các đối tượng.

Không có nhiều điểm trong mẫu dịch vụ IMHO, vì mọi thứ bạn có thể làm dễ dàng như với một nhà máy. Các ngoại lệ có thể là:

  • Nếu bạn quan tâm đến loại khai báo của dịch vụ khởi tạo của bạn vì một số lý do - nếu bạn sử dụng mẫu dịch vụ, hàm tạo của bạn sẽ là loại dịch vụ mới.
  • Nếu bạn đã có một hàm tạo mà bạn đang sử dụng ở nơi khác mà bạn cũng muốn sử dụng như một dịch vụ (mặc dù có lẽ không sử dụng nhiều nếu bạn muốn tiêm bất cứ thứ gì vào đó!).

Có thể cho rằng, mẫu dịch vụ là một cách đẹp hơn một chút để tạo một đối tượng mới theo quan điểm cú pháp, nhưng cũng tốn kém hơn để khởi tạo. Những người khác đã chỉ ra rằng góc sử dụng "mới" để tạo ra dịch vụ, nhưng điều này không hoàn toàn đúng - không thể làm điều đó vì mỗi nhà xây dựng dịch vụ có số lượng tham số khác nhau. Những gì góc cạnh thực sự là sử dụng mô hình nhà máy bên trong để bọc chức năng xây dựng của bạn. Sau đó, nó thực hiện một số trò đùa tinh nghịch thông minh để mô phỏng toán tử "mới" của javascript, gọi hàm tạo của bạn với một số lượng đối số có thể thay đổi - nhưng bạn có thể bỏ qua bước này nếu bạn chỉ sử dụng trực tiếp mô hình nhà máy, do đó tăng rất ít hiệu quả của bạn mã.


Các dịch vụ được xây dựng hiệu quả hơn so với các nhà máy vì các nhà máy sử dụng các đóng cửa tương đối đắt tiền và các dịch vụ (các lớp) có thể tận dụng nguyên mẫu.
jacob

@jacob Không chắc ý của bạn về việc đóng cửa? Nhà máy chỉ là một chức năng trả về một đối tượng. Bạn chỉ phải sử dụng bao đóng nếu đối tượng trả về của bạn yêu cầu trạng thái "riêng tư". Bạn vẫn phải làm điều tương tự nếu bạn đã sử dụng hàm tạo (dịch vụ). Tôi đưa ra quan điểm của bạn về nguyên mẫu mặc dù - mặc dù bạn vẫn có thể làm điều này trong một nhà máy nếu bạn muốn.
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 } }); } Trong khi cả MyFactory và MyService đều sử dụng nguyên mẫu, MyFactory vẫn thực hiện một cú đánh hiệu năng phải xây dựng đối tượng được trả về. Trong cả hai ví dụ, họ có quyền riêng tư, nhưng trong MyService tương đối không có sự khác biệt về hiệu suất.
jacob

1
Đối với tôi, sự khác biệt là liệu tôi có muốn sử dụng trực tiếp nhà máy mà không cần phương thức hay không: MyFactory(someArgument)(ví dụ $http()). Điều đó là không thể đối với một dịch vụ vì bạn đang tham khảo hàm tạo : MyService(someArgument).
jacob

Về thời gian xây dựng đối tượng, tôi thực sự không thấy nhà máy = {} là một điểm nhấn hiệu năng, hơn cả việc khởi tạo javascript "cái này" cho bạn khi nó gọi hàm tạo của bạn? Và tôi nghĩ rằng hiệu suất lớn hơn nằm ở phía góc cạnh khi nó bao bọc nhà xây dựng của bạn trong một nhà máy và sau đó phải nhảy qua các vòng để mô phỏng "mới" để nó có thể tiêm phụ thuộc của bạn.
Dan King
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.