LoginSignup
25
24

More than 5 years have passed since last update.

Wicketによるアプリ開発事始め

Posted at

匿名内部クラスについて

Javaには、匿名内部クラス というものがあります.

これは、クラス(またはインタフェース)を継承したクラスを作成すると同時に、オブジェクトを生成するものです.

次に例を示します.

Object o = new Object() {
    public Strign toString() {
        return "匿名内部クラスのオブジェクトです.";
    }
};

new Object() {}の、中括弧({})がポイントです.

これを付けることで、Objectクラスを継承したクラスを作ったことになり、それをnewしているのでオブジェクトを生成していることになります.

匿名内部クラスはWicketを使うときには頻出するため、ぜひ慣れておきましょう.

下記のサイトで構文と効果を学習しておきましょう.

匿名内部クラス

Pageクラス実装基礎

コンポーネントの初期化

Pageクラスには、通常、画面を動的に編集するためのコンポーネントが載ります.

コンポーネントを使うには、次のようなスタイルでコーディングすることをおすすめします.

  • コンポーネントはインスタンス変数に宣言
  • getterを作る
  • getterの中は遅延初期化
  • コンポーネントのインスタンスが必要なときは、必ずgetterを通して得る(そのインスタンスのgetterの中は例外)

サンプルを示します

SomePage.java
private Form<?> form;
private TextField<String> description;

private Form<?> getForm() {
    if (this.form == null) {
        this.form = new Form<Object>("form");
        this.form.add(getDescription()); // コンポーネントの入れ子. getterを使って子コンポーネントにアクセスする.
    }
    return this.form;
}

private TextField<String> getDescription() {
    if (this.description == null) {
        this.description = new TextField<String>("description", Model.Of(""));
    }
    return this.description;
}

コンポーネントいろいろ

サブミットするコンポーネント(Buttonクラス/SubmitLinkクラス/Formクラスなど)

これらのコンポーネントは、サブミット時の処理をonSubtmitメソッドに記述します.

ここで匿名内部クラスを使うと、ソースがコンパクトになります.

private Button submitter;
private Button getSubmitter() {
    if (this.submitter == null) {
        this.submitter = new Button("submitter") {
            public void onError() {
                // エラー時の処理. 一般的にはあまりやることがない
            }
            public void onSubmit() {
                // 入力値検証が全てOKの場合にここが実行される.
                // 小さい処理はここに直接書いてもいいが、大きい処理は別にメソッドを作るのが良い.
            }
        };
    }
    return this.submitter;
}

Ajaxでサブミットするコンポーネント(IndicatingAjaxButtonクラスなど)

これらのクラスも、サブミットしたときの処理はonSubmitメソッドに記述します.

ただし、通常のButtonクラスなどによるサブミットはPage全体が再描画されることに対し、Ajaxの場合は再描画するコンポーネントを限定するのが一般的です.

(これによりデータ転送量を抑え、ユーザビリティの向上が望める)

ということは、Ajaxでサブミットした場合は、何らかの方法で再描画するコンポーネントをプログラム側で指定する必要がある、ということになります.

ここで登場するのがAjaxRequestTargetクラスです.

AjaxRequestTarget

実は、AjaxButtonなどのAjaxでサブミットするコンポーネントのonSubmitメソッドは、引数にAjaxRequestTargetを取ります.

下記はAjaxButtonクラスのonSubmitメソッドのシグニチャです.

/**
 * Listener method invoked on form submit with no errors, before {@link Form#onSubmit()}.
 * 
 * @param target
 * @param form
 */
protected void onSubmit(AjaxRequestTarget target, Form<?> form)
{
}

AjaxRequestTarget#addメソッドにコンポーネントを与えると、そのコンポーネントは再描画対象になります.

protected void onSubmit(AjaxRequestTarget pTarget, Form<?> pForm) {
    pTarget.add(getDescription());
}

注意点・再描画するコンポーネントはsetOutputMarkupId(true)を呼んでおく必要がある!

AjaxRequestTargetのその他の機能

appendJavaScriptメソッド

Ajaxのレスポンスをブラウザが受け取ったときに実行するJavaScriptを指定します.

focusComponentメソッド

Ajaxのレスポンスをブラウザが受け取ったときにフォーカスを移すコンポーネントを指定します.

ループの実現(ListViewクラス)

Ajaxによるページング

モデルいろいろ

AbstractReadOnlyModel

PropertyModel

CompoundPropertyModel

小ネタ

Pageクラスが保持する可能性のあるクラスにはSerializableを実装するべし

状態のないページ(Stateless)

ブックマーク可能なページはステートレスにするべき

ログイン画面が実は難易度高い(正直、困る)

Ajaxコンポーネントが載っているページをステートレスにするのは難しい.

Pageクラス階層

共通のレイアウトを定義するために、基底クラスを作りましょう.

レイアウトを変更可能にしておく必要がある箇所は、PanelやModelを返すような抽象メソッドを作ることで対処します.

WebPageBase.java
public abstract AppPageBase extends WebPage {

    private Label titleLabel;
    private Panel headerPanel;

    public AppPageBase() {
        this.add(getTitleLabel());
        this.add(getHeaderPanel());
    }


    /**
     * @return titleタグの中のテキストとなるモデルを返して下さい.
     */
    protected abstract IModel<String> getTitleModel();

    /**
     * @return headerタグの中身となる任意のPanelオブジェクトを生成して返して下さい.
     */
    protected Panel createHeaderPanel(String pPanelId);

    private Label getTitleLabel() {
        if (this.titleLabel == null) {
            this.titleLabel = new Label("titleLabel", new AbstractReadOnlyModel<String>() {
                @Override
                public String getObject() {
                    return getTitleModel().getObject() + " - App"; // 全てのページのタイトルの末尾にアプリ名を付与する.
                }
            });
        }
        return this.titleLabel;
    }

    private Panel getHeaderPanel() {
        if (this.headerPanel == null) {
            this.headerPanel = this.createHeaderPanel("headerPanel");
        }
        return this.headerPanel;
    }
}
IndexPage.java
public class IndexPage extends AppPageBase {

    @Override
    protected IModel<String> getTitleModel() {
        return Model.Of("目次");
    }

    @Override
    protected Panel createHeaderPanel() {
        // 特に表示するものがない場合はEmptyPanelが利用できる
        return new EmptyPanel();
    }
}

推奨クラス構成

レイヤ構造

階層の基本は次の通り.

  <ブラウザ寄り>

     Pageクラス
        ↓
    Serviceクラス
        ↓
      Daoクラス

     <DB寄り>

大原則は 上のクラスは直下のクラスしか参照出来ない ということ.

クラスの種類

  • Pageクラス:WicketのPageクラス
  • Serviceクラス:ビジネスロジック
  • Daoクラス:DBアクセス担当. JPAを使う場合はEntityManagerがここに該当
  • Entityクラス:DBスキーマをマップ
  • Modelクラス:Entityでは表現出来ない状態を保持

Pageクラスが参照してよいクラス

  • Serviceクラス
  • Entityクラス
  • 他のPageクラス(相互参照は極力避ける)
  • Modelクラス

要はDaoクラス以外

Serviceクラスが参照してよいクラス

  • Entityクラス
  • 他のServiceクラス(相互参照は極力避ける)
  • Modelクラス

Daoクラスが参照してよいクラス

  • Entityクラス

WicketでのDIコンテナ

Guiceが楽.

AppWicketApplication.java
// GuiceによるDI機能をWicketに対して有効にする仕込み.
// これによりDaoBaseクラスを継承したクラスのpublicメソッドはトランザクションが自動ではられるようになる.
getComponentInstantiationListeners().add(new GuiceComponentInjector(this, DI.getGuiceInjector()));

なおGuiceによるトランザクション制御のサンプルは以下を参照のこと.
jabara-jpa-guice

25
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
24