日付の範囲クラスの作成(精度指定付き)-2

昨日のCalendarRangeを修正したバージョン。
switch-caseを使う代わりにenumにプロパティを持たせ、それを利用して初期化します。
(Effective-Java 2ndも出たことだし、と無理矢理ぎみに使ってみまんた)

// ベースは昨日の日記参照
// http://d.hatena.ne.jp/asc_gamefreak/20080609/1213016167

public class CalendarRange extends Range<Calendar> {

	// Scaleにクリアする精度と初期値を追加
	public static enum Scale {
		 YEAR(Calendar.MONTH, 0) //
		,MONTH(Calendar.DAY_OF_MONTH, 1) //
		,DAY(Calendar.HOUR_OF_DAY, 0) //
		,HOUR(Calendar.MINUTE, 0) //
		,MINUTE(Calendar.SECOND, 0) //
		,SECOND(Calendar.MILLISECOND, 0) //
		,MILLISECOND(null, 0);

		private Integer clearScale;
		private int initValue;

		private Scale(Integer clearScale, int initValue) {
			this.clearScale = clearScale;
			this.initValue = initValue;
		}
	}
	
	// 省略...
	
	private static Calendar adjustScale(Calendar original, Scale scale) {
		if (scale == null)
			throw new NullPointerException("scale must not be null.");
		if (original == null)
			return null;

		Calendar cal = (Calendar) original.clone();

		// switch-caseを使わずにScaleに渡したパラメータを使って初期化
		for (Scale comparison : Scale.values()) {
			if (scale.ordinal() <= comparison.ordinal()
					&& comparison.clearScale != null) {
				cal.set(comparison.clearScale, comparison.initValue);
			}
		}

		return cal;
	}

Enum#ordinal()メソッドは、定数を宣言した順に0起算の整数値を返してくれます。逆に言うと定数の定義順に依存して変ってしまう値なので、プログラマが定数に明確な順序を表す整数値を定義したいなら、ちゃんと専用のプロパティを用意してあげたほうが堅牢なコードになります。

// #ordinal() は定義順に依存する
enum Scale1 {
   YEAR  // YEAR. ordinal() => 0
  ,MONTH // MONTH.ordinal() => 1
  ,DAY;  // DAY.  ordinal() => 2
}

enum Scale2 {
   MONTH // MONTH.ordinal() => 0
  ,YEAR  // YEAR. ordinal() => 1
  ,DAY;  // DAY.  ordinal() => 2
}


// こっちのほうがプログラマが明確に順序を定義できる
enum Scale {
   YEAR(0)
  ,MONTH(1)
  ,DAY(2);

  private final int order;

  private Scale(int order) {
    this.order = order;
  }


というわけで今回のCalendarRangeも、本当のところはordinal()を使うのではなく個別にオーダー値を振ったほうが確実です。面倒なのでサボりましたが。



書くだけ書いてみたものの、昨日のswitch-caseのほうがよっぽど直感的な気がしました。
if(scale.ordinal() <= comparison.ordinal() ... とか何が言いたいんだかわからないコードです。