読者です 読者をやめる 読者になる 読者になる
無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

依存関係の循環エラー抑制

JavaScript ContainerJS

ContainerJSでは、コンポーネントの依存関係が循環している場合でも単純な循環であればエラーとせずに依存関係を解決できたりします。たとえば以下のようなパターンであれば問題なく解決可能です。

function main() {
  // 循環参照するコンポーネントも以下のパターンであれば問題なく依存関係を解決できる。
  var c = new container.Container( function( binder ) {
    // コンポーネント "mii"
    binder.bind( Kitten ).to( "mii" ).inject({
      // "tora" をインジェクト
      friend : container.component( "tora" ),
      name : "mii"
    });

    // コンポーネント "tora"
    binder.bind( Kitten ).to( "tora" ).inject({
     // "tora" をインジェクト
      friend : container.component( "mii" ),
      name : "tora"
    });
  });
  var out = document.getElementById("out");
  out.innerHTML += c.get("mii").toString() + "<br/>"; 
  out.innerHTML += c.get("tora").toString() + "<br/>";  
}
function Kitten() {
  this.friend = null;
  this.name = null;
}
Kitten.prototype = {
    toString : function() { 
      return "name=" + this.name + " friend=" + this.friend.name;
    }
}

実行結果はこちら

内部的には、「依存モジュールのインジェクションを行う前に、コンポーネントを作成しキャシュとして登録しておくことで循環エラーを回避する」ようになっていて、↑の場合であれば、

  1. "mii"として登録されたコンストラクタが呼び出され、インスタンスが作成される。
  2. "mii"はシングルトンなので、インスタンスがキャッシュに登録される。
  3. "mii"のinject()で指定されたコンポーネントが注入される。
    1. friendとして"tora"コンポーネントに依存しているので、"tora"がコンテナからgetされる。
    2. "mii"と同様にインスタンスが作成され、キャシュに登録される。
    3. "tora"のinject()で指定されたコンポーネントが注入される。"mii"はすでにキャッシュが登録されているのでキャッシュの値が注入される

という流れで、最終的に期待されたコンポーネントを保持するインスタンスが作成されます。

ただし、この方式だと以下のような問題はあります。

  • コンストラクタインジェクションはできない。
    • 依存関係の解決なしにインスタンスを生成できる必要があるため。
  • 初期化関数呼び出しのタイミンクでは、インジェクションされたコンポーネントの依存関係が解決されていない場合がある。
    • たとえば以下のような場合は、"tora"のinit()のタイミングでは"mii"のインジェクションは完了しておらず、"mii"のnameはnullのままです。
function main() {
  var c = new container.Container( function( binder ) {
    
    // コンポーネント "mii"
    binder.bind( Kitten ).to( "mii" ).inject({
      // "tora" をインジェクト
      friend : container.component( "tora" ),
      name : "mii"
    }).initialize("init"); // 初期化メソッドとして"init"を実行
    
    // コンポーネント "tora"
    binder.bind( Kitten ).to( "tora" ).inject({
     // "tora" をインジェクト
      friend : container.component( "mii" ),
      name : "tora"
    }).initialize("init"); // 初期化メソッドとして"init"を実行
    
  });
  var out = document.getElementById("out");
  c.get("mii");
}
// テスト用クラス
function Kitten() {
  this.friend = null;
  this.name = null;
}
Kitten.prototype = {
  init : function() {
    //初期化メソッドとして呼び出される関数。
    //循環参照がある場合、依存コンポーネントのインジェクションは完了していない場合がある。
    var out = document.getElementById("out");
    out.innerHTML += this.toString() + "<br/>"; 
  },
  toString : function() { 
    return "name=" + this.name + " friend=" + this.friend.name;
  }
}

実行結果はこちら

ということでご注意。今日は2つめの問題にはまって、30分ほど悩んだよ・・・。orz。