バインドメソッド
JavaScriptで動くDIコンテナの解説その2。
バインドメソッドは、登録済みコンポーネントの任意の関数をコンポーネントとして登録する機能です。
といった使い方をイメージしています。UI層とそれ以外の層の関連づけをコンテナで行うことで、レイヤー間の結合を疎にできます。
使用方法
バインドメソッドはtoFunction()を使用して登録します。
- 引数で関数の所有者であるコンポーネント名と関数名を指定します。
- コンテナからコンポーネントを取得するのと同じ方法で関数を取得し、実行することができます。
- 関数には、それの所有者であるコンポーネントが封入されているので、内部でthisが使えます。
- 引数もOK。
- スコープやインジェクションの指定を行うことはできません。(toFunction()は戻り値を返しません。)
// テスト用のクラス。 function Kitten( ) { this.name = name; } Kitten.prototype = { getName: function() { return this.name; }, setName: function(name) { this.name = name; } } var c = new container.Container( function( binder ) { // コンポーネントを登録 binder.bind("mii").to( Kitten ).inject( { name: "mii" } ); // バインドメソッドを登録 binder.bind( "get" ).toFunction( "mii", "getName" ); binder.bind( "set" ).toFunction( "mii", "setName" ); }); var mii = c.get("mii"); stdout.innerHTML += mii.getName() + "</br>"; // nii // バインドメソッドの取得と実行。 var get = c.get("get"); var set = c.get("set"); stdout.innerHTML += get() + "</br>"; // mii set("tora"); // 引数も使える。 stdout.innerHTML += get() + "</br>"; // tora // コンポーネント mii のAPIが呼び出されているので、miiの名前が更新されている stdout.innerHTML += mii.getName() + "</br>"; // tora
具体例
実用的な例としてモデルの更新リスナをバインドメソッドで登録してみます。
// モデル兼コントローラ function Kitten( ) { this.state = "stop"; this.listeners = []; } Kitten.prototype = { run: function() { this.setState("running"); }, stop: function() { this.setState("stop"); }, jump: function() { this.setState("jump!"); }, // 状態を変更する。 setState: function( state ) { this.state = state; // 変更したらリスナに通知。 for ( var i = 0; i < this.listeners.length; i++ ) { this.listeners[i].call( null, state ); } } } // 猫の状態表示UI function KittenStateView( elmId ) { this.elmId = elmId; } KittenStateView.prototype = { // 状態が変わったらUIを更新する。 onKittenStateChanged : function( newState ) { document.getElementById( this.elmId ).innerHTML = newState; } } // コンテナ var c = new container.Container( function( binder ) { // 猫 binder.bind("mii").to( Kitten ).inject( { // リスナとして"onKittenStateChanged"の名を持つコンポーネントを設定。 "listeners": container.components( "onKittenStateChanged" ) } ); // コンポーネントを登録 binder.bind("view1").to( KittenStateView ).inject( { elmId: "view1" } ); binder.bind("view2").to( KittenStateView ).inject( { elmId: "view2" } ); binder.bind("view3").to( KittenStateView ).inject( { elmId: "view3" } ); // バインドメソッドを登録 binder.bind( "onKittenStateChanged" ).toFunction( "view1", "onKittenStateChanged" ); binder.bind( "onKittenStateChanged" ).toFunction( "view2", "onKittenStateChanged" ); binder.bind( "onKittenStateChanged" ).toFunction( "view3", "onKittenStateChanged" ); }); var mii = c.get("mii"); // これのAPIを呼び出すと 状態の変更→UIの更新が行われる。