愛流浪的小風

技術隨手寫

[AngularJs]淺談Angular.js的Provider機制

| Comments

Angular.js的核心是由DI(Dependency Injection)所構成,在開發時期我們透過Angular.js所提供的Service、Factory等Provider,將可共用的程式碼包裝註冊到模組中,最後再透過Angular.js注入(Inject)到我們所想要使用的地方。而Service、Factory等功能其實都是Angular.js中$provide服務所提供的方法之一,讓我們可以將常用、可重複使用的功能包裝成元件,但Service、Factory這幾種Provider有什麼不同呢?今天的文章就要向大家介紹其中的差異之處。

$provide所提供的幾種Provider

Constant

Contant代表著常數,從註冊之後就不會再被改變,也沒辦法透過decorator等機制攔截客制化,它可以在任何的地方被註冊使用。Constant的值可以是數字、文字或是物件,我們可以直接透過angular.constant來註冊常數。

angualr.module('app')
    .constant('version', 1);

畫面上的結果為 1

範例程式碼

通常Constant會使用在設定檔,或是不會變的常數上面。

Value

Value其實和Constant非常類似,它可以是任何種類的數字、文字或是物件,甚至可以是一個function,但它和Constant最主要的差別是他可以透過decorator等機制去攔截,在實際使用前做客制化的調整。

angular.module('app')
    .value('version', 1);

畫面上的結果為 1

範例程式碼

透過Decorator做攔截value,並進行客制化

angular.module('app')
    .config(function ($provide) {
        $provide.decorator('version', function ($delegate) {
        return 'Version - ' + $delegate;
    });
});

畫面上的結果為 Version - 1

範例程式碼

Value通常會使用來作為App的初始值、預設值等用途。

※ Constant和Value有一個重大差異是,Constant可以被注入到Config區段,Value不行

Service

Service通常會用在註冊包含某些功能的Function,Angular.js在注入時會幫我們將Service進行初始化的動作(有點像我們寫C#時會先定義好Class,然後DI在注入時會幫我們new class的實體),在Angular.js中,Service都是Singleton,所以我們可以透過Service來跨Controller分享資料。

angular.module('app')
    .service('myService', function () {
        var self = this;
        self.getName = function () {
            return 'John';
        };
    });

angular.module('app')
    .controller('MainCtrl', function ($scope, myService) {
    //// Angular會初始化方法,並回傳給我們

    //// 類似 new myService()

        $scope.name = myService.getName();
    });

畫面上的結果為 John

範例程式碼

我們可以使用Service將$http取資料的程式包裝,或是封裝常用的商業邏輯為Service。

Factory

與Service不同的是,Factory類似Factory Pattern,我們註冊的不會是一個功能的Function,而是一個如何產生物件的方法,與在註冊為Service時不同,Angular.js會幫我們執行這個產生物件的Function,然後回傳這個物件。

angular.module('app')
    .factory('myFactory', function () {
        return {
            name: 'John'
        }
    });

angular.module('app')
    .controller('MainCtrl', function ($scope, myFactory) {
    //// Angular會執行工廠方法,並回傳物件

    //// 類似 myFactory()

        $scope.name = myFactory.name;
    });

畫面上的結果為 John

範例程式碼

Factory可以用來產生共用的Utility,或是存放一些跨區塊使用的物件。

Provider

Provider是最複雜的一種,它是所有Provider功能的基底,像Factory、Service其實都是Provider包裝後提供的一種語法糖,讓我們可以快速實現並註冊元件。Provider提供了彈性的設定方法,也讓我們可以用類似Factory的方法將物件的初始化包裝起來。

假設我們希望在頁面上顯示version,但又希望在不同的頁面可以顯示不同的樣式,我們就可以使用Provider的方法來實作,如果不做任何調整的話就是顯示預設值。

angular.module('app')
    .constant('version', 1);

angular.module('app')
    .provider('versionTitle', function () {
        var self = this;
            
    //// Provider提供可彈性調整的方法,透過angular.config呼叫並修改prefix

        self.versionPrefix = '';
        self.changeVersionPrefix = function (prefix) {
            self.versionPrefix = prefix;
        }
        
    //// Provider實際產生物件的方法,回傳的實體透過此方法產生

    //// 類似Factory,使用$get()取得物件

        self.$get = ["version", function (version) {
            return self.versionPrefix + version;
        }];
});

畫面上的結果為 1

範例程式碼

如果我們希望替version加上不同prefix的話,只需要額外加上config,並呼叫changeVersionPrefix方法修改prefix即可。

angular.module('app')
    .config(function (versionTitleProvider) {
        versionTitleProvider.changeVersionPrefix('VersionTitle - ');
});

畫面上的結果為 VersionTitle - 1

範例程式碼

Provider通常用來做可根據頁面動態調整設定的方法或物件,擁有比較高的客制化彈性。

Decorator

我們如果想要針對已經撰寫好的Provider客制化,增加想要執行的邏輯,可以透過Decorator方法來攔截並擴充,有點類似AOP

假設我們已經撰寫好一個取得姓名的Service

angular.module('app')
    .service('myService', function () {
        var self = this;
        self.getName = function () {
            return 'John';
        };
});

畫面上的結果為 John

但我們希望在特定的頁面顯示大寫的名字,但又不希望修改原本的Service,我們就可以在這頁使用Decorator的方法,將名字透過擴充的方法修改為大寫,卻不影響原本已經擁有的模組。

angular.module('app')
    .config(function ($provide) {
        $provide.decorator('myService', function ($delegate) {
         //// 將原本的物件包裝起來,並回傳大寫的內容

            return {                  
                getName: function(){
                    return $delegate.getName().toUpperCase();
                }
            };
        });
    });

畫面上的結果為 JOHN

範例程式碼

小結

使用Angular.js所提供的Provider機制,可以讓程式碼更有彈性並且容易管理,我們可以將大部分常用的邏輯都封裝為Service或Factory,真的少部分需要客制的部分透過Decorator或是Provider來彈性設定、調整,讓程式碼不會總是因為微調而一直讓程式增加,並更符合物件導向單一職責的概念,保持程式碼的乾淨與清楚!關於以上內容,若有任何問題歡迎大家一起討論喔!

※ 參考文章

Comments

comments powered by Disqus