The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:
Несколько месяцев назад я создал небольшую библиотеку на Java, которую стоит объяснить, так как дизайн ее классов и интерфейсов довольно необычен. Она очень объектно-ориентирована для довольно императивной задачи: создания конвейера преобразования документов. Цель состояла в том, чтобы делать это декларативным и неизменяемым способом, и на Java. Ну, насколько это возможно.
Предположим, у вас есть документ, и у вас есть коллекция преобразований, каждое из которых будет что-то делать с документом. Каждое преобразование, например, является небольшим куском кода на Java. Вы хотите создать список преобразований и затем пропустить документ через этот список.
Сначала я создал интерфейс Shift
(вместо часто используемого и скучного “преобразования”):
Затем я создал интерфейс Train
(это имя я придумал для совокупности преобразований) и его реализацию по умолчанию.
Ах, я забыл сказать тебе. Я большой поклонник неизменяемых объектов. Вот почему у Train
нет метода add
, а вместо него есть with
. Разница в том, что add
изменяет объект, а with
создает новый.
Теперь я могу создать поезд с перегрузками с помощью TrDefault
, простой реализации Train
, предполагая, что ShiftA
и ShiftB
уже реализованы.
Затем я создал класс Xsline
(это “XSL” + “pipeline”, поскольку в моем случае я управляю XML-документами и преобразую их с помощью таблиц стилей XSL). Экземпляр этого класса инкапсулирует экземпляр Train
и затем проходит документ через все его преобразования:
So far so good.
Теперь я хочу, чтобы все мои преобразования записывались в журнал. Я создал StLogged
, декоратор Shift
, который инкапсулирует исходный Shift
, декорирует его метод apply
и выводит сообщение в консоль, когда преобразование завершено.
Сейчас мне нужно сделать это:
Похоже на дублирование new StLogged(
, особенно с коллекцией из нескольких десятков сдвигов. Чтобы избавиться от этого дублирования, я создал декоратор для Train
, который динамически декорирует сдвиги, которые он инкапсулирует, используя StLogged
.
В моем случае все сдвиги выполняют преобразования XSL, беря таблицы стилей XSL из файлов, доступных в classpath. Поэтому код выглядит так:
Есть явное дублирование new StXSL(...)
, но я не могу просто избавиться от него, так как метод with
ожидает экземпляр Shift
, а не String
. Чтобы решить эту проблему, я сделал Train
обобщенным и создал декоратор TrClasspath
:
TrClasspath.with()
accepts String
, turns it into StXSL
and passes to TrDefault.with()
.
Обратите внимание на приведенный выше фрагмент: train
теперь имеет тип Train<String>
, а не Train<Shift>
, как требуется Xsline
. Вопрос теперь в том, как вернуться к Train<Shift>
?
Ах, я забыл упомянуть. Я хотел разработать эту библиотеку с одним важным принципом, предложенным в 2014 году: все объекты могут реализовывать только методы своих интерфейсов. Вот почему я не мог просто добавить метод getEncapsulatedTrain()
к TrClasspath
.
Я ввел новый интерфейс Train.Temporary<T>
с единственным методом back()
, возвращающим Train<T>
. Класс TrClasspath
его реализует, и я могу сделать так:
Затем я решил избавиться от дублирования вызовов .with()
. Очевидно, что было бы проще иметь возможность предоставить список имен файлов в виде массива String
и создать поезд из него. Я создал новый класс TrBulk
, который именно это делает:
С помощью этого дизайна я могу почти любым способом конструировать поезд.
Вот, например, как мы его используем здесь и здесь.
Translated by ChatGPT gpt-3.5-turbo/42 on 2023-12-05 at 21:40