Wicketで階層構造を扱う際にHTMLとJavaコードの構造を一致させる

Scala exercise 6: Tackling the Wicket hierarchy mismatch problemという記事に触発されて、同様の手法をJavaで再現してみました。

Wicketでネストされたコンポーネントを扱う場合、HTMLテンプレートに比べてJava側は見通しが悪くなりがちだと思います。元記事ではScalaを利用した解決方法が示されているわけですが、私が理解した範囲ではポイントは下記の2点のようです。

  1. コンポーネントの階層構造と同じになるようにソースコードをインデント(構造化)する。
  2. 追加対象を常にctxという同じ変数名にすることでHTMLの構造変化があってもどこにでもコピペ移動ができる。

実際にこの手法で書かれたソースコードを見るとなかなか悪くなさそうです。ただ残念ながら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ファイルも下記からダウンロードできます。

 http://jenkins.madogiwa.org/job/wicket-nest/

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>

*1:今回の内容には不要ですが、この例では[http://d.hatena.ne.jp/mdgw/20101106/p1:title=前回作成したライブラリ wicket-altmark]を利用しています。