前回のエントリが
あまりにもマニアックでアクセスが全然伸びないのですが(苦笑)どういうときにそこにハマるのかについてフォローしておきます。
外部とのコンバータ
HTTPなどもそうなのですが、要するにJavaの外部の世界とのやりとりなどをすると、
オブジェクトの型がなかったり失われていたりします。
こういう時に型を導出するコンバータのようなものが必要になることがあります。
HTTPあたりでイメージしましょうか。ある入力フィールドは数値に変換する必要があり、
ある入力フィールドは文字列、ある入力フィールドは日付だとしましょう。
するとコンバータのクラスは
/** Integerへのコンバータ */
public class IntegerConverter {
public Integer convert(String in) {
return Integer.valueOf(in);
}
}
/** 文字列へのコンバータ */
public class StringConverter {
public String convert(String in) {
return in;
}
}
/** 日付へのコンバータ */
public class DateConverter {
public Date convert(String in) {
DateFormat formater = new SimpleDateFormat("yyyy/MM/dd");
try {
return formater.parse(in);
} catch (ParseException e) {
// 面倒なんでnullを返す仕様にしておく
return null;
}
}
}
ってな具合になるのですが、これらを一意に扱うために親のクラスを用意します。
/** 抽象的なコンバータ */
public abstract class AbstractConverter <T> {
/** 文字列からT型への変換処理 */
public abstract T convert(String in);
}
この抽象クラスを用いて先のコンバータを書きなおすと
/** Integerへのコンバータ */
public class IntegerConverter extends AbstractConverter<Integer> {
public Integer convert(String in) {
return Integer.valueOf(in);
}
}
/** 文字列へのコンバータ */
public class StringConverter extends AbstractConverter<String>{
public String convert(String in) {
return in;
}
}
/** 日付へのコンバータ */
public class DateConverter extends AbstractConverter<Date> {
public Date convert(String in) {
DateFormat formater = new SimpleDateFormat("yyyy/MM/dd");
try {
return formater.parse(in);
} catch (ParseException e) {
// 面倒なんでnullを返す仕様にしておく
return null;
}
}
}
というような継承の仕方になります。
コンバータなんてSingletonでいいんじゃない?
内部に状態を持たないステートレスなオブジェクトは毎回毎回インスタンスを生成する必要がありません。
というわけで、Singletonとか、staticフィールドに作っておけばいいじゃないかという話になります。
/** 抽象的なコンバータ */
public abstract class AbstractConverter <T> {
/** コンストラクタをprivateにして継承できなくしてしまう */
private AbstractConverter() {}
/** 文字列からT型への変換処理 */
public abstract T convert(String in);
/** Integerへのコンバータ */
public static final AbstractConverter<Integer> INTEGER_CONVERTER =
new AbstractConverter<Integer>() {
public Integer convert(String in) {
return Integer.valueOf(in);
}
};
/** 文字列へのコンバータ */
public static final AbstractConverter<String> STRING_CONVERTER =
new AbstractConverter<String>() {
public String convert(String in) {
return in;
}
};
/** 日付へのコンバータ */
public static final AbstractConverter<Date> DATE_CONVERTER =
new AbstractConverter<Date>() {
public Date convert(String in) {
DateFormat formater = new SimpleDateFormat("yyyy/MM/dd");
try {
return formater.parse(in);
} catch (ParseException e) {
// 面倒なんでnullを返す仕様にしておく
return null;
}
}
};
}
そして、これがJava5.0以前で列挙を表現するためのTypesafeEnumパターンのバリエーションになっていますから、
Java5.0からの列挙型にしてしまおうと考えると、実はかなり無理があって変換の実装をStrategyパターンとして
Enumのコンストラクタで受け取る形になってしまう。
public enum Converter {
INTEGER(AbstractConverter.INTEGER_CONVERTER),
STRING(AbstractConverter.STRING_CONVERTER),
DATE(AbstractConverter.DATE_CONVERTER)
;
/** 変換用のコンバータ。Strategyパターン */
private AbstractConverter<?> conv;
/** Enumのコンストラクタ */
private Converter(AbstractConverter<?> conv) {
this.conv = conv;
}
/** 変換処理。内部のStrategyに委譲 */
public Object convert(String in) {
return this.conv.convert(in);
}
}
JavaのEnumはジェネリクスの型パラメータを持てないのでEnumに定義したメソッドの戻りがジェネリクスで指定できず、
Object型になっちゃってますね。
Converterを外部にさらす方法にしても、
/** 変換用Strategyを取得 */
public AbstractConverter<?> getConvert() {
return this.conv;
}
というように、結局型が安全にならない。
どうにかならないかで試していて発見したのが前回のエントリのローカルなジェネリクス型パラメータを
用いた際の矛盾なのです。
こういうクラスはEnumにしようと思っちゃいけない。というか、Enumを必要以上に拡張して使おうとしてはいけないのでしょうかね。
投稿日時 : 2008年2月27日 20:10