Одной из определяющих характеристик дивного нового мира Java является растущая распространенность шаблона компоновщика в пространстве Java . Groovy , который представляется наиболее популярным альтернативным языком (Java) в JVM, хорошо известен своим интенсивным использованием Builder как в основных библиотеках, так и в библиотеках и инфраструктурах, поддерживаемых Groovy. Джош Блох (Josh Bloch) выдвинул шаблон на передний план мышления сообщества разработчиков Java, рассмотрев шаблон в пункте № 2 второго издания своей влиятельной книги « Эффективная Java» . В JDK было добавлено несколько сборщиков, включая Locale.Builder в J2SE 1.7 . В этом посте я кратко представляю Calendar.Builder, приходящий к JDK 8 .
Сегодня разработчик Java обычно заполняет экземпляр класса Calendar либо
вызывая один из методов «set», который принимает длинный список содержимого для экземпляра, или вызывая отдельные методы «set» в экземпляре один за другим. Эти два типичных подхода к Calendar
экземпляра Calendar
демонстрируются в следующих двух листингах кода.
Заполнение календаря одним методом set
01
02
03
04
05
06
07
08
09
10
11
12
|
/** * Demonstrate pre-JDK 8 method of instantiating Calendar instance using * "set" method for main fields. */ public static void demoCalendarWithSingleSet() { final Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone(timeZoneId), ENGLISH); calendar.set( 2013 , APRIL, 6 , 15 , 45 , 22 ); out.println( "Calendar via Constructor: " + stringifyCalendar(calendar)); } |
Заполнение календаря несколькими отдельными «установленными» методами
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
/** * Demonstrate pre-JDK 8 method of instantiating Calendar instance using * individual "set" calls for each pair of field names and values. */ public static void demoCalendarWithIndividualSets() { final Calendar calendar = Calendar.getInstance( TimeZone.getTimeZone(timeZoneId), ENGLISH); calendar.set(YEAR, 2013 ); calendar.set(MONTH, APRIL); calendar.set(DATE, 6 ); calendar.set(HOUR, 3 ); calendar.set(MINUTE, 45 ); calendar.set(SECOND, 22 ); calendar.set(AM_PM, PM); out.println( "Calendar via set methods: " + stringifyCalendar(calendar)); } |
ПОБОЧНОЕ ПРИМЕЧАНИЕ: в обоих приведенных выше примерах я использовал еще одну популярную особенность современной Java: статический импорт . Такие константы, как ENGLISH , YEAR и SECOND , фактически статически импортируются из таких классов, как Locale и Calendar . Как я уже писал ранее , кажется, что статический импорт становится все более популярным среди разработчиков на Java, особенно в свете тенденции к свободным интерфейсам.
Два «традиционных» подхода, показанных выше, показывают разные способы заполнения экземпляра Calendar
. Одним из крайних случаев является установка каждого отдельного поля отдельно, а другим — установка всех значимых полей с помощью одного метода «set». У каждого подхода есть свои преимущества. В одном методе «set» меньше состояний «незавершенного» объекта, чем в подходе с множеством множеств, но подход с множеством множеств удобнее для чтения, поскольку имя устанавливаемого значения ясно на основе первого параметра каждого «множества» Метод. Подход с одним множеством немного громоздкий, потому что он требует шести целых чисел, которые можно легко перепутать в порядке передачи, потому что нет никакого очевидного способа определить, какое целое является каким, а не неявным порядком.
Calendar.Builder использует рекламируемые преимущества Builder, как описано в Bloch: устраняет существование «несовместимых состояний в процессе построения [объекта]». Это продемонстрировано в следующем листинге кода.
Calendar.Builder позволяет создавать отдельные инструкции с читаемыми настройками
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
/** * Demonstrate using JDK 8's Calendar.Builder to instantiate an instance of * Calendar using the set methods to set each field individually based on * field name and value. */ public static void demoCalendarWithCalendarBuilderSetFields() { final Calendar calendar = new Calendar.Builder() .set(YEAR, 2013 ) .set(MONTH, APRIL) .set(DATE, 6 ) .set(HOUR, 15 ) .set(MINUTE, 45 ) .set(SECOND, 22 ) .setTimeZone(TimeZone.getTimeZone(timeZoneId)) .setLocale(ENGLISH) .build(); out.println( "Calendar via Calendar.Builder 'set' Fields: " + stringifyCalendar(calendar)); } |
В приведенном выше листинге кода экземпляр Calendar
создается И заполняется в одном операторе, что устраняет необходимость подвергать объект риску несовместимости в нескольких операторах. В этом примере сохраняется удобочитаемость традиционного индивидуального подхода «set» [ set (int, int) ] с дополнительной безопасностью полного заполнения объекта сразу при создании экземпляра.
Для разработчиков, которые хотят предоставить меньше отдельных методов «set», еще одна возможность с Calendar.Builder
— использовать методы setDate (int, int, int) и setTimeOfDay (int, int, int), как показано в следующем листинге кода.
Calendar.Builder Установка даты и времени как два вызова
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
/** * Demonstrate using JDK 8's Calendar.Builder to instantiate an instance of * Calendar using the "setDate" and "setTimeOfDay" builder methods. */ public static void demoCalendarWithCalendarBuilderSetDaySetTime() { final Calendar calendar = new Calendar.Builder() .setDate( 2013 , APRIL, 6 ) .setTimeOfDay( 15 , 45 , 22 ) .setTimeZone(TimeZone.getTimeZone(timeZoneId)) .setLocale(ENGLISH).build(); out.println( "Calendar via Calendar.Builder setDate/setTimeOfDay: " + stringifyCalendar(calendar)); } |
В этом подходе меньше символов и строк для ввода, но он частично восстанавливает недостаток, заключающийся в большей вероятности непреднамеренного переключения целочисленного параметра, поскольку каждый из двух методов принимает три целых числа (или перегруженная версия setTimeOfDay()
будет принимать четвертое целое число, представляющее миллисекунды).
Для разработчиков, которым нужна максимальная гибкость в определении параметров Calendar
во время его создания, Calendar.Builder
предоставляет метод setFields (int…), который принимает произвольную длину пары целых чисел, причем первое целое число пары представляет поле, которое нужно установить, и второе целое число пары, представляющее значение для этого поля. Этот метод используется в следующем листинге кода.
Задание полей календаря с помощью метода setFields Calendar.Builder
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
/** * Demonstrate using JDK 8's Calendar.Builder to instantiate an instance of * Calendar using the setFields method that allows providing of Calendar * fields as key/value pairs. */ public static void demoCalendarWithCalendarBuilderSetPairs() { final Calendar calendar = new Calendar.Builder() .setFields(YEAR, 2013 , MONTH, APRIL, DATE, 6 , HOUR, 15 , MINUTE, 45 , SECOND, 22 ) .setTimeZone(TimeZone.getTimeZone( "timeZoneId" )) .setLocale(ENGLISH) .build(); out.println( "Calendar via Calendar.Builder setPairs: " + stringifyCalendar(calendar)); } |
Этот setFields(int ...)
повышает риск искажения порядка целых чисел, используемых для создания экземпляра нового экземпляра Calendar
, но использование статически импортированных констант Calendar
улучшает читабельность и снижает вероятность неправильного смешивания целых чисел. Если указано нечетное число целых чисел (что означает наличие неполной пары), генерируется исключение IllegalArgumentException .
Хотя Calendar.Builder
обеспечивает некоторое удобство при создании и заполнении экземпляра Calendar
, любой, кому посчастливится принять JDK 8, получит доступ к новому API даты / времени, поэтому можно задать вопрос: «Зачем использовать Calendar.Builder?» Возможно, лучший ответ заключается в том, что существуют миллионы строк существующего кода, библиотек и структур, использующих и ожидающих экземпляры Calendar
, поэтому, вероятно, пройдет много времени, прежде чем потребность в Calendar
полностью исчезнет (если вообще когда-либо). К счастью, Calandar.Builder
позволяет легко преобразовать экземпляр Instant (часть нового API данных / времени Java ) в Calendar
помощью CalendarBulder.setInstant (long) . Это продемонстрировано в следующем листинге кода.
Преобразование мгновенного в календарь с Calendar.Builder
01
02
03
04
05
06
07
08
09
10
11
12
13
|
/** * Demonstrate using JDK 8's Calendar.Builder to instantiate an instance of * Calendar based on "now" Instant. */ public static void demoCalendarWithCalendarBuilderInstant() { final Calendar calendar = new Calendar.Builder().setInstant(Instant.now().toEpochMilli()) .setTimeZone(TimeZone.getTimeZone(timeZoneId)) .setLocale(ENGLISH) .build(); out.println( "Calendar via Calendar.Builder and Instant: " + stringifyCalendar(calendar)); } |
Обратите внимание, что перегруженная версия метода setInstant
принимает Date
для создания экземпляра Calendar
. В обоих случаях, независимо от того, setInstant(long)
ли он с помощью setInstant(long)
или setInstant(Date)
, никакой другой метод «set» в Calender.Builder
должен вызываться, чтобы избежать исключения IllegalStateException .
Легко пойти в другом направлении (получить Instant
из Calendar
), используя Calendar.toInstant () . Другие методы, введенные в Calendar
с помощью JDK 1.8, связаны с предоставлением типа календаря текущего экземпляра (в виде строки) или набора доступных типов календаря (набора строк). Когда я запускаю Calendar.getAvailableCalendarTypes () в моей системе, я вижу эти три строки: « gregory », « japanese » и « buddhist » (те же три календаря, задокументированные в поддерживаемых календарях )
Вывод
Как и многие разработчики Java , я с нетерпением жду улучшенного API данных / времени Java, встроенного в стандартный Java Development Kit. Тем не менее, я также понимаю, что, особенно в больших базах кода и при использовании библиотек и сред, ожидающих Calendar
или Date
, я не буду свободен от Calendar
и Date
течение некоторого времени. Внедрение Calendar.Builder
в JDK 8 немного облегчает эту нагрузку.