Yan Container−Constructor Injection

Yan Containerにクラスを登録して使う(2006.2.12)

とりあえず,Yan Containerを使って簡単なプログラムを作ってみた.魚(Fish)と釣り人(Fisherman)だ.

//Fish.java
public class Fish{
  public void fish(Fisherman fisherman){
    System.out.println(this+" was fished by "+fisherman);
  }
}

//Fisherman.java
public class Fisherman{
  Fish fish;

  public Fisherman(Fish fish){
    this.fish=fish;
  }
  public void fish(){
    fish.fish(this);
  }
}

//Main.java
import jfun.yan.Container;
import jfun.yan.containers.DefaultContainer;

public class Main{
  public static void main(String[] args){
    Container yan=new DefaultContainer();     //a. コンテナのインスタンスを生成
    yan.registerConstructor(Fish.class);      //b. Fish.classをコンテナに登録
    yan.registerConstructor(Fisherman.class); //c. Fisherman.classをコンテナに登録
    Fisherman fisherman=(Fisherman)yan.getInstanceOfType(Fisherman.class); 
                                              //d. Fishermanのインスタンスを生成
    fisherman.fish();                         //e. 魚を釣る
  }
}

このプログラムをコンパイル&実行するとこんな結果になる.魚は釣れたようだ.

Fish@861f24 was fished by Fisherman@166aa18

Fish.javaとFisherman.javaはYan Containerの使用有無とは無関係なクラスで,Yan Containerを使っているのはMain.javaである.手順は以下の通り.

  1. Yan Containerのインスタンスを生成(Main.javaのa)
  2. 使用するクラスをコンテナに登録(Main.javaのbとc)
  3. 使用するクラスのインスタンスをコンテナから取出す(Main.javaのd)
  4. 上記3で取出したインスタンスのメソッドを呼ぶ(Main.javaのe)

Fishermanクラスのインスタンス生成時にはFishクラスのインスタンスが必要たが,Yan Containerが自動的に作ってくれる.

クラスに特定の値を指定(2006.2.26)

何が釣れたのかわからないので,魚の種類を付け加えることにした.FishクラスのコンストラクタにString型の引数を追加する.

//Fish.java
public class Fish{
  private String kind;

  public Fish(String kind){ //魚の種類を追加
    this.kind=kind;
  }
  public void fish(Fisherman fisherman){
    System.out.println(this+"("+kind+") was fished by "+fisherman);
  }
}

//Fisherman.java −変更なし−
public class Fisherman{
  private Fish fish;

  public Fisherman(Fish fish){
    this.fish=fish;
  }
  public void fish(){
    fish.fish(this);
  }
}

//Main.java
import jfun.yan.Container;
import jfun.yan.containers.DefaultContainer;

public class Main{
  public static void main(String[] args){
    Container yan=new DefaultContainer();
    yan.registerValue("タイ"); //a. 魚の種類をYan Containerに登録
    yan.registerConstructor(Fish.class);
    yan.registerConstructor(Fisherman.class);
    Fisherman fisherman=(Fisherman)yan.getInstanceOfType(Fisherman.class);
    fisherman.fish();
  }
}

このプログラムを実行するとこんな結果になる.タイは釣れたようだ.

Fish@19360e2(タイ) was fished by Fisherman@bdb503

Fishクラスのコンストラクタに追加したString型の引数の値をMain.javaのaで行っている.注意しないといけないのは,Container.registerValueメソッドがコンテナに登録しているのは「Fishクラスのコンストラクタの引数に指定したString型の値」ではなく「Containerに登録した全クラスが使うString型の値」であるという点である.

例えば,FishermanクラスでもString型の引数を持っていたとしよう.

//Fisherman.java
public class Fisherman{
  private String name;
  private Fish fish;

  public Fisherman(String name,Fish fish){ //コンストラクタにString型の引数(Name)を追加
    this.name=name;
    this.fish=fish;
  }
  public void fish(){
    fish.fish(this);
  }
  public String getName(){ //アクセサを定義
    return this.name;
  }
}

Fish.javaで釣上げた人の名前を出力する.

public class Fish{
  private String kind;

  public Fish(String kind){
    this.kind=kind;
  }
  public void fish(Fisherman fisherman){
    System.out.println(this+"("+kind+") was fished by "+fisherman+"("+fisherman.getName()+")"); //釣上げた人の名前を出力
  }
}

Main.javaは同じである.コンパイル&実行してみよう.

Fish@861f24(タイ) was fished by Fisherman@166aa18(タイ)

釣り人の名前もタイになってしまう.

DefaultContainer.getComponent(java.lang.Object)メソッドを使うとそのオブジェクトに指定した値を確認することができる.Main.javaを少し修正する.

//Main.java
import jfun.yan.Container;
import jfun.yan.containers.DefaultContainer;

public class Main{
  public static void main(String[] args){
    Container yan=new DefaultContainer();
    yan.registerValue("タイ");
    yan.registerConstructor(Fish.class);
    yan.registerConstructor(Fisherman.class);
    Fisherman fisherman=(Fisherman)yan.getInstanceOfType(Fisherman.class);
    fisherman.fish();
    System.out.println(yan.getComponent(String.class)); //String.classに指定した値を確認
  }
}

このプログラムを実行すると以下のようになる.

Fish@861f24(タイ) was fished by Fisherman@166aa18(タイ)
タイ

コンストラクタの引数を指定(2006.3.12)

魚には魚の名前を,釣り人には釣り人の名前をつけよう.Main.javaを修正する.

//Main.java
import jfun.yan.Components;
import jfun.yan.Container;
import jfun.yan.containers.DefaultContainer;

public class Main{
  public static void main(String[] args){
    Container yan=new DefaultContainer();
    yan.registerValue("kind of fish","タイ");         //a. 魚の名前を登録
    yan.registerValue("fisherman's name","浜ちゃん"); //b. 釣り人の名前を登録
    yan.registerComponent(Components.ctor(Fish.class)
                          .withArgument(0,Components.useKey("kind of fish")));
                          //c. 魚の名前を指定してコンテナに登録
    yan.registerComponent(Components.ctor(Fisherman.class)
                          .withArgument(0,Components.useKey("fisherman's name")));
                          //d. 釣り人の名前を指定してコンテナに登録
    Fisherman fisherman=(Fisherman)yan.getInstanceOfType(Fisherman.class);
    fisherman.fish();
  }
}

このプログラムを実行すると,こんな結果になる.

Fish@12d96f2(タイ) was fished by Fisherman@110003(浜ちゃん)

では,Main.javaを説明しよう.

Main.javaのa/bでContainer.registerValueに引数を二つつけて使用している.最初の引数が値につける名前(キー)で,次の引数が具体的な値である.キー"kind of fish"でタイ,キー"fisherman's name"で浜ちゃんをコンテナに登録している.なお,前述したようにキーを指定しないで(引数を一つで)Container.registerValueを使用すると,引数に指定した値のクラスが全て引数に指定した値になる.

c/dでFish.classとFisherman.classをコンテナに登録している.その際に,魚の名前と釣り人の名前を指定している.引数のあるコンストラクタを使用する場合は,Container.registerConstructorメソッドではなくContainer.registerComponentメソッドを使用する.

Components.ctorメソッドでComponentオブジェクト(Yan Containerに登録できるオブジェクト)を作成し,DefaultContainer.registerComponentでYan Containerに登録する.ComponentsクラスはComponentクラスを操作するためのクラスで,インスタンスを作成しなくても(クラスメソッドで)使用可能である.

Component.withArgumentメソッドで引数を指定する.第一引数の数値が引数の場所(1番目の引数が0)で第二引数が値である.Components.useKeyメソッドで引数に指定したキーに対応する値を取出すことができる.

魚や釣り人の名前をコンポーネント作成時にしか使わないのなら,Container.registerValueを使ってコンテナに登録する必要はない.

//Main.java
import jfun.yan.Components;
import jfun.yan.Container;
import jfun.yan.containers.DefaultContainer;

public class Main{
  public static void main(String[] args){
    Container yan=new DefaultContainer();
    yan.registerComponent(Components.ctor(Fish.class)
                          .withArgument(0,Components.value("タイ")));     //a. 魚の名前を指定
    yan.registerComponent(Components.ctor(Fisherman.class)
                          .withArgument(0,Components.value("浜ちゃん"))); //b. 釣り人の名前を指定.
    Fisherman fisherman=(Fisherman)yan.getInstanceOfType(Fisherman.class);
    fisherman.fish();
  }
}

a,bで使っているComponents.valueメソッドは引数に指定した値に対応するComponentオブジェクトを帰す.

コンテナから取り出す(Container.getInstanceOfTypeメソッドを使う)のはFishermanオブジェクトだけなのでFish.classの登録もやめてみる.

//Main.java
import jfun.yan.Component;
import jfun.yan.Components;
import jfun.yan.Container;
import jfun.yan.containers.DefaultContainer;

public class Main{
  public static void main(String[] args){
    Container yan=new DefaultContainer();
    Component fish=Components.ctor(Fish.class).withArgument(0,Components.value("タイ"));
                          //a. コンテナへの登録はしないでComponentの作成のみ
    yan.registerComponent(Components.ctor(Fisherman.class)
                          .withArgument(0,Components.value("浜ちゃん"))
                          .withArgument(1,fish));
                          //b. Fisherman.classの引数にfishを指定.
    Fisherman fisherman=(Fisherman)yan.getInstanceOfType(Fisherman.class);
    fisherman.fish();
  }
}

Component.withArgumentで複数の引数を指定する場合は,順序は前後しても良い.以下の例の場合,まずFisherman.classの第二引数(釣れる魚)を指定し,その後で第一引数(釣り人気名前)を指定している.

//Main.java
import jfun.yan.Component;
import jfun.yan.Components;
import jfun.yan.Container;
import jfun.yan.containers.DefaultContainer;

public class Main{
  public static void main(String[] args){
    Container yan=new DefaultContainer();
    Component fish=Components.ctor(Fish.class).withArgument(0,Components.value("タイ"));
    Component fisher=Components.ctor(Fisherman.class).withArgument(1,fish);
                                                        //a. 釣れる魚(第二引数)を指定して

    yan.registerComponent(fisher.withArgument(0,Components.value("浜ちゃん"))); 
                                                        //b. 釣り人(第一引数)を決める
    Fisherman fisherman=(Fisherman)yan.getInstanceOfType(Fisherman.class);
    fisherman.fish();

    yan.registerComponent(fisher.withArgument(0,Components.value("スーさん")));
                                                        //c. 別の人も来た.
    fisherman=(Fisherman)yan.getInstanceOfType(Fisherman.class);
    fisherman.fish();
  }
}

このプログラムの実行結果は以下の通りである.

Fish@b5dac4(タイ) was fished by Fisherman@12d96f2(浜ちゃん)
Fish@110003(タイ) was fished by Fisherman@17e4ca(スーさん)

複数のコンストラクタ(2006.3.26)

Fishermanクラスに複数のコンストラクタを用意しよう.

//Fisherman.java
public class Fisherman{
  private String name;
  private Fish fish;

  public Fisherman(Fish fish){
    this("浜ちゃん",fish);
  }
  public Fisherman(String name,Fish fish){
    this.name=name;
    this.fish=fish;
  }
  public void fish(){
    fish.fish(this);
  }
  public String getName(){
    return this.name;
  }
}

//Fish.java −変更なし−
public class Fish{
  private String kind;

  public Fish(String kind){
    this.kind=kind;
  }
  public void fish(Fisherman fisherman){
    System.out.println(this+"("+kind+") was fished by "+fisherman+"("+fisherman.getName()+")");
  }
}

以下のMain.javaを実行するとエラーになる.

//Main.java
import jfun.yan.Components;
import jfun.yan.Container;
import jfun.yan.containers.DefaultContainer;

public class Main{
  public static void main(String[] args){
    Container yan=new DefaultContainer();
    yan.registerComponent(Components.ctor(Fish.class)
                          .withArgument(0,Components.value("タイ")));
    yan.registerComponent(Components.ctor(Fisherman.class));
    Fisherman fisherman=(Fisherman)yan.getInstanceOfType(Fisherman.class);
    fisherman.fish();
  }
}

実行結果.

jfun.yan.util.AmbiguityException: type Fisherman has more than one qualified constructors
at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:180)
at org.apache.tools.ant.taskdefs.Java.run(Java.java:710)
at org.apache.tools.ant.taskdefs.Java.executeJava(Java.java:178)

at org.apache.tools.ant.taskdefs.Java.execute(Java.java:84)
at org.apache.tools.ant.UnknownElement.execute(UnknownElement.ja
<<以下省略>>

Fishermanオブジェクトには実行できるコンストラクタが複数あるため,コンテナは実行すべきコンストラクタがわからないわけだ.コンストラクタが複数ある場合には,以下のようにMain.javaを修正し,使用するコンストラクタを明示する必要がある.Components.ctorメソッドの第二引数に使用するコンストラクタの引数を配列で指定する.

//Main.java
import jfun.yan.Components;
import jfun.yan.Container;
import jfun.yan.containers.DefaultContainer;

public class Main07{
  public static void main(String[] args){
    Container yan=new DefaultContainer();
    yan.registerComponent(Components.ctor(Fish.class)
                          .withArgument(0,Components.value("タイ")));
    yan.registerComponent(Components.ctor(Fisherman.class,new Class[]{Fish.class}));
                                    //Fishオブジェクトのみを引数に持つコンストラクタを使う
    Fisherman fisherman=(Fisherman)yan.getInstanceOfType(Fisherman.class);
    fisherman.fish();
  }
}

実行結果は以下の通り.

Fish@b5dac4(タイ) was fished by Fisherman@12d96f2(浜ちゃん)

Fishオブジェクトと文字列を引数に持つコンストラクタを使う場合は以下のようになる.

//Main.java
import jfun.yan.Components;
import jfun.yan.Container;
import jfun.yan.containers.DefaultContainer;

public class Main07{
  public static void main(String[] args){
    Container yan=new DefaultContainer();
    yan.registerComponent(Components.ctor(Fish.class)
                          .withArgument(0,Components.value("タイ")));
    yan.registerComponent(Components.ctor(Fisherman.class,new Class[]{String.class,Fish.class}) //StringオフジェクトとFishオブジェクトを引数に持つコンストラクタを使用
                          .withArgument(0,Components.value("スーさん")));
    Fisherman fisherman=(Fisherman)yan.getInstanceOfType(Fisherman.class);
    fisherman.fish();
  }
}

実行結果は以下の通り.

Fish@110003(タイ) was fished by Fisherman@17e4ca(スーさん)

Component.withArgumentメソッドを使って引数を指定してもエラーは発生する.例えば,以下のMain.javaではエラーが発生する.

//Main.java
import jfun.yan.Component;
import jfun.yan.Components;
import jfun.yan.Container;
import jfun.yan.containers.DefaultContainer;

public class Main{
  public static void main(String[] args){
    Container yan=new DefaultContainer();
    Component fish=Components.ctor(Fish.class).withArgument(0,Components.value("タイ"));
    yan.registerComponent(Components.ctor(Fisherman.class) //Fishermanクラスのコンストラクタの引数を指定しない
                          .withArgument(0,Components.value("スーさん"))
                          .withArgument(1,fish));
    Fisherman fisherman=(Fisherman)yan.getInstanceOfType(Fisherman.class);
    fisherman.fish();
  }
}

Components.ctorメソッドに使用するコンストラクタの引数を指定するとエラーはなくなる.

//Main.java
import jfun.yan.Component;
import jfun.yan.Components;
import jfun.yan.Container;
import jfun.yan.containers.DefaultContainer;

public class Main07{
  public static void main(String[] args){
    Container yan=new DefaultContainer();
    Component fish=Components.ctor(Fish.class).withArgument(0,Components.value("タイ"));
    yan.registerComponent(Components.ctor(Fisherman.class,new Class[]{String.class,Fish.class}) //Fishermanクラスのコンストラクタの引数を指定する
                          .withArgument(0,Components.value("スーさん"))
                          .withArgument(1,fish));
    Fisherman fisherman=(Fisherman)yan.getInstanceOfType(Fisherman.class);
    fisherman.fish();
  }
}

interfaceの使用(2006.4.16)

interface Fishableを導入してみよう.Fish.javaとScuid.javaはFishableをimplementsして,Fisherman.javaではFishやScuidではなくFishableを使っている.

//Fisherman.java
public class Fisherman{
  private String name;
  private Fishable fishable;

  public Fisherman(String name,Fishable fishable){
    this.name=name;
    this.fishable=fishable;
  }
  public void fish(){
    fishable.fish(this);
  }
  public String getName(){
    return this.name;
  }
}

//Fishable.java
public interface Fishable{
  public void fish(Fisherman fisherman);
}

//Fish.java
public class Fish implements Fishable{
  private String kind;

  public Fish(String kind){
    this.kind=kind;
  }
  public void fish(Fisherman fisherman){
    System.out.println(this+"("+kind+") was fished by "+fisherman+"("+fisherman.getName()+")");
  }
}

//Scuid.java
public class Scuid implements Fishable{
  private String kind;

  public Scuid(String kind){
    this.kind=kind;
  }

  public void fish(Fisherman fisherman){
    System.out.println(this+"("+kind+") was fished by "+fisherman+"("+fisherman.getName()+")");
  }
}

//Main.java
import jfun.yan.Component;
import jfun.yan.Components;
import jfun.yan.Container;
import jfun.yan.containers.DefaultContainer;

public class Main{
  public static void main(String[] args){
    Container yan=new DefaultContainer();
    Component fish=Components.ctor(Fish.class).withArgument(0,Components.value("タイ"));
    yan.registerComponent(Components.ctor(Fisherman.class)
                          .withArgument(0,Components.value("浜ちゃん"))
                          .withArgument(1,fish));
    Fisherman fisherman1=(Fisherman)yan.getInstanceOfType(Fisherman.class);
    fisherman1.fish();

    Component scuid=Components.ctor(Scuid.class).withArgument(0,Components.value("ホタルイカ"));
    yan.registerComponent(Components.ctor(Fisherman.class)
                          .withArgument(0,Components.value("スーさん"))
                          .withArgument(1,scuid));
    Fisherman fisherman2=(Fisherman)yan.getInstanceOfType(Fisherman.class);
    fisherman2.fish();
  }
}

このプログラムの実行結果は以下の通りである.

Fish@14da8f4(タイ) was fished by Fisherman@18f6235(浜ちゃん)
Scuid@641e9a(ホタルイカ) was fished by Fisherman@115273a(スーさん)

singleton(シングルトン)(2006.5.7)

特定のオブジェクトのインスタンスを一つに制限する(singleton)ことも可能である.Component.singletonメソッドを使用する.修正が必要なのはMain.javaのみで,他のソースには修正は不要である.

//Main.java
import jfun.yan.Component;
import jfun.yan.Components;
import jfun.yan.Container;
import jfun.yan.containers.DefaultContainer;

public class Main{
  public static void main(String[] args){
    Container yan=new DefaultContainer();
    Component fish=
      Components.ctor(Fish.class).withArgument(0,Components.value("タイ")).singleton();
      //Component.singletonメソッドで,生成するインスタンスは一つに制限する
    yan.registerComponent(Components.ctor(Fisherman.class)
                          .withArgument(0,Components.value("浜ちゃん"))
                          .withArgument(1,fish));
    Fisherman fisherman1=(Fisherman)yan.getInstanceOfType(Fisherman.class);
    fisherman1.fish();

    yan.registerComponent(Components.ctor(Fisherman.class)
                          .withArgument(0,Components.value("スーさん"))
                          .withArgument(1,fish));
    Fisherman fisherman2=(Fisherman)yan.getInstanceOfType(Fisherman.class);
    fisherman2.fish();
  }
}

実行結果は以下の通り

Fish@adb1d4(タイ) was fished by Fisherman@175d6ab(浜ちゃん)
Fish@adb1d4(タイ) was fished by Fisherman@160a26f(スーさん)

浜ちゃんもスーさんも同じ魚を釣り上げたようだ.

lifecycle(2006.5.28)

特定のインスタンスの生成から消滅までをコンテナに管理してもらうことができる.今回はFishクラスをコンテナに管理してもらう.

//Fish.java
public class Fish implements Fishable{
  private String kind;

  public Fish(String kind){
    this.kind=kind;
  }
  public void initialize(){
    System.out.println(this+"("+kind+") initialized.");
  }
  public void dispose(){
    System.out.println(this+"("+kind+") disposed.");
  }
  public void fish(Fisherman fisherman){
    System.out.println(this+"("+kind+") was fished by "+fisherman+"("+fisherman.getName()+").");
  }
}

//Main.java
import jfun.yan.Component;
import jfun.yan.Components;
import jfun.yan.Container;
import jfun.yan.containers.DefaultContainer;
import jfun.yan.lifecycle.DefaultLifecycleManager;

public class Main{
  public static void main(String[] args){
    Container yan=new DefaultContainer();
    DefaultLifecycleManager manager=new DefaultLifecycleManager();
                                          //a. ライフサイクルマネージャのインスタンスを生成
    Component fish=manager.newLifecycle() //b-1. ライフサイクルマネージャに
      .initializer("initialize")          //b-2. インスタンス生成時に実行するメソッド
      .disposer("dispose")                //b-3. インスタンス消滅時に実行するメソッド
      .manage(Components.ctor(Fish.class).withArgument(0,Components.value("タイ")));
                                          //b-4. ライフサイクルマネージャが管理するオブジェクト
    yan.registerComponent(Components.ctor(Fisherman.class)
                          .withArgument(0,Components.value("浜ちゃん"))
                          .withArgument(1,fish));
    Fisherman fisherman=(Fisherman)yan.getInstanceOfType(Fisherman.class);
    try{
      manager.init();                     //c-1. ライフサイクルマネージャの初期化
      fisherman.fish();
      manager.dispose();                  //c-2. ライフサイクルマネージャの停止
    }
    catch(Throwable t){
      System.out.println(t);
    }
  }
}

実行結果は以下の通り.

Fish@1faba46(タイ) initialized.
Fish@1faba46(タイ) was fished by Fisherman@1c74f37(浜ちゃん).
Fish@1faba46(タイ) disposed.

c-1でライフサイクルマネージャを初期化した(DefaultLifeCycleManager.initメソッドを呼んだ)際に,b-1で指定したinitializerを実行し,ライフサイクルマネージャの消滅(DefaultLifeCycleManager.desposeを呼んだ)際にb-2で指定したdisposerを実行する.

参考