Wicketで階層構造を扱う際にHTMLとJavaコードの構造を一致させる
Scala exercise 6: Tackling the Wicket hierarchy mismatch problemという記事に触発されて、同様の手法をJavaで再現してみました。
Wicketでネストされたコンポーネントを扱う場合、HTMLテンプレートに比べてJava側は見通しが悪くなりがちだと思います。元記事ではScalaを利用した解決方法が示されているわけですが、私が理解した範囲ではポイントは下記の2点のようです。
実際にこの手法で書かれたソースコードを見るとなかなか悪くなさそうです。ただ残念ながらScalaは今ひとつ使い方が分からないので、元記事のScala exerciseという目的を全力で無視して、Javaで同様の手法を再現してみることにしました。
どのようにJavaで再現するかですが、まずコンポーネントの階層構造をソースコードに反映させる部分は、無名インナークラスを利用することにしました。コンポーネントの階層が増えるたびにインナークラスを作成することになります。また追加対象を常に同じ変数にする部分は、せっかく無名クラスをネストさせるので、単純にthisを利用することにしました。
このような方針で適当に作ってみたところ、下記のようなコードが出来上がりました。
public MyPage ... Form form = new Form("form"); add(new With(form) { { TextField nameField = new TextField("email"); add(nameField); } }); ..
対応するHTMLテンプレートはこんな感じです。*1
<form id="wicket-form" action="..."> <input id="wicket-email" type="text" /> </form>
作成したプログラムはWithというクラス1つのみです。Javaコードでは、無名インナークラスとしてWithから派生したクラスを作成しています。Withという名前のとおり、コンストラクタに渡されたコンテナが、インナークラス内でのthisとして使われることをイメージしています。また記述の簡略化のために、インスタンスの初期化ブロックを利用しています。
WithはMarkupContainerが持つ全てのpublic methodを実装したクラスです。それらのメソッドは全て、コンストラクタに渡されたコンテナへと処理が委譲されます。この構造により、インスタンスの初期化ブロック内でaddメソッドを呼ぶと、実際にはコンストラクタに渡されたコンテナ(この例の場合はform)へと追加されることになります。
またWithはIBehaviorを実装しているため、addで追加することができます。IBehaviourは親コンテナに追加される際に、bindメソッドが呼ばれます。そこでbindメソッド内で、コンストラクタに渡されたコンテナがaddされていないようであれば、Withの親(この例の場合はWebPage)にaddしています。これにより、Withクラスの作成とコンテナの追加を1行で記述することが可能になっています。ちなみにWith自体は他に何の処理も行わないので追加しても無害(なはず)です。
ダウンロード
1ファイルだけの簡単なコードなのですが、一応Google Codeで公開しています。
http://code.google.com/p/wicket-nest/
またJARファイルも下記からダウンロードできます。
Maven2
JARをダウンロードする以外に、Maven2のリポジトリもあります。
もしMaven2で利用する場合は、pom.xmlに下記のリポジトリを追加して下さい。
<repositories> <repository> <id>maven.madogiwa.org</id> <name>madogiwa.org Repository</name> <url>http://maven.madogiwa.org/maven2</url> </repository> </repositories>
また同様にpom.xmlに下記の依存設定を追加して下さい。
<dependency> <groupId>org.madogiwa</groupId> <artifactId>wicket-nest</artifactId> <version>1.0</version> </dependency>