案件の忙しさにかまけて、3週間ほどブログの更新が止まってしまいました。そろそろ戻していきます。
最近、コンフィグ(設定ファイル)の代わりにDIコンテナを使うというのがメジャーになりつつあると思います。これまでであればCommons Digesterを使っていたようなコンフィグについてもDIコンテナにまかせることでスケーラブルなアプリケーションの構築が可能になりました。
例えばワークフローエンジンを考えます。ワークフローは複数のステート(状態)を持ち、そのステートごとにアクション(動作)と、そのアクティビティを実行可能とするコンディション(条件)を持ちます。
単純な文書管理で編集中ステートでは編集アクティビティと申請アクティビティがあるという場合、XMLで考えると以下のようになります。
<state name="Editing">
<activity name="Edit">
<execute class="sample.EditActivitiy"/>
<condition class="sample.EditCondition"/>
</activity>
<activity name="Apply">
<execute class="sample.ApplyActivitiy"/>
<condition class="sample.ApplyCondition"/>
</activity>
</state>
</workflow>
要素workflowが1つのフローを示しています。次の要素Stateは編集中の状態を示しています。この状態では、編集(Edit)と申請(Apply)という2つのアクティビティが用意されています。それぞれのアクティビティには、要素actionで実際に発生するアクション、要素conditionでこのアクティビティを実行する条件が示されます。
例えば申請は特定の権限を持つ人しかできないのであれば、sample.ApplyConditionの中で、そのような実装を行なうわけです。
ここまで考えたら、従来であればエレメントに合うようにJavaBeansを用意して、Digesterでマッピングを行ないます。そして、WorkflowManager#addWorkflow()とかでフローを管理させるところでしょう。
で、こいつをDIコンテナで考える非常に面白くなります。例えばSpringFrameworkでは次のようになります。
<property name="flows">
<map>
<entry key="DocumentManagement">
<bean class="flow.Flow">
<property name="states">
<map>
<entry key="Editing">
<bean class="flow.State">
<property name="activities">
<map>
<entry key="Edit">
<bean class="flow.Activity">
<property name="action">
<bean class="sample.EditActivitiy"/>
</property>
<property name="condition">
<bean class="sample.EditCondition"/>
</property>
</bean>
</entry>
<entry key="Apply">
<bean class="flow.Activity">
<property name="action">
<bean class="sample.ApplyActivitiy"/>
</property>
<property name="condition">
<bean class="sample.ApplyCondition"/>
</property>
</bean>
</entry>
</map>
</property>
</bean>
</entry>
</map>
</property>
</bean>
</entry>
</map>
</property>
</bean>
まずクラスflow.WorkflowManagerそのものをworkflowManagerで登録します。そいつの属性flowsはjava.util.Mapになっており複数のフローを登録できます。ここではキーDocumentManagementで、実体としてクラスflow.FlowをInjectしています。クラスflow.Flowには属性statesがあり、これがまたjava.util.Mapになっています。登録されているのはキーEditingで実体がクラスflow.State。そしてクラスflow.Stateには属性activitiesがあり、これまたjava.util.Map。登録されているのはキーEditとキーApplyで実体がクラスflow.Activityです。さらにクラスflow.Activityには、属性actionと属性conditionがあります。書かれていませんが、それぞれインタフェースflow.Actionとインターフェースflow.Conditionを取ります。
いやー、長ったらしいですね。先ほどのXMLを違って、SpringFrameworkのエレメントは非常に汎用的なのでこういう表現になってしまいます。でも、Digesterを用いたときとは根本的に違う価値が提供されています。
Digesterの場合にはコンフィグファイルのXMLを、そのままJavaBeansにバインドして、それをWorkflowManagerに登録しているイメージでした。つまり、XMLはまさにコンフィグだけだったわけです。コンフィグを紙に書いて、それをアプリケーションに読み込ませるわけですね。
ところがDIコンテナを用いた場合、WorkflowManagerに対して直接ActivityやConditionのクラスをInjectしています。つまり、このコンフィグファイルは「コンフィグのみ」ではなくて、「アプリケーションの構成そのものとしてのコンフィグ」を示しています。設定を紙に書くのではなく、直接、完成されたイメージを組み上げているのです。
この違いが如実に現れるのがスケーラビリティです。
たとえば、アクティビティの実行条件であるコンディションにおいて、複数の条件をAND ORでつなぎたいとしましょう。Digester版では、すでにXMLのスキーマは固定的なので、やるとすれば呼び出し先のConditionクラスにおいて実装をすることになります。あるいはスキーマを変えていいなら、
<and>
<condition class="sample.Condition1" />
<condition class="sample.Condition2" />
</and>
</condition>
という感じでしょうか。ようはスキーマを変えて設定できる範囲を広げる必要性があります。
しかし、DIコンテナの場合にはInjectできるわけですから、例えば
<bean class="sample.AndCondition">
<property name="conditions">
<list>
<bean class="sample.Condition1" />
<bean class="sample.Condition2" />
</list>
</property>
</bean>
</property>
というように書けます。クラスsample.AndConditionというコンディションを作ります。そいつの属性conditionsに含まれている全てのコンディションを順番に実行し、trueなりfalseなりを返します。これだけでAnd条件の処理が書けてしまいます。コンフィグのスキーマがどうこうという話にはなりません。なぜなら、ここで書いているのはアプリケーションの構成そのものであって、設定ファイルではないからです。
この自由度の違いがお分かりになるでしょうか?Conditionを例に挙げましたが、例えばActionでも同じです。複数のアクションを実行したいとしても、Digester版ではスキーマの変更が必要です。DIコンテナ版であれば、sample.ExecAllActionsクラスを作って、その属性actionsにListとしてActionを突っ込んでやればいいのです。
これが「コンフィグとしてのDIコンテナ」です。このテクニックは非常に有効です。これからDigesterを使うときには、必ずこの方法で代替できないか考えてみてください。
で、DIコンテナで書くのはいいけど、書きにくい!という方がいるかと思います。そこで登場したのがXBeanというわけです(参考:SpringのXML設定ファイルを簡素化 - XBeanで)。
このXBeansのすごさを語りだすと長くなるので今回はここまでにします。
