を纏める時に、AngularJSのDeveloper Guideを読んだわけですが、AngularJSがどのようにDIを実現しているか、についても書かれています。興味あったんで纏めてみます(というか訳してみます、に近いか・・・)。
コンポーネント(objectやfunction)が依存するブツを得る方法は3つしかない。
- コンポーネントは依存するブツを生成することができ、一般的にはnew演算子を使う。
- コンポーネントはグローバル変数から依存するブツを探すことができる。
- コンポーネントはそれが必要となる場所で渡される。
最初の生成する、とか、探すっていう2つの選択肢は、コンポーネントに依存するブツをハードコードしなきゃいけなくなるので、最適とはいえない。依存するブツを変更することが面倒になってしまう。これは特にテスト用に分離したブツのモックを提供したいといった場面で問題になる。
3番目の選択肢は、コンポーネントから依存するブツの場所を知る責務を取り除くのでもっともよさそう。依存するブツは単純にそのコンポーネントに渡されるだけ。
function SomeClass(greeter) {
this.greeter = greeter;
}
SomeClass.prototype.doSomething = function(name) {
this.greeter.greet(name);
};
上の例のSomeClassはgreeterの生成や場所の特定などには関係していなくて、インスタンスが作成される際に、単純にgreeterを手渡されるだけ。 それはいいんだけど、SomeClassを生成するコード上に依存するブツを得るっていう責務を生んでる。
依存するブツを生成するっていう責務を管理するために、angularjsアプリケーションは”injector”を持ってる。injectorは、依存するブツの生成や検索を責務とするサービスロケータである。
injectorサービスを使った例を挙げる。
var myModule = angular.module('myModule', []);
injectorにどうやってgreeterサービスを組み立てるか教えてあげる。greeterは$windowサービスに依存していることに注目しよう。greeterサービスはgreetメソッドを持つオブジェクト。
myModule.factory('greeter', function($window) {
return {
greet: function(text) {
$window.alert(text);
}
};
});
で、myModuleに定義されたコンポーネントを提供することができる新しいinjectorを生成して、 そいつにgreeterサービスをリクエストする。(これは通常angular bootstrapで自動的に行われる。)
var injector = angular.injector(['myModule', 'ng']);
var greeter = injector.get('greeter');
ハードコードの問題の解決を望んだら、今度はinjectorがアプリケーション全体を通じて存在する必要性が出ちゃった。injectorを渡すことが「デメテルの法則」を破ってしまう。これを救済するために、下の例のように、コンポーネントを生成する責務をinjectorに移譲するために、HTMLテンプレート上で宣言的な記法を使っている。
function MyController($scope, greeter) {
$scope.sayHello = function() {
greeter.greet('Hello World');
};
}
AngularはHTMLをコンパイルする時、injectorに対してコントローラとそれが依存するブツのインスタンスを次々に生成するよう要求しながらng-controllerディレクティブを処理する。
injector.instantiate(MyController);
これは全て舞台裏で完了する。ng-controllerを使うってことは、これまでにinjectorを知っているコントローラなしで、injectorにそのクラスのインスタンスを生成するよう要求し、MyControllerの全ての依存性を満たすってことに注意しよう。
これがベストな結果。アプリケーションコードはinjectorを使ってどうこうするとかなしに、単純に必要な依存性を宣言するだけ。この仕組なら「デメテルの法則」を破らないですむ。
というようなことが書いてあって、訳しかたがうまくなくて、はっきりと理解できたわけではないが、とりあえずメモとして残しておきます。
0 件のコメント:
コメントを投稿