原版地址:http://code.angularjs.org/1.0.2/docs/guide/di
一、Dependency Injection(依賴注入)
依賴注入(DI)是一個軟件設計模式,處理代碼如何得到它所依賴的資源。
關於DI更深層次的討論,可以參觀Dependency Injection(http://en.wikipedia.org/wiki/Dependency_injection),Inversion of Control(http://martinfowler.com/articles/injection.html),也可以參觀軟件設計模式的書。
1. DI in a nutshell(簡說DI)
object或者function,只能夠通過以下三種方式獲取他們依賴的資源:
1) 可以通過new運算符創建依賴的資源。
2) 可以通過全局變量查找依賴的資源。
3) 可以通過參數傳入依賴的資源。
1、2兩種方式,並不是最佳的,因為它們對依賴關系進行hard code,這使得修改依賴關系時,不是不可能,但會變得比較復雜。這對於測試來說尤其是個問題,通常在獨立測試時,希望能夠提供模擬的依賴資源。
第3種方法相對來說最可行,因為它去除了從組件(component)中定位依賴的責任。依賴僅僅交給組件就可以了。
function SomeClass(greeter) { this.greeter = greeter } SomeClass.prototype.doSomething = function(name) { this.greeter.greet(name); }
這樣是比較合適的,但它將獲取依賴資源的責任交給了負責構建SomeClass的代碼那里。
為了管理創建依賴的責任,每一個angular應用都有一個injector(http://code.angularjs.org/1.0.2/docs/api/angular.injector)。injector是一個服務定位器,負責定位並創建依賴的資源。
請求依賴,解決了hard code的問題,但它意味着injector需要貫穿整個應用。傳遞injector,會破壞Law of Demeter(http://baike.baidu.com/view/823220.htm)。為了糾正這個問題,我們將依賴查找的責任轉給injector。
上面說了那么多,看看下面經我修改過的例子,合並了原文的兩個例子,分別在angular內、外使用inject:
<!DOCTYPE HTML> <html lang="zh-cn" ng-app="MainApp"> <head> <meta charset="UTF-8"> <title>injector</title> </head> <body> <div ng-controller="MyController"> <button ng-click="sayHello()">Say Hello</button> </div> <script src="../angular-1.0.1.js" type="text/javascript"></script> <script type="text/javascript"> //創建OtherModule這個module,相當於外部的module var otherModule = angular.module("OtherModule", []); //教injector如何創建"greeter" //注意,greeter本身需要依賴$window otherModule.factory("greeter", function ($window) { //這里是一個工廠方法,負責創建greet服務 return { greet:function (text) { $window.alert(text); } }; }); //下面展示在非當前module中,通過injector調用greet方法: //從module中創建新的injector //這個步驟通常由angular啟動時自動完成。 //必須引入'ng',angular的東東 //故意顛倒順序,暫時證實這玩意的順序是無所謂的。。 var injector = angular.injector(['OtherModule','ng']); //請求greeter這個依賴。 var g = injector.get("greeter"); //直接調用它~ g.greet("Hi~My Little Dada~"); //這里是當前的主app,需要依賴OtherModule var mainApp = angular.module("MainApp", ["OtherModule"]); //留意Controller的定義函數的參數,在這里直接注入$scope、greeter。 // greeter服務是在OtherModule中的 mainApp.controller("MyController",function MyController($scope,greeter) { $scope.sayHello = function() { greeter.greet("Hello Kitty~~"); }; } ); //ng-controller已經在背后默默地做了這個事情 //injector.instantiate(MyController); </script> </body> </html>
注意,因為有ng-controller,初始化了MyController,它可以滿足MyController的所有依賴需要,讓MyController無須知道injector的存在。這是一個最好的結果。應用代碼簡單地請求它所需要的依賴而不需要處理injector。這樣設置,不會打破Law of Demeter。
二、Dependency Annotation(依賴注釋,說明依賴的方式)
injector如何知道什么服務需要被注入呢?
應用開發者需要提供被injector用作解決依賴關系的注釋信息。所有angular已有的API函數,都引用了injector,每一個文檔中提及的API都是這樣。下面是用服務名稱信息注釋我們的代碼的三個等同的方法。
1. Inferring Dependencies(隱含依賴)
這是獲取依賴資源的最簡單的方式,但需要假定function的參數名稱與依賴資源的名稱一致。
function MyController($scope, greeter) { ... }
雖然這樣做很簡單,但這方法在javascript混淆壓縮后就行不通了,因為參數名稱會被改變。這讓這個方式只能對pretotyping(產品可用性原型模擬測試法,http://www.pretotyping.org/,http://tech.qq.com/a/20120217/000320.htm)和demo應用有作用。
2. $inject Annotation($inject注釋)
為了允許腳本壓縮器重命名函數的方法后,仍然能夠注入正確的服務,函數必須通過$inject屬性來注釋依賴。$inject屬性是一個需要注入的服務的名稱的數組。
var MyController = function(renamed$scope, renamedGreeter) { ... } //這里依賴的東東,如果不在當前的module中,它還是不認識的。
//需要在當前module中先依賴對應的module。跟之前的例子差不多。但我不知道這是不是正確的方法。 MyController.$inject = ['$scope', 'greeter'];
這個注釋方法,對於controller聲明來說是有用的,因為它與函數一起指定注釋信息。
3. inline Annotation(行內注釋)
有時候,不方便使用$inject注釋的方式,例如注釋directive的時候。
例如:
someModule.factory('greeter', function($window) { ...; });
var greeterFactory = function(renamed$window) { ...; }; greeterFactory.$inject = ['$window']; someModule.factory('greeter', greeterFactory);
由於這樣(代碼膨脹),angular還提供了第三種注釋風格:
someModule.factory('greeter', ['$window', function(renamed$window) { ...; }]);
三、Where can I user DI?
DI遍及整個angular。它通常使用在controller和factory方法中。
1. DI in controllers
controller是負責(描述)應用行為的類。建議的controller聲明方法是:
var MyController = function(dep1, dep2) { ... } MyController.$inject = ['dep1', 'dep2']; MyController.prototype.aMethod = function() { ... }
factory方法是負責創建大多數angular對象。例如directive、service、filter。factory方法注冊在module中,建議的factory聲明方法是:
angualar.module('myModule', []). config(['depProvider', function(depProvider){ ... }]). factory('serviceId', ['depService', function(depService) { ... }]). directive('directiveName', ['depService', function(depService) { ... }]). filter('filterName', ['depService', function(depService) { ... }]). run(['depService', function(depService) { ... }]);
By Lcllao.