-S04: 道路工事の裏側

コンテナS2Containerの生成プロセスから見ていきましょう。list04-1のように、S2Containerはファクトリによって生成します。

list04-01. list03-01よりコンテナ生成部分を抜粋
private static String PATH = "tutorial/org/seasar/console/car.xml";

S2Container container = S2ContainerFactory.create(PATH);

ファクトリS2ContainerFactoryは、そのstaticなcreate(String)メソッドの引数に指定されたXMLファイルの設定にしたがってS2Containerを初期化します。例では引数には"tutorial/org/seasar/console/car.xml"を渡していますが、こちらはJavaのクラスローダーでリソース取得を行っています。よって、car.xmlというXMLファイルをtutorial.org.seasar.consoleパッケージフォルダの中におけば良い訳です。
その後、生成されたS2Containerからコンポーネント取得をしています。

list04-02. list03-01よりコンポーネント取得部分を抜粋
Car car = (Car)container.getComponent(Car.class);

S2Container#getComponent(Class)メソッドにてコンポーネントが取得されています。引数には取得するコンポーネントのインターフェイスの型を渡します。コンポーネント取得には他に、S2Container#getComponent(String)というメソッドもあります。こちらは、設定XML中でコンポーネントにつけられた名前で取得する方法です。list03-4では、コンポーネントをこう設定していました。

list04-03. list03-04よりコンポーネント登録部分を抜粋
<component class="tutorial.org.seasar.console.HelloCar"/>

<component>エレメントのclass属性の値はコンポーネントのクラス名です。S2Containerはこのコンポーネントクラスを調べ、implementsされているインターフェイスをすべてキーとしてコンポーネントを保持するS2Container内のコレクションに登録します。list03-3のHelloCarクラスのコードにおいて、

list04-04. list03-03よりクラス定義部分を抜粋
public class HelloCar implements Car

とありますから、HelloCarを登録すれば、キー(Car)&値(HelloCar)を取得することができます。よってlist03-1でgetComponent(Car.class)でコンポーネントが取得できているのです。getComponent(*)の返値はObject型なので、コンポーネントの利用には取得の後に適切な型にキャストする必要があります。その際も、取得のキーとして指定したインターフェイスにキャストすれば完全に実装の隠蔽が可能となります。この例では、(Car)getComponent(Car.class)とやってます。
コンポーネントに名前をつける場合は、以下のとおりです。

list04-05. コンポーネントに名前をつけたcar.xml
<component name="helloCar" class="tutorial.org.seasar.console.HelloCar"/>

Highway.javaの変更部分
Car car = (Car)container.getComponent("helloCar");

<component>エレメントのname属性に設定された名前で、後にS2Container#getComponent(String)で取得しています。name属性が設定されたコンポーネントは、S2Container内部で、インターフェイスをキーとするコンポーネントの保持に加え、キー("helloCar")&値(HelloCar)という組み合わせも登録します。
コンポーネント取得後は、list03-1の最後のように、

list04-06. list03-1からコンポーネント実行部を抜粋
car.run();

といったメソッド実行を行えます。Highwayクラスのコード上はCar#run()というインターフェイスメソッドコールを行っていますが、もちろん、設定XMLに定義したクラスHelloCarに実装されたrun()メソッドを実行しています。よって、設定XMLの書き換えで挙動を変更することができます。たとえば、

list04-7 GoodbyCar.java と GoodbyCarを登録したcar.xml
package tutorial.org.seasar.console;
public class GoodbyCar implements Car {
  public void run() {
    System.out.println("Bye bye!");
  }
}

<?xml version="1.0" encoding="UTF-8"?>
<components>
  <component class="tutorial.org.seasar.console.GoodbyCar"/>
</components>

これでブートストラップのHighwayクラスをまったく変更しなくても、実行結果は、

Bye bye!

となります。例はとてつもなくつまらないですが、実装がきちんと隠蔽されました。S2Containerを外部から利用するアプリケーションと、S2Container上に搭載するコンポーネント疎結合することが可能になったのです。