匿名内部クラスについて
Javaには、匿名内部クラス というものがあります.
これは、クラス(またはインタフェース)を継承したクラスを作成すると同時に、オブジェクトを生成するものです.
次に例を示します.
Object o = new Object() {
public Strign toString() {
return "匿名内部クラスのオブジェクトです.";
}
};
new Object() {}
の、中括弧({})がポイントです.
これを付けることで、Objectクラスを継承したクラスを作ったことになり、それをnewしているのでオブジェクトを生成していることになります.
匿名内部クラスはWicketを使うときには頻出するため、ぜひ慣れておきましょう.
下記のサイトで構文と効果を学習しておきましょう.
[匿名内部クラス] (http://java.keicode.com/lang/anonymous-inner-class.php)
Pageクラス実装基礎
コンポーネントの初期化
Pageクラスには、通常、画面を動的に編集するためのコンポーネントが載ります.
コンポーネントを使うには、次のようなスタイルでコーディングすることをおすすめします.
- コンポーネントはインスタンス変数に宣言
- getterを作る
- getterの中は遅延初期化
- コンポーネントのインスタンスが必要なときは、必ずgetterを通して得る(そのインスタンスのgetterの中は例外)
サンプルを示します
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を返すような抽象メソッドを作ることで対処します.
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;
}
}
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が楽.
// GuiceによるDI機能をWicketに対して有効にする仕込み.
// これによりDaoBaseクラスを継承したクラスのpublicメソッドはトランザクションが自動ではられるようになる.
getComponentInstantiationListeners().add(new GuiceComponentInjector(this, DI.getGuiceInjector()));
なおGuiceによるトランザクション制御のサンプルは以下を参照のこと.
jabara-jpa-guice