Builder pattern в js-joda

В библиотеке js-joda неизменяемость (immutability) является фундаментальным принципом проектирования. Любая операция над датой или временем не модифицирует исходный объект, а возвращает новый. В таких условиях классический подход «создать объект и постепенно наполнять его полями» невозможен в прямом виде. Вместо этого используется функционально-билдерный стиль, в котором состояние накапливается через цепочки преобразований.

Builder-подход в js-joda проявляется не как единый класс Builder, а как совокупность API, поддерживающих пошаговое конструирование через цепочки вызовов, корректировки и композицию временных объектов.


Иммутабельность как основа построения объектов

Каждый временной тип в js-joda — LocalDate, LocalTime, LocalDateTime, ZonedDateTime — является неизменяемым. Любая модификация приводит к созданию нового экземпляра.

const date1 = LocalDate.of(2026, 1, 10);
const date2 = date1.withMonth(5);

Здесь date1 остаётся неизменным, а date2 представляет новый объект.

Такой подход естественным образом формирует «builder-like» поведение: объект постепенно трансформируется через последовательность шагов.


Цепочки вызовов как форма builder-паттерна

Наиболее распространённая форма построения сложных значений — fluent API, где каждый вызов возвращает новый экземпляр с изменённым состоянием.

Коррекция полей через with

Методы семейства with являются базовыми строительными блоками:

const base = LocalDate.of(2026, 1, 1);

const updated = base
  .withYear(2027)
  .withMonth(12)
  .withDayOfMonth(25);

Каждый шаг формирует промежуточный объект, что концептуально эквивалентно builder, который поэтапно устанавливает поля.


Арифметика времени: plus и minus

Дополнительный уровень построения — смещение временных значений:

const time = LocalDateTime
  .of(2026, 5, 1, 10, 30)
  .plusDays(10)
  .minusHours(3);

Здесь происходит не просто установка значений, а накопление изменений, что усиливает builder-ориентированную модель.


Нормализация состояния через промежуточные шаги

Важная особенность js-joda заключается в автоматической нормализации значений. При «построении» даты через последовательные операции система сама корректирует выход за границы диапазонов:

const dt = LocalDateTime
  .of(2026, 1, 31, 23, 0)
  .plusMonths(1);

Результат учитывает различную длину месяцев и корректирует итоговое значение без ручного управления состоянием.


Builder-подобная композиция с периодами и длительностями

Отдельный слой построения временных значений реализуется через Period и Duration. Эти структуры выступают как конфигурируемые строительные блоки.

Period как декларативный строитель времени

const period = Period.ofYears(1).withMonths(2).withDays(10);
const result = LocalDate.of(2026, 1, 1).plus(period);

Здесь Period выступает как промежуточный builder, накапливающий параметры до применения к дате.


Duration как строитель временных интервалов

const duration = Duration.ofHours(5).plusMinutes(30);
const result = LocalTime.of(10, 0).plus(duration);

Состояние Duration формируется пошагово и затем применяется к временной сущности.


TemporalAdjuster как функциональный builder

Одним из наиболее выразительных механизмов builder-подхода является TemporalAdjuster. Это функциональный объект, который трансформирует дату по заданному правилу.

const nextMonday = TemporalAdjusters.next(DayOfWeek.MONDAY);

const result = LocalDate.of(2026, 5, 25).with(nextMonday);

Здесь сам adjuster выступает как инкапсулированный строитель логики изменения даты. Он не хранит результат, но описывает шаг трансформации.


DateTimeFormatterBuilder как классический Builder

Наиболее прямое воплощение паттерна Builder в js-joda реализовано в модуле форматирования через DateTimeFormatterBuilder.

Он позволяет поэтапно собирать форматтер, добавляя элементы форматирования в цепочке вызовов.

const formatter = new DateTimeFormatterBuilder()
  .appendLiteral('Дата: ')
  .appendValue(ChronoField.DAY_OF_MONTH)
  .appendLiteral('.')
  .appendValue(ChronoField.MONTH_OF_YEAR)
  .appendLiteral('.')
  .appendValue(ChronoField.YEAR)
  .toFormatter();

Каждый вызов расширяет внутреннее описание форматтера, формируя сложную структуру без необходимости вручную управлять конфигурацией.


Условная логика внутри builder

Builder форматтера поддерживает ветвления, что делает его более мощным по сравнению с простыми цепочками:

const formatter = new DateTimeFormatterBuilder()
  .appendOptional(
    new DateTimeFormatterBuilder()
      .appendLiteral('ISO: ')
      .appendInstant()
  )
  .toFormatter();

Такая структура позволяет собирать гибкие правила форматирования без дублирования кода.


Композиция builder-структур

Одной из характерных особенностей js-joda является возможность комбинировать разные уровни builder-подобных конструкций:

  • DateTimeFormatterBuilder формирует формат вывода
  • Period и Duration формируют временные смещения
  • with и plus/minus формируют итоговые значения
const period = Period.ofMonths(6).withDays(15);

const formatter = new DateTimeFormatterBuilder()
  .appendValue(ChronoField.YEAR)
  .appendLiteral('-')
  .appendValue(ChronoField.MONTH_OF_YEAR)
  .toFormatter();

const date = LocalDate.of(2026, 1, 1)
  .plus(period)
  .withDayOfMonth(10);

Каждый слой выполняет роль отдельного builder-а в общей системе построения временных вычислений.


Ленивая природа построения значений

Builder-подход в js-joda тесно связан с отложенными вычислениями. Хотя цепочки вызовов выглядят последовательными, реальные вычисления происходят на каждом шаге создания нового объекта, а не в момент определения цепочки.

Это обеспечивает:

  • предсказуемость результатов
  • отсутствие скрытого состояния
  • возможность безопасной композиции операций

Функциональная модель вместо классического состояния

В отличие от классического builder-паттерна, где объект мутирует внутри билдера до вызова build(), js-joda использует функциональный эквивалент:

  • каждый шаг = новая версия объекта
  • отсутствует промежуточное изменяемое состояние
  • «сборка» выражается через цепочку преобразований
const result = LocalDate.of(2026, 1, 1)
  .withMonth(3)
  .plusDays(10)
  .withYear(2027);

Такой стиль ближе к функциональному программированию, чем к классическим объектным конструкциям, но сохраняет структуру builder-подхода.


Итоговая архитектурная роль builder-подхода

Builder-паттерн в js-joda не является единым механизмом. Он распределён по нескольким слоям API и проявляется через:

  • fluent-интерфейсы временных типов
  • объекты периодов и длительностей
  • функциональные корректоры (TemporalAdjuster)
  • специализированные builder-классы форматирования

Эта модель обеспечивает единый принцип: сложные временные структуры формируются постепенно, через композицию малых неизменяемых операций.