Я создал в DayDate абстрактный метод с именем getDayOfWeekForOrdinalZero и реализовал его в SpreadsheetDate так, чтобы он возвращал Day.SATURDAY. Затем я переместил метод getDayOfWeek наверх по цепочке в DayDate и изменил его так, чтобы в нем вызывались методы getOrdinalDay и getDayOfWeekForOrdinalZero.
public Day getDayOfWeek() {
Day startingDay = getDayOfWeekForOrdinalZero();
int startingOffset = startingDay.index - Day.SUNDAY.index;
return Day.make((getOrdinalDay() + startingOffset) % 7 + 1);
}
Заодно присмотритесь к комментарию в строках с 895 по 899. Так ли необходимо это повторение? Как и в предыдущих случаях, я удалил этот комментарий вместе со всеми остальными.
Переходим к следующему методу compare (строки 902–913). Уровень абстракции этого метода снова выбран неправильно [G6], поэтому я поднял его реализацию в DayDate. Кроме того, его имя недостаточно содержательно [N1]. В действительности этот метод возвращает промежуток в днях, начиная с аргумента, поэтому я переименовал его в daysSince. Также я заметил, что для этого метода нет ни одного теста, и написал их.
Следующие шесть функций (строки 915–980) представляют собой абстрактные методы, которые должны реализовываться в DayDate. Я извлек из SpreadsheetDate.
Последнюю функцию isInRange (строки 982–995) также необходимо извлечь и переработать. Команда switch выглядит некрасиво [G23]; ее можно заменить, переместив условия в перечисление DateInterval.
public enum DateInterval {
OPEN {
public boolean isIn(int d, int left, int right) {
return d > left && d < right;
}
},
CLOSED_LEFT {
public boolean isIn(int d, int left, int right) {
return d >= left && d < right;
}
},
CLOSED_RIGHT {
public boolean isIn(int d, int left, int right) {
return d > left && d <= right;
}
},
CLOSED {
public boolean isIn(int d, int left, int right) {
return d >= left && d <= right;
}
};
public abstract boolean isIn(int d, int left, int right);
}
public boolean isInRange(DayDate d1, DayDate d2, DateInterval interval) {
int left = Math.min(d1.getOrdinalDay(), d2.getOrdinalDay());
int right = Math.max(d1.getOrdinalDay(), d2.getOrdinalDay());
return interval.isIn(getOrdinalDay(), left, right);
}
Мы подошли к концу класса DayDate. Сейчас я еще раз пройдусь по всему классу и напомню, что было сделано.
Открывающий комментарий был слишком длинным и неактуальным; я сократил и доработал его [C2].
Затем все оставшиеся перечисления были выделены в отдельные файлы [G12].
Статическая переменная (dateFormatSymbols) и три статических метода (getMonthNames, isLeapYear, lastDayOfMonth) были выделены в новый класс с именем DateUtil [G6].
Абстрактные методы были перемещены на более высокий уровень абстракции, где они были более уместными [G24].
Я переименовал Month.make в Month.fromInt [N1] и проделал то же самое для всех остальных перечислений.
Для всех перечислений был создан метод доступа toInt(), а поле index было объявлено приватным.
В plusYears и plusMonths присутствовало дублирование кода [G5], которое мне удалось устранить введением нового метода correctLastDayOfMonth. При этом код всех трех методов стал более понятным.
«Волшебное число» 1 [G25] было заменено соответствующей конструкцией Month.JANUARY.toInt() или Day.SUNDAY.toInt(). Я потратил некоторое время на доработку класса SpreadsheetDate и чистку алгоритмов. Конечный результат представлен в листингах с Б.7 (с. 442) по Б.16 (с. 451).
Интересно заметить, что покрытие кода в DayDate
Заключение