自分でコントローラを作っていると、PathInfoから適応するクラスを選択する時にリフレクションを利用するか、if文連発で制御するしか頭になかった。けどやっぱり面倒なのでJavaあんまり分からないなりに、変換テーブル作って自動で対応するクラスをロードする機能を作ってみた。
目標としては、以下の3つ。
- 新しいページの追加が楽。
- リフレクションを使わない。
- 無駄にクラスをnewしない。
・インターフェース
public interface ActionInterface
{
public ViewInterface execute(HttpServletRequest request, Context context);
}
public interface ViewInterface
{
public void execute(HttpServletResponse response, VelocityContext context);
}
・実装
public class Navigation
{
/**
* コントローラの変換テーブルを実装します。
*/
public enum Table
{
Hoge {
@Override public ActionInterface getAction() { return new HogeAction(); }
@Override public String toString() { return "hoge"; }
},
Fuga {
@Override public ActionInterface getAction() { return new FugaAction(); }
@Override public String toString() { return "fuga"; }
},
Default {
@Override public ActionInterface getAction() { return new DefaultAction(); }
@Override public String toString() { return ""; }
};
/**
* 指定された列挙型に対応するActionインターフェースを返します。
* @return 対応するアクションのインターフェース
*/
public abstract ActionInterface getAction();
}
public static ActionInterface get(String[] pathInfo)
{
String first = "";
if(pathInfo.length >= 1)
{
first = pathInfo[0];
}
for(Table value : Table.values())
{
if(value.toString().equals(first))
{
return value.getAction();
}
}
return Table.Defalut.getAction();
}
}
あとはひたすら列挙体のTableに要素を追加していけば大丈夫そう。なんかテーブルの記述でいちいちインターフェース名を書くのが面倒・・・。ジェネリクスをうまく使ってあげればどうにかなりそうだけどとりあえずこのままで。実装に対しての突っ込みお待ちしております^^。
こうなる前は列挙体のコンストラクタの引数の型にActionInterfaceを指定して実装してみたけど、そうすると目標の「無駄にnewしない」に引っかかる動作になってしまったので断念。以下のコードがまさにそれです。
public enum Table
{
Hoge(new HogeAction()),
Huga(new FugaAction());
private ActionInterface action;
private Table(ActionInterface action)
{
this.action = action
System.out.println("Tabel." + name());
}
public ActionInterface getAction()
{
return this.action;
}
}
上のコードの"getAction()"をコールしたときの出力結果
Table.Hoge
Table.Huga
つまりTableの要素にアクセスした瞬間に列挙体のコンストラクタに指定したオブジェクトが全てインスタンス化されちゃうわけです。当たり前っていえば当たり前。