LiQ Containerのコンポーネントの定義は複数のModuleに分割して
定義することができます。そして、あるModuleのコンポーネントは
他のコンポーネントは他のModuleのコンポーネントを依存関係として
使うことができます。
たとえば、前回の解説で示した、GlassクラスとWaterクラスを
以下のように別々のModuleに分けて定義する例を考えてみましょう。
----
相変わらず全くもって実践的な例で無い点は御容赦ください。
複数のModuleを一緒に使うには、ModuleSetというクラスを使います。
使い方は以下の様にします。
----
このように、使いたいModuleを全部 ModuleSet のインスタンスに
ブチこんで、ModuleSetのgetInstanceメソッドからコンポーネントの
インスタンスを取得します。
ModuleSetは、要求されたコンポーネントを保持している Module を探して、
そこからコンポーネントを取得しようとします。
取得しようとするコンポーネントの依存関係が、同じModuleの中に
あるのであれば、それを普通に使います。もし依存関係が同じModuleの中に
ないのであれば、ModuleSetに登録されている他のModuleの保持するコンポーネント
の中から依存関係を探します。
この例では、要求されたコンポーネント Glass.class をインスタンス化
するために必要なBeverage.classのインスタンスは、同じGlassModuleの中には
存在しないので、ModuleSetに登録されているWaterModuleから、Beverageを実装している
Water.classのインスタンスをとってきて inject しています。
このアプローチは他のDIコンテナとはちょっと違います。
PicoContainerのように継承元となる「親コンテナ」を参照できるというアプローチが
一番良くあるパターンですが、これは親を一つしか持てない、という制約が大きくて
嫌だな、と思っていました。
S2 Containerのように参照したい設定を「インクルード」する、というアイデアも
良いとは思いました。実際途中までは S2 のアイデアをパクって「インクルード」の線で
実装を進めていたのです。
しかし、LiQ Containerで「インクルード」をやろうとするとどうしてもインクルード
するModuleのクラスを指定することになり、インクルードしたいModuleがクラスパスに
通っていないとコンパイルすら出来ない、という事態が発生します。
またインクルードする側がどの設定ファイルを使うか、という
ことを意識しなければならないので、環境によってインクルードする
ファイルを変えるには条件付きインクルードのようなやや複雑な機構が
必要になってきます。
(LiQ Containerの Module は普通の Java コードなので、if文でいいじゃん、
という説もあったのですが。Moduleにif文を書いたりするのは、
自分的にはちょっと違う、と感じました)
で、暫く悩んだ結果、実はコンポーネントの設定は階層構造にする必要は
ないのではないか、フラットな構造でも十分だし、むしろ利用者に
とってもわかりやすいのではないか、という結論に達し、
ModuleSetという仕組を導入することにしたのであります。
ModuleSetに追加するModuleを変えるだけで、簡単にコンポーネント
が参照するインタフェースの実装を差し替えることができて、
しかも各 Module は、自分が必要とする他のModuleを意識する必要が
ないところが良いのではないかと思っています。
次はコンポーネントをインスタンス化する際の基本的なメカニズムに
ついて解説したいと思います。
定義することができます。そして、あるModuleのコンポーネントは
他のコンポーネントは他のModuleのコンポーネントを依存関係として
使うことができます。
たとえば、前回の解説で示した、GlassクラスとWaterクラスを
以下のように別々のModuleに分けて定義する例を考えてみましょう。
----
public class Glass { private Beverage content; public Glass(Beverage content) { this.content = content; } public Beverage getContent() { return content; } } public class Water implements Beverage { public String getName() { return "water"; } public int getPrice() { return 110; } } public class GlassModule extends Module { @Override protected void moduleDef() { components(Glass.class); } } public class WaterModule extends Module { @Override protected void moduleDef() { components(Water.class); } }----
相変わらず全くもって実践的な例で無い点は御容赦ください。
複数のModuleを一緒に使うには、ModuleSetというクラスを使います。
使い方は以下の様にします。
----
ModuleSet moduleSet = new ModuleSet(); moduleSet.addModule(new GlassModule()); moduleSet.addModule(new WaterModule()); Glass glass = moduleSet.getInstance(Glass.class);----
このように、使いたいModuleを全部 ModuleSet のインスタンスに
ブチこんで、ModuleSetのgetInstanceメソッドからコンポーネントの
インスタンスを取得します。
ModuleSetは、要求されたコンポーネントを保持している Module を探して、
そこからコンポーネントを取得しようとします。
取得しようとするコンポーネントの依存関係が、同じModuleの中に
あるのであれば、それを普通に使います。もし依存関係が同じModuleの中に
ないのであれば、ModuleSetに登録されている他のModuleの保持するコンポーネント
の中から依存関係を探します。
この例では、要求されたコンポーネント Glass.class をインスタンス化
するために必要なBeverage.classのインスタンスは、同じGlassModuleの中には
存在しないので、ModuleSetに登録されているWaterModuleから、Beverageを実装している
Water.classのインスタンスをとってきて inject しています。
このアプローチは他のDIコンテナとはちょっと違います。
PicoContainerのように継承元となる「親コンテナ」を参照できるというアプローチが
一番良くあるパターンですが、これは親を一つしか持てない、という制約が大きくて
嫌だな、と思っていました。
S2 Containerのように参照したい設定を「インクルード」する、というアイデアも
良いとは思いました。実際途中までは S2 のアイデアをパクって「インクルード」の線で
実装を進めていたのです。
しかし、LiQ Containerで「インクルード」をやろうとするとどうしてもインクルード
するModuleのクラスを指定することになり、インクルードしたいModuleがクラスパスに
通っていないとコンパイルすら出来ない、という事態が発生します。
またインクルードする側がどの設定ファイルを使うか、という
ことを意識しなければならないので、環境によってインクルードする
ファイルを変えるには条件付きインクルードのようなやや複雑な機構が
必要になってきます。
(LiQ Containerの Module は普通の Java コードなので、if文でいいじゃん、
という説もあったのですが。Moduleにif文を書いたりするのは、
自分的にはちょっと違う、と感じました)
で、暫く悩んだ結果、実はコンポーネントの設定は階層構造にする必要は
ないのではないか、フラットな構造でも十分だし、むしろ利用者に
とってもわかりやすいのではないか、という結論に達し、
ModuleSetという仕組を導入することにしたのであります。
ModuleSetに追加するModuleを変えるだけで、簡単にコンポーネント
が参照するインタフェースの実装を差し替えることができて、
しかも各 Module は、自分が必要とする他のModuleを意識する必要が
ないところが良いのではないかと思っています。
次はコンポーネントをインスタンス化する際の基本的なメカニズムに
ついて解説したいと思います。