複数種類の入力からモデルを生成するには・・・

仕事で作っているツールで、XMLファイルあるいはCSVファイルからモデルを生成する必要があり、その方法で悩んでみました。
とりあえずモデルの実装クラスはXMLとかCSVとかと非依存の形にして、モデル生成部(モデルビルダ)をXMLCSVの形式にあわせて二通りの実装を用意。
実装を選択させる方法ですが、XMLから読み込む場合は一つのXMLファイルに一つのプロジェクトモデルが対応しているのですが、CSVからの読み込みの場合は一つのCSVファイルでプロジェクトモデルが持つ子モデルの一つに対応しているため、微妙に不一致があります。
この不一致を気にせず、CSVだろうが、XMLだろうが、DOMの要素だろうがうまくモデルビルダの実装を選択して読み込むための方法を考えてました。

結果、以下のようなインタフェース、抽象クラスを作成

ModelBuilderFactory
モデルビルダファクトリ、モデルビルダを生成する。
ModelBuilder
モデルビルダ、モデルを生成する。
ModelBuilderConfiguration
モデルビルダの設定情報
ModelInputSource
入力ソース、モデルを生成するのに必要な情報(XMLCSV)をあらわす。

モデルビルダファクトリの実装を取得

ModelBuilderFactory は抽象クラスであり、XMLCSV等の入力ソースの種類毎に実装を用意します。
ModelBuilderFactory の実装取得メソッドは引数に ModelBuilderConfiguration オブジェクトを受け取ります。ModelBuilderFactoryConfiguration は入力ソースの種別毎(XMLCSV)に実装を作成します。ModelBuilderFactory の実装取得メソッドでは、引数に受け取った ModelBuilderFactoryConfiguration オブジェクトがどの入力ソースの設定オブジェクトかを判断し、対応する実装を返します。こうして入力ソース種別毎のモデルビルダファクトリを取得します。

モデル毎のモデルビルダ実装を取得

ModelBuilderFactory には ModelBuilder 生成メソッドがあり、このメソッドの引数は Class オブジェクトです。メソッド引数に渡す Class オブジェクトは生成したいモデルのインタフェースの Class オブジェクトを渡します。入力ソース種別毎の ModelBuilderFactory 実装は自身の管理する ModelBuilder の中に Class オブジェクトで指定されているモデルを生成可能な物があれば、その ModelBuilder オブジェクトを返します。こうして入力ソース・モデル毎のモデルビルダを取得します。

モデルビルダを使用し、モデルを生成

ModelBuilder にはモデルを新規に生成するメソッドと、入力ソースより取得した情報に従いモデルを生成するメソッドがあります。
モデルを新規に生成するメソッドでは、適当なモデル実装のオブジェクトを生成して返します。
入力ソースより生成するメソッドでは、引数に入力ソースの情報を表す ModelInputSource のオブジェクトを受け取ります。ModelInputSource は入力ソースの種別毎に実装を作成します。こうすることで、入力ソースごとに異なる処理対象を軽く隠蔽しています。モデルビルダファクトリの実装を取得するときに使用したモデルビルダ設定情報の種類と、入力ソースの種類を対応させることで、モデルビルダが入力ソースを正しく処理できることを狙っています。

クラス図とかが無いと、分かりづらいですね・・・
とりあえず、使用時の例。

XMLModelBuilderConfiguration
モデルビルダの設定情報を表す ModelBuilderConfiguration の XML 用実装
XMLModelInputSource
モデル生成の入力ソースを表す ModelInputSource のXML用実装、InputStream や DOM の Element を入力ソースとして使用可能
XMLModelBuilderConfiguration config = new XMLModelBuilderConfiguration();
XMLModelInputSource inputSource = new XMLInputSource( new FileInputStream( "moe.xml" ) );

ModelBuilderFactory factory = ModelBuilderFactory.newInstance( config );
ModelBuilder modelBuilder = factory.newModelBuilder( LittleBSDModel.class );
LittleBSDModel littleBSDModel1 = (LittleBSDModel)modelBuilder.createModel();
LittleBSDModel littleBSDModel2 = (LittleBSDModel)modelBuilder.parseModel( inputSource );

上記のような感じで使用。このような仕組みをつくって何が嬉しいのかというと、上記の例を見てもらえばわかるのですが、XML に依存する部分は設定情報と入力ソースだけになります。このXML依存部分はメニュー等から呼び出されるアクションで生成してしまい、それ以降の部分は入力ソースの種別が何であったかは意識しないで処理を行なえるようになります。また、メニュー等ではなく、コマンドライン引数として入力ソースの種別が決定されるとした場合、コマンドライン引数のパース部分に入力ソース種別依存部分は閉じ込めてしまうことができたりもします。
うまく作りこめば、設定ファイル等によってあとから入力ソースの種別を増やしたりすることも可能かと。

CSVXMLのどちらからでもモデルを生成するという要求から始まり、上記のようなことを考えて&半分ほど実装してみました。たぶんオーバースペック(w
ちなみに、私的には

factory.newModelBuilder( LittleBSDModel.class )

の部分がお気に入り。ちょっとだけダイコン(IoCコンテナ)の影響を受けてみたり(ww