エラー内容の流し方
J2EEで開発しているプロジェクトで,EJB層で検出したエラーチェック内容をvalue objectのプロパティとして流しているのを見かけた.問題点と改善案を示してみる.
まずは現状の説明から.EJB層からの戻り値となるvalue objectでは,EJB層で検出するひとつのエラーに対して,ひとつの~Flgというvalue objectのプロパティを定義している.
public class FooResult {
private boolean hogeCodeErrorFlg;
private boolean gehoCodeErrorFlg;
public boolean getHogeCodeErrorFlg() {
return hogeCodeErrorFlg;
}
public void setHogeCodeErrorFlg(boolean value) {
this.hogeCodeErrorFlg = value;
}
public boolean getGehoCodeErrorFlg() {
return gehoCodeErrorFlg;
}
public void setGehoCodeErrorFlg(boolean value) {
this.gehoCodeErrorFlg = value;
}
}
EJB層ではメソッドの戻り値として返すFooResultに対して,エラーがあった場合はそれぞれのプロパティにfalseを,なかった場合にはtrueを設定している.
public FooResult foo(...) throws Exception {
FooResult result = new FooResult();
if (validateHogeCode(...)) {
result.setHogeCodeErrorFlg(true);
} else {
result.setHogeCodeErrorFlg(false);
}
if (validateGehoCode(...)) {
result.setGehoCodeErrorFlg(true);
} else {
result.setGehoCodeErrorFlg(false);
}
/// do something...
return result;
}
JSP/Servlet層では,このvalue objectのget~Flg()をそれぞれ調べて,falseの場合はエラーを表示している.以下の例ではstrutsのタグを使ってエラーを表示している.
<logic:equal name="fooResult" property="hogeCodeErrorFlg" value="false">
<%= "hogeコードがおかしいですよ." %>
</logic:equal>
<logic:equal name="fooResult" property="gehoCodeErrorFlg" value="false">
<%= "gehoコードがおかしいですよ." %>
</logic:equal>
このコードにはいくつかの問題点がある.
まず大きな問題として,エラーの追加という変更に対して非常に弱いということである.EJB層のfoo()メソッドで行うエラーチェックをひとつ増やすと,上で示した3つのコードのすべての箇所で修正が必要である.データ・ロジック・表示の修正が必要と,まさにダメ三冠王である.例えばFooResultではエラー内容をListなどに格納しておき,JSP/Servlet層ではそのListの中身を順番に表示する,といった形にすればデータと表示に関してはエラーの追加による影響を減らすことができる.
次に気になるのはプロパティ名である.boolean型のプロパティ名に~Flg,~Flagなどと名づけるのは基本的にはご法度である.hogeCodeErrorFlgといわれても,エラーがあった場合の値がtrueなのかfalseなのかわからない.実際私は上のコードを書いていて何度かtrueとfalseを間違えてしまった.boolean型のプロパティ名は,is~,has~,can~のどれかの名前にしないといけない,と思っておけば問題ない.例えばisValidHogeCodeやisNotValidHogeCodeといったプロパティ名にしておけば,エラーがあった場合の値がtrueなのかfalseなのかはプロパティ名だけから推測できる.
改善案の一例を以下に示しておく.エラー情報保持用のValidationErrorクラスを定義し,FooResultがリストでエラーを保持できる形にする.なお,この例では簡単のため,エラーメッセージをValidateionErrorのプロパティとして持たせているが,idとエラーの原因となった入力値だけをプロパティに持たせておいて,JSP/Servlet層でそれらの値からメッセージを組み立てるようにした方がよいこともある.
/// なにかしらのエラー情報を持つクラス
public class ValidationError {
/// エラーの種別を表すID
private String id;
/// エラーメッセージ
private String message;
public ValidationError(String id, String message) {
this.id = id;
this.message = message;
}
public String getId() {
return id;
}
public String getMessage() {
return message;
}
}
/// エラー情報をリストで保持
public class FooResult {
private List<ValidationError> errors;
public FooResult() {
errors = new ArrayList<ValidationError>();
}
public List<ValidationError> getErrors() {
return errors;
}
}
EJB層では,エラーがあったときにValidationErrorオブジェクトを生成して,FooResultのerrorsプロパティに追加する.
public FooResult foo(...) throws Exception {
FooResult result = new FooResult();
if (!validateHogeCode(...)) {
result.getErrors().add(
new ValidationError("error.isNotValidHogeCode", "hogeコードがおかしいですよ.");
);
}
if (!validateGehoCode(...)) {
result.getErrors().add(
new ValidationError("error.isNotValidGehoCode", "gehoコードがおかしいですよ.");
);
}
/// do something...
return result;
}
JSP/Servlet層では,FooResultのerrorsプロパティに格納されているValidationErrorのmessageプロパティを順次表示する.
<logic:iterate id="error" name="fooResult" property="errors">
<bean:write name="error" property="message"/>
</logic:iterate>
これでエラーチェックを追加する場合でも,EJB層のfoo()メソッドのコードの修正だけで対応できるようになる.