The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:
Я обязан своей достаточно высокой репутацией на Stack Overflow (ссылка) в основном этому вопросу, который я задал несколько лет назад: Как распечатать дату ISO 8601 на Java? С тех пор он собрал много голосов “за” и более 20 ответов, включая мой собственный. Почему же в Java, такой развитой экосистеме, не было встроенного простого решения для этой примитивной задачи? Я считаю, что это связано с тем, что разработчики SDK Java были 1) достаточно умными, чтобы не создавать метод print()
непосредственно в классе Date
, и 2) недостаточно умными, чтобы предоставить нам расширяемый набор классов и интерфейсов для разбора и печати дат элегантным способом.
По моим данным, существуют в основном три способа разделения ответственности между разбором и печатью в JDK:
Первый случай - когда что-то отвечает за печать и разбор, в то время как объект является просто хранилищем данных. Существует класс SimpleDateFormat
, который должен быть настроен сначала с правильным часовым поясом и шаблоном форматирования. Затем его следует использовать для печати:
Для его разбора обратно существует метод parse()
:
Это классическое сочетание объекта DTO и утилитного класса. DTO - это объект Date
, а утилитный класс - SimpleDateFormat
. Объект-дата предоставляет все необходимые атрибуты данных через несколько методов получения, а утилитный класс печатает дату. Объект-дата не оказывает влияния на этот процесс. Это на самом деле не объект, а просто контейнер данных. Это совсем не объектно-ориентированное программирование.
В Java 8 был добавлен класс Instant
с методом toString()
, который возвращает время в формате ISO-8601:
Для его разбора обратно существует статический метод parse()
в том же классе Instant
.
Данный подход выглядит более объектно-ориентированным, но проблема заключается в том, что невозможно изменить шаблон печати любым способом (например, удалить миллисекунды или полностью изменить формат). Более того, метод parse()
является статическим, что означает отсутствие полиморфизма - мы не можем изменить логику разбора. Также мы не можем изменить логику печати, поскольку Instant
является финальным классом, а не интерфейсом.
Такой дизайн кажется приемлемым, если все, что нам нужно, это строки даты/времени ISO 8601. В момент, когда мы решим расширить его каким-либо образом, мы столкнемся с проблемами.
В Java 8 также есть DateTimeFormatter
, который вводит третий способ работы с объектами даты/времени. Чтобы напечатать дату в виде строки, мы создаем экземпляр “форматтера” и передаем его объекту времени.
Для обратного анализа нам необходимо отправить formatter
в статический метод parse()
, вместе с текстом для анализа.
Как они общаются, LocalDateTime
и DateTimeFormatter
? Объект времени является TemporalAccessor
с методом get()
, позволяющим извлекать то, что находится внутри. Другими словами, снова DTO. Форматер все еще является утилитарным классом (даже не интерфейсом), который ожидает прибытие DTO, извлекает то, что находится внутри, и печатает.
Как они разбираются? Метод parse()
считывает шаблон, строит и возвращает другой TemporalAccessor
DTO.
А что насчет инкапсуляции? “На этот раз нет,” говорят разработчики JDK.
Вот как я бы его спроектировал. Сначала я создал бы общий неизменяемый Template
с таким интерфейсом:
Он будет использоваться таким образом:
Этот шаблон внутренне определяет, как распечатать поступающие данные, в зависимости от инкапсулированного шаблона. Вот как Date
смог бы распечатать себя:
Так работает разбор (в целом это не очень хорошая идея помещать код в конструктор, но для этого эксперимента это приемлемо):
Допустим, мы хотим вывести время в формате “13-е января 2019 года” (на русском языке). Как мы можем это сделать? Мы не создаем новый Template
, а несколько раз декорируем существующий. Сначала создаем экземпляр того, что у нас есть:
Этот текст будет выглядеть примерно так:
Date
не отправляет значение MMMM
в него, поэтому он не заменяет текст правильно. Нам нужно его украсить:
Теперь, чтобы получить русскую дату из объекта Date
, мы делаем следующее:
Допустим, мы хотим вывести дату в другом часовом поясе. Мы создаем еще один декоратор, который перехватывает вызов с помощью "HH"
и вычитает (или добавляет) разницу во времени.
Этот код будет выводить Московское время (UTC+3) на русском языке.
Мы можем украшать настолько, насколько нам нужно, делая Template
настолько мощным, насколько это необходимо. Элегантность этого подхода заключается в том, что класс Date
полностью отделен от Template
, что делает их как заменяемыми, так и полиморфными.
Может быть, кого-то заинтересует создание библиотеки печати и разбора даты и времени с открытым исходным кодом для Java с учетом этих принципов?
Translated by ChatGPT gpt-3.5-turbo/42 on 2023-12-17 at 16:10