#5 Javaプログラミングデザイン考3
Javaプログラミングにおける定数定義の目的
今回は、かなり意見が分かれるであろう定数の定義方法について切り込んでいきたいと思います。まずは定数を定義する意義について考えてみましょう。一般的に定数を定義する目的は以下のようなものが考えられます。
DBの値をプログラム中に直書きすることの回避策
プログラムのあちこちに数値の1, 2, 3 であったり、”SAMPLE”など固定の名称が記述されていたとしたら、いざ該当する値に変更が発生した場合の改修工数は無駄に膨れ上がります。それを回避するための最も一般的と言える定数定義を用いる理由です。
同一属性の情報を一元管理する
例えば日付属性のデータに対しての書式をいくつか用意する必要がある場合であったり、他サイトへ接続する時のURLであったり、同一属性で複数存在する固定値となる情報を一元的に管理するクラスとして定数クラスを用いる場合もあります。
同値を繰り返し定義させないための措置
そして定数定義をする最も大きな理由としてはこれがあげられるでしょう。
リソースの無駄遣いとも言える同値の定義が散在する状況を撲滅する意味合いは決して小さいものではないと思います。
・・・とは言え、とは言え(このぼやきは後述します)
何でもかんでも放り込んでいい所ではない
これは実体験に基づく話ですが、おそらくどのプロジェクトでも定数定義を一元管理するクラスは「Const.java」「Constant.java」という名称でプロジェクト配下にひとつクラスが存在するのではないでしょうか。
とある案件で遭遇したConst.javaですが、選択肢の値から始まり「定数・固定値」と判断したものはすべてひとつの定数クラス内に格納したため、static final で定義された変数が実に4000を超える巨大なものに。あまりに巨大になりすぎて管理不能となったため実態調査をしたところ、同値の定義がクラス内で8つも発見されたというお粗末な事態になっておりました。場合によっては情報の属性に応じた定数クラスの分割も必要であることが学べた事象でした。
どこから直していけばよいか
ではどの様なところから直していけばよいのか。まず着手するのは定数定義クラスそのものからです。
選択肢1個ずつ定義するのは Java1.4 以前のやり方
J2SE5.0からリリースされたenum (列挙型)クラス。これを使えばひとつの変数ですべての選択肢は定義できます。ちなみにJ2SE5.0がリリースされたのが2004年9月なので、選択肢1個ずつ定義の手法は今から17年以上前にさかのぼる「真正のレガシー」な手法と言えます。
// 削除フラグ
// DB上で0, 1で管理しているのであれば、各要素のordinalメソッドで比較
public enum DELETE_FLG {
ON
, OFF
}
ひとつの機能に特化した定義はそちらに移譲する
例えば日付を指定書式で成型する処理はシステム全体で利用する可能性が非常に高い処理と言えます。であれば、ユーティリティクラスとして作成し、システム全体で日付関連の処理を任せましょう。当然フォーマットの定義を使うのはそのユーティリティクラスだけになります。なので日付書式の定数定義はそのユーティリティクラスへ移譲すればよいのです。
**
* カレンダーユーティリティ.
* 日付関連のユーティリティクラス.
*
* @author Junji Yamaguchi(DLYMJ)
*/
public class CalendarUtil {
/** 日付フォーマット YYYYMMDD */
public static final String DATEFORMAT_YYYYMMDD = "yyyyMMdd";
/** 日付フォーマット スラッシュ区切り YYYY/MM/DD */
public static final String DATEFORMAT_YYYYMMDD_S = "yyyy/MM/dd";
/** 日付フォーマット ハイフン区切り YYYY-MM-DD */
public static final String DATEFORMAT_YYYYMMDD_H = "yyyy-MM-dd";
/** 時刻フォーマット HH:mm:ss */
public static final String TIMEFORMAT_HHMMSS = "HH:mm:ss";
/** 時刻フォーマット HH:mm:ss.SSS */
public static final String TIMEFORMAT_HHMMSSSSS = "HH:mm:ss.SSS";
/**
* 日付を指定のフォーマットで成型する.
*
* @param date 日付
* @param format 日付フォーマット
* @return フォーマットした日付文字列
*/
public static String formatDate(Date date, String format) {
SimpleDateFormat sdf = new SimpleDateFormat(format);
return sdf.format(date);
}
/**
* 日付時刻を指定のフォーマットで成型する.
*
* @param time 日付時刻
* @param dateFormat 日付フォーマット
* @param timeFormat 時刻フォーマット
* @return 成型した日付時刻文字列
*/
public static String formatDateTime(
Timestamp time, String dateFormat, String timeFormat) {
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat + "" + timeFormat);
return sdf.format(time);
}
...
共通化推進は良いがしすぎるのはいかがなものか
例えばテーブルの項目でよくある「停止フラグ」「削除フラグ」といったもので、ON/OFFで示すような項目の場合、格納されるのは同じ値、同じ種類数、ほぼ同じ意味になりますが、これを「FLAG_ON/FLAG_OFF」みたいな定数で共通化してしまうと、意味の理解に遅れが生じてしまうことになりかねません。利用目的の異なる項目は一緒くたにすべきではないでしょうね。
定数クラスの問題については、現役エンジニアの中でも問題と認識している人が多いようで、結構な数の記事が見つかります。参考までの検索結果を転載しておきます。
定数・固定値の定義方法については、エンジニア各位で思うところが色々あるお題だと思いますので、また別の機会にもう一度取り上げてみたいと思います。今回はここまでといたします。
今回のまとめ
定数・固定値 (Const) クラスを使い一元管理する
但し何でもかんでも定数クラスに放り込んでよい訳ではない
フラグのON/OFFや選択肢はenumクラスで一括に定義
ひとつの機能に特化した定数はそのクラスに定義するのもアリ
共通化するのはよいがしすぎるのはよくない