Angular#serviceと#factoryの違い。どういう時どっちを利用するか。定義方法。使い方を理解するまとめ(2015/8/29更新)

  • 78
    Like
  • 0
    Comment

Angular#serviceとfactoryの違い。どういう時どっちを利用するか。定義方法。使い方を理解する

serviceとfactoryの定義、違いと利用方法、使い方、がいまいちわからないわたしはあらゆるリファレンスを元に理解しようと努めました。その様子です。

※Angularのサービスはいろいろありますが、今回はserviceとfactoryです。
※文中いろいろな言い方で同じようなことを言っている箇所があります。理解したい一心ですのでご了承ください。

factory,service共通

  • アプリケーション内にインスタンスは一つしか生成されず、使い回される(サービス共通。value,constant等も)。シングルトン。
  • ビジネスロジックを定義する

factory

構文: angular.Module.factory(name:string,getFn)
  • 共有したい関数、値、オブジェクトを「返却」する。リターンしたそれらがそのまま利用される
  • 引数を取った関数を記載できる
  • プリミティブ型を扱いたい時、またはnewでインスタンスを生成したくない時使う
  • インスタンスを返す関数を指定する
  • 既にサービスとして提供したい機能がクラスとして用意されている場合
  • 特定のメソッドの戻り値をサービスとして利用したい場合
//ex1 //値返す書き方
var myApp = angular.module('MyApp',[]);
myApp.factory('myUrlFactory',function myUrlFunc(){
 return 'https://github.com/kenjimorita/'; 
})

//ex2 //関数を返す書き方
var myApp = angular.module('MyApp',[]);
myApp.factory('myFunc',function($window){
 return{
   get : function(text){
     $window.text;
     alert($window.text);
   }
 }
})

//ex3 //インスタンスに登録していき最後返却する書き方
var myApp = angular.module('MyApp',[]);
myApp.factory('factoryService',function(){
 var moritaService = {}; //moritaServiceインスタンスを生成
 moritaService.message = "This is kenjiService";//プロパティ登録
 moritaService.value = {//オブジェクト登録
  value  : 111,
  value2 : 222 
 };
 moritaService.add = function(a,b){//メソッド登録
  retunr a + b;
 }
 return moritaService; //ホストオブジェクトを返却//このserviceを利用する側はmoritaServiceをそのまま利用する
})

//ex4 //DIできるfactory。他のサービスを利用しながら定義
var myApp = angular.module('MyApp',[]); 
myApp.value('myURL','https://github.com/kenjimorita/');//アプリケーション全体の定数管理(サーバーサイドのURLなど)今回使わない
myApp.constant('apiUrl','/api/products.json');
myApp.constant('apiKey','faea13vata42gae5kk6eeeg75645nkiji');
//$resourceをラップしたサービスを定義
myApp.factory('myApiFactory',[$resource,apiUrl,apiKey,
function($resurce,apiUrl,apiKey){//配列で最後定義
 return $resource(apiUrl).query({api_key : apiKey});
}]);
//↓myApiFactoryを利用する側
angular.module('myApp').controller('moritaController',
['$scope','myApiFactory',
 function($scope,myApiFactory){
  $scope.apiFactory = myApiFactory;
 }]
)

//ex5 既にどこかで用意されているHogeClass
.factory('MyService',function(){
 return new HogeClass();
})

//ex6 サービスオブジェクトを返す
.factory('MyService',function(){
 return FooClass.GetInstance(data);
})

service

構文: angular.Module.service(name,constructor);
  • アプリケーション内にインスタンスは一つしか生成されず、使い回される(factory,valueも全てのサービス共通)
  • 共有したいオブジェクトの「コンストラクタ」を「登録」する
  • 必ずnewインスタンスが作成されるためfactoryのようにプリミティブ型を提供できない
  • 登録されたコンストラクタはサービス利用側でnew、インスタンスが生成されるが、 利用する度に生成されるわけではなく、「インスタンスを生成したサービス」が複数サービスや コントローラで共有される。 インジェクトする度にインスタンスが生成されるわけではない(※ex2)
  • ロジックがcontrollerに記述されている時にserviceに切り出す(Modelにメソッドとして実装するのも、Controllerに実装するのも適切ではない処理)。factoryで書いていたけど、初期化処理したくなった場合も。値だけの時はvalue,関数はfactory,javascriptクラスはservice。

//ex1
var myApp = angular.module('myApp',[]);
myApp.service('myService',function(){
 this.value = 'myService!!';
 this.valueObj = {
  value1 : 333,
  value2 : 555 
 };
 this.get = function(a,b){
  return a + b;
 };
});
//ex2 factoryとserviceの比較
var myApp = angular.module('myApp',[]);
var myService = function(){//下記serviceとfactoryで利用するためのサービスの定義
 this.message = 'This is my service';
 this.add = function(a, b){
  return a + b;
 };
};
//下記は上記で定義したmyServiceを利用する例。同じ意味。
myApp.service('MyserviceByService',myService);//コンストラクタ関数の登録。利用する側でnew呼び出し、共有される。newは一回、初期化処理されたらそれを共有する
myApp.factory('MyserviceByFactory',function(){//インスタンスを呼び出して渡す
 return new myService();
})


//ex3 
//3つのメソッドを登録するservice
.service('triangle',['$log',function($log){//配列アノテーションで他のサービスを注入できる
    //triangleメソッド
    this.triangle = function(base, height){//javascript同様にconstructor関数内でthis.[method]=~形式で定義できる
        $log.info('[triangle]底辺:' + base);
        $log.info('[triangle]高さ:' + height);
        return base * height / 2;
    };
    //circleメソッド
    this.circle = function(radius){
        $log.info('[circle]半径:' + radius);
        return radius * radius * Math.PI;
    }
    //trapezoid メソッド
    this.trapezoid = function(upper, lower, height){

        $log.info('[trapezoid]上辺:' + upper);
        $log.info('[trapezoid]下辺:' + lower);
        $log.info('[trapezoid]高さ:' + height);
        return (upper + lower) * height / 2;
    };
}])
//つまりこの構文の形は。。
.service('triangle',[,,function(
  A
){
 B
}]);
//TypeScriptでいうところのこういう形
class triangle {
 constructor(A)
 {
  B //this.triangle = ()=>{}
   ...
 }
}


//ex3をAngularで利用する
//html
<ul>
    <li>三角形 (底辺4×3) : {{triangle}}</li>
    <li>(半径5) : {{circle}}</li>
    <li>台形 (上辺5 /下辺10 × 3) : {{trapezoid}}</li>
</ul>

//Angular
angular.module('myApp',[])
.service('FigureService', ['$log',function($log){
    ...中略...
])
.controller('Mycontroller',['$scope','FigureService',
function($scope,FigureService){
    $scope.triagle = FigureService.triangle(4,3);
    $scope.circle = FigureService.circle(5);
    $scope.trapezoid = FigureService.trapezoid(5,10,3);
}])

TypescriptでClassとしてservceを登録


class MyService{
 constructor(
 private $resource:ng.resource.IResourceService,//returnしないものはprivete
 private apiUrl:String
 ){}
 get (){
  return this.$resource(this.apiUrl).get();
 }
}
angular.module('MyApp',[]).service('MyService',MyService);//ここでnewされる。ただし1回のみで、このサービスをDIして使い回す。controller内にserviceをDIしすぎに注意

いかがでしたか?(LIG風)
serviceとfactoryの違い、利用方法を理解しましたか?
わたし。

おいしく頂きましたごちそうさまでした
Revealing Module Patternによるオブジェクトの作り方
お前のAngular.jsはもうMVCではない。と言われないためのTutorial
AngularJS スタイルガイド
夕日本
AngularJSのTutorialのstep-11よりServiceの使い方
AngularJS アプリケーションプログラミング
AngularJSアンチパターン集
プレゼンテーションの分離
業務Webアプリの作り方の基礎(後編)
えぇー!MVCのContollerはプレゼンテーションロジックのinput担当だったのかい!?