2013年11月2日土曜日

[Angular.js]factoryとserviceの使い分けについて

最近、プライベートのプロダクトでAngular.jsを勉強がてら使っている。
Yeomanでgenerator-angularを使う方法でやってたり。
で、結構悩んで調べたことがあったので、メモ。
※使い始めて間もないので、間違いがあったらご指摘ください。

サービスを作るやり方、Angular.jsだと色々あります。
yeoman-generatorのREADMEを参考に挙げると

service, factory, provider, value, constant
と、実にたくさん。

providerとvalueとconstantは、名前からなんとなく使い分けが判るんだけど、
serviceとfactoryの違いって何よ?って結構悩んだ。
簡単な実装例だと、「結局どっちも書き方が違うだけで同じ事してるやん」って思ってしまって…。

悩んだ時はソース嫁、という事で読んで見ました。
実装はこのへん。

factoryは、渡したfunctionをfunctionのままサービスに登録する。
serviceは、渡したfunctionをコンストラクタとしてインスタンスを生成し、そのインスタンスをサービスに登録する。

という違いがある様子。

それで何が変わるのか?というと。

serviceはインスタンスにしてから登録する。
JavaScriptのオブジェクトにはprivateが無いので、serviceで登録したコンストラクタで定義したメソッドは、何処からでも参照する事が出来てしまう。

一方、factoryはfunctionをそのまま登録する。
そして、factoryで登録したサービスから見えるのは、function内でreturnしたオブジェクトだけ。
なので、以下のように書いておくと、privateのような事が出来る。
angular.module('sampleServices', []).
    factory('nyanService', function() {
        var privateValue = 0;
        var privateMethod = function() {
            privateValue++;
        };
        return {
            publicMethod: function() {
                privateMethod();
                return privateValue();
            }
        };
    });
テストはこんな感じのが通る。
  describe('factory tests', function() {
      it('publicMethodがサービス経由で見えること', function() {
          expect(nyanService.publicMethod).toBeDefined();
      });
      it('privateMethodがサービス経由で見えないこと', function() {
          expect(nyanService.privateMethod).toBeUndefined();
      });
      it('privateValueがサービス経由で見えないこと', function() {
          expect(nyanService.privateValue).toBeUndefined();
      });
      it('1回実行すると1が帰ってくること', function() {
          expect(nyanService.publicMethod()).toBe(1);
      });
      it('2回実行すると2が帰ってくること', function() {
          nyanService.publicMethod();
          expect(nyanService.publicMethod()).toBe(2);
      });
  });
なので、使い分けとしては
  • service: ある程度まとまった関数群をまとめたUtilオブジェクト向け
  • factory: ビジネスロジックをしっかり書いたりするようなモデル向け
という感じでいいんじゃないかと。

Yeomanが作ってくれるfactoryのコードにも
return{}の前に"Public API here"ってコメント書いてあるし、この使い分けで間違いない…かな?