2014年1月5日 星期日

AngularJS Service, Factory, Provider 差別

   初學 AngularJS 時,看到 service, factory, provider, 常讓人摸不著頭腦。在網路上survey了一陣,StackOver flow 上最被推崇的一篇:Angular.js: service vs provider vs factory?*1 看了之後還是不解其惑。直到最近在閱讀Pawel 的 Mastering Web Application Development with AngularJS*2,裡面有了詳細的說明,讓我瞬間有豁然開朗之感。以下是一些小小的整理。



關於 Dependency System

  在了解 Service, Factory, Provider的差別前,首先有一個重要的觀念,就是 AngularJS的 Dependency Injection System (DI system)*3。在開發時,有些功能或服務會在不同的 controller 中使用,如果要在每個 controller 裡自己控管該服務,會讓程式重複程式碼變多,也增加了測試時的複雜度,因此 AngularJS 提供了 DI engine 來替 App 控管這些功能和服務。

AngularJS DI system 的整理如下:

  1. 為 AngularJS App 提供了一個抽象層,透過 DI engine 來控管服務的生命週期。
  2. 所有服務都需要註冊 (Registering Service)。
  3. 向 DI system 註冊服務有3種「方式」:Service, Factory, Provider。
  4. 在 DI system 情境下提到 Service,就是一般在說的「服務」,其中包括service,factory,provider 等3種。
  5. 所有向 DI 註冊的 Service 都是 Singleton !

關於 Registering Service

    AngularJS 註冊 Service 的方法有3種:service, factory, provider。我想比較好解釋的方法是先看這三個註冊方式的架構,再來解釋其中的差別。

service:
myApp.service('myService', function(){
   // 關於這個 service 的code, 當調用 myService 時,會回傳這個 function object 本身!
});


factory:
myApp.factory('myFactory', function(){
  // 關於這個服務的一些初始化設定,提供了類似 private 的區域。

  return {
     // 當調用 myFactory 時,回傳的物件,可以操作的method, 變數...。只要是合法的 JS object 即可,即便是 function object 也可以。
  };
});


provider:
myApp.provider('myProvider', function(){
  // 關於這個服務的一些初始化設定,提供了類似 private 的區域。

  return {
    // 在 App 調用 myProvider 前 (angularJS 調用 $get() 方法前),可以在外部對此服務做一些調整
  };

  $get: function(){ // 此function 等同於 factory 的function,也就是在 controller 裡注入 myProvider 時,所拿到的服務實體
    
    // 如同 factory,可在此對這個服務做一些初始化設定,提供了類似 private 的區域。

    return{
      // 當拿到此服務實體時,所可以操作的method, 變數...
    };
  }; 
});

從 AngularJS 所提供的3種 Service 註冊方式來看,複雜度為:service < factory < provider。

詳細說明和整理

service:

  透過 service 所註冊的服務,在 controller 取得實體時,拿到的實體就是該傳進去的 function。DI engine 透過所提供的 function 「new」 出該 function 的實體,該 function 相當於是 JS constructor 的功能。DI engine 的服務都是 singleton,也就是 AngularJS 只有在 App 一開始時 會初始化向 DI 註冊的 service,之後調用服務的動作,都只是「取得」服務,不會再有 new 的動作。

使用頻率:較少
使用時機:當在外部已經寫好了某個服務的 constructor,可透過 service 直接用該 constructor 向 DI system 註冊服務。

factory:

  factory 註冊方式相較 service 註冊方式提供較高的彈性,因為在 function 裡面多了一個 return 區域。在一開始產生該服務實體時,factory 可以有先行的預設值和邏輯運算,而 return 區域則是在 controller 調用該 service 時,拿到的實體物件。因此大部分自製的服務要向 DI 註冊時,透過 factory 註冊比較適合。

使用頻率:較多
使用時機:自行製作該 「App」的服務時。

provider:

  provider 提供彈性最大的方式讓 user 向 DI system 註冊服務。provider 裡面有兩個結構,一個是 return,另一個是 $get。
  return 提供 provider 本身的 function。也就是提供更廣泛的客製化。User 可改變 provider 的設定,但是必須要在 AngularJS 呼叫此服務的 $get function 前。通常是透過可在 App 的 config 做設定。
  而 $get function 可以視做和 factory 完全相同的功能,在$get 中的 return 才是 controller 在調用該服務時,取得的實體物件。

使用頻率:普通
使用時機:自行製作該 「App」的服務,且希望該服務能夠客製化,在注入前做一些設定和調整。

最後補充一點,DI system 所提供的服務,可以在 AngularJS app 中任何地方調用,不只限於 controller。

以上,希望能解決一些對 service, factory, provider 的迷惑。

References