無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

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

JavaScriptで動くDIコンテナを作ってみた。

JavaScriptで動くDIコンテナを作ってみました。(→計画)

ダウンロード

container.js(コンテナ本体) - ver 0.1.0
テストケースの実行結果はこちら。

    • Yahoo Test Utilityを使っています。
    • 動作はIE6とFireFox2で確認しています。

使い方

基本的な使い方
  1. コンテナを作成します。
  2. container.Container#get(name)で、コンテナからコンポーネントを取得します。
var stdout = document.getElementById( "stdout" );

// クラス
function Kitten() {}
Kitten.prototype = {
   getName: function () { return this.name; }
};

// コンテナを作成。
// 引数でコンポーネントを定義する関数を渡す。
var c = new container.Container( function( binder ) {
  // Kittenクラスを"mii"に関連付け。
  // また、コンポーネントの"name"プロパティに"mii"をインジェクション
  binder.bind( "mii" ).to( Kitten ).inject( { "name": "mii" } ); 
});

// コンテナからコンポーネントを取得する。
var mii = c.get( "mii" );
stdout.innerHTML += mii.getName();

確認はこちらから

コンポーネントの登録

「クラス指定」、「オブジェクト指定」、「プロバイダ指定」の3つがあります。

var c = new container.Container( function( binder ) {
  // クラス指定
  binder.bind( "mii" ).to( Kitten ).inject( { "name": "mii" } );
  
  // オブジェクト指定
  binder.bind( "tora" ).toInstance( new Kitten( "tora" ) );
  
  // プロバイダ指定
  binder.bind( "shiro" ).toProvider( function( container ){
      return new Kitten( "shiro" );
  } );
});
コンポーネントのスコープ

「シングルトン」と「プロトタイプ」をサポートします。デフォルトは「シングルトン」です。

var c = new container.Container( function( binder ) {
  
  // シングルトン
  binder.bind( "mii" ).to( Kitten ).inject( { 
    "name": "mii" 
  }).scope( container.Scope.Singleton );
  
  // プロトタイプ
  binder.bind( "tora" ).to( Kitten ).inject( { 
    "name": "tora" 
  }).scope( container.Scope.Prototype );
});
プロパティインジェクション

プロパティインジェクション(フィールドに値を直接設定)のみサポートされます。

  • ハッシュで指定された値がそのままインジェクションされます。
  • コンテナに登録されたコンポーネントをインジェクションするには、container.component(name) または container.components(name) を使用します。
    • container.component(name)の場合、container.Container#get(name)した値がインジェクションされます。
    • container.components(name)の場合、container.Container#gets(name)した値がインジェクションされます。
var c = new container.Container( function( binder ) {
  
  // プロパティインジェクションの設定
  binder.bind( "mii" ).to( Kitten ).inject( { 
    // ハッシュの値がそのままインジェクションされる。
    "name": "mii",
    "getAge": function() { return this.age; } ,
    
    // 定義済みコンポーネントのインジェクション指定
    "kitten" : container.component("kitten"),
    "kittens" : container.components("kittens")
  });
  
  binder.bind( "kitten" ).to( Kitten ).inject( { "name":"tora"});
  binder.bind( "kittens" ).to( Kitten ).inject( { "name":"kuro"});
  binder.bind( "kittens" ).to( Kitten ).inject( { "name":"shiro"});
});
初期化関数・破棄関数の実行

コンポーネントを作成時に呼ばれる関数(初期化関数)と破棄時に呼ばれる関数(破棄関数)を登録できます。

  • 初期化関数は、コンポーネントが作成され、プロパティインジェクションされた後、インターセプタの設定前に実行されます。
  • 破棄関数はcontainer.Container#destroy()を実行した際に以下の条件を満たす場合、実行されます。
  • 初期化関数・破棄関数には、引数としてコンポーネントとコンテナが渡されます。
var c = new container.Container( function( binder ) {
  
  // 初期化関数・破棄関数の設定
  binder.bind( "mii" ).to( Kitten ).inject( { 
    "name": "mii"
  }).initialize( function( obj, container ) {
      // コンポーネントを初期化する処理
  }).destroy( function( obj, container ) {
      // コンポーネントを破棄する処理
  });
});
インターセプタの適用

コンポーネントのメソッドにインターセプタを設定できます。

  • インターセプタは関数で指定します。
  • インターセプタを適用する関数はcontainer.Matcherで指定します。
  • インタセプタは複数設定できます。
var c = new container.Container( function( binder ) {
  
  var builder = binder.bind( "mii" ).to( Kitten ).inject( { "name": "mii" });
  
  // インターセプタの適用
  builder.intercept( 
    function( methodInvocation ) {
        methodInvocation.getMethodName(); // 実行されたメソッド名を取得
        methodInvocation.getThis(); // コンポーネントを取得
        methodInvocation.getArguments()[0] = "foo"; // 引数を取得&変更
        
        // 元のメソッドを実行
        return methodInvocation.proceed();
    }, 
    // インターセプタを適用する関数をcontainer.Matcherで指定する。
    new container.Matcher( /get.*/, /set.*/ ) );
  
  // インターセプタは複数回設定できる。
  builder.intercept( 
    function( methodInvocation ) { return methodInvocation.proceed(); }, 
    // すべてにマッチするcontainer.Matcherを作成するユーティリティ
    container.any()); 
});
アノテーション

コンポーネントの設定はアノテーション(もどき)で行うことも可能です。

  • アノテーション指定とAPI指定の両方がある場合、API指定での設定が有効となります。
  • ただし、インターセプタの設定に関しては、「追加」の動作となります。
// クラス
function Kitten() {}
Kitten.prototype = {
   getName: function () { return this.name; }
};
Kitten.prototype.meta ={
    
    // アノテーション(もどき)でのコンポーネント設定
    "@Container": {
        // インジェクション
        "@Inject":{
            "name" : "foo"
        },
       //初期化関数・破棄関数
        "@Initialize": function( obj, container ) {},
        "@Destroy": function( obj, container ) {},
        // スコープ
        "@Scope": container.Scope.Singleton,
        // インターセプタ
        "@Intercept": [
            [ function( mi ){ return mi.proceed(); }, container.any() ],
            [ function( mi ){ return mi.proceed(); }, new container.Matcher( /get.*/ ) ]
        ]
    }
}
getとgets

コンテナには同じ名前のコンポーネントを複数登録することができます。

  • get()を使用した場合、最初に登録した定義を元に作成されたコンポーネントが返されます。
  • gets()の場合、すべての定義を元にコンポーネントが作成され、その配列が返されます。
// コンテナを作成。
var c = new container.Container( function( binder ) {
  // 同じ名前で複数のコンポーネントを登録
  binder.bind( "kittens" ).to( Kitten ).inject( { "name":"mii"});
  binder.bind( "kittens" ).to( Kitten ).inject( { "name":"kuro"});
  binder.bind( "kittens" ).to( Kitten ).inject( { "name":"shiro"});
});

// gets()でコンテナからすべてのコンポーネントを取得する。
var kittens = c.gets( "kittens" ); 
stdout.innerHTML += kittens[0].getName() + "<br/>"; // "mii"
stdout.innerHTML += kittens[1].getName() + "<br/>"; // "kuro"
stdout.innerHTML += kittens[2].getName() + "<br/>"; // "shiro"

// get()の場合、最初に登録したコンポーネントが返される。
var kitten = c.get( "kittens" ); 
stdout.innerHTML += kitten.getName(); // "mii"
残り

以下の機能についてはそのうち書きます。