XXXUtils の乱用をやめる - Calendar 編

標準のライブラリが使いにくいとき、つい XXXUtils のようなクラスを作ってしまいがちです。
例えば、悪名高き java.util.Calendar。来年の2月15日が何曜日か調べようと思ったら、次のようなコードを書かなければなりません。値の取得も設定も面倒で、なぜか月が0始まりになっています (歴史的な制約?)。

Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.YEAR, 1);
// 2月は month = 1
calendar.set(Calendar.MONTH, 1);
calendar.set(Calendar.DAY_OF_MONTH, 15);
// 文字列に変換する場合は、DateFormat を使用する。
DateFormat df = new SimpleDateFormat("EEEE");
String dayOfWeek = df.format(calendar.getTime());
System.out.println(dayOfWeek);  // => 月曜日


まず、よくやる対策は CalendarUtils のようなクラスを作る方法です。

public class CalendarUtils {

    public static int getYear(Calendar calendar) {
        return calendar.get(Calendar.YEAR);
    }

    public static void addYears(Calendar calendar, int years) {
        calendar.add(Calendar.YEAR, years);
    }

    public static void setMonth(Calendar calendar, int month) {
        calendar.set(Calendar.MONTH, month - 1);
    }

    public static void setDay(Calendar calendar, int day) {
        calendar.set(Calendar.DAY_OF_MONTH, day);
    }

    public static String getDayOfYearText(Calendar calendar) {
        return new SimpleDateFormat("EEEE").format(calendar.getTime());
    }

}

CalendarUtils を使うと、先ほどのコードは次のように書けます。

Calendar calendar = Calendar.getInstance();
CalendarUtils.addYears(calendar, 1);
// 2月は month = 2
CalendarUtils.setMonth(calendar, 2);
CalendarUtils.setDay(calendar, 15);
String dayOfWeek = CalendarUtils.getDayOfYearText(calendar);
System.out.println(dayOfWeek);  // => 月曜日

月が1始まりになり、コードも多少簡潔になりました。
この方法のメリットは、必要になったときに順次機能を追加できることです。
ただし、手続き的で、クラス名の装飾や、引数の calendar が冗長です。


そこで、最近はまっているのがラッパークラスを作る方法です。
Seasar2 付属の S2JDBC や、JavaScript ライブラリの jQuery でも採用されている方法です。
更新系のメソッドで this を戻り値で返しているのがミソです。

public class ISOCalendar {

    final Calendar calendar;

    public ISOCalendar() {
        this(Calendar.getInstance());
    }

    public ISOCalendar(Calendar calendar) {
        this.calendar = calendar;
    }

    public ISOCalendar addYears(int years) {
        calendar.add(Calendar.YEAR, years);
        return this;
    }

    public ISOCalendar setMonth(int month) {
        calendar.set(Calendar.MONTH, month - 1);
        return this;
    }

    public ISOCalendar setDay(int day) {
        calendar.set(Calendar.DAY_OF_MONTH, day);
        return this;
    }

    public String getDayOfWeekText() {
        return new SimpleDateFormat("EEEE").format(calendar.getTime());
    }

}

このクラスを使うと、先ほどのコードがさらに簡潔になります。

String dayOfWeek = new ISOCalendar()
    .addYears(1)
    .setMonth(2)
    .setDay(15)
    .getDayOfWeekText();
System.out.println(dayOfWeek);  // => 月曜日

IDE だと補間も効くので、ほんとサクサクコードが書けます。
この方法は、拡張が少し面倒なので、あらかじめある程度の機能を作りこんでおく必要があるのですが、一度作ってしまうと、開発がずいぶん楽になると思います。
というわけで、いま実際に作成している Calendar のラッパークラスです。
ISOCalendar.java

参考