Как стать автором
Обновить
46
0
Сергей Егоров @bsideup

Пользователь

Отправить сообщение

Такой подход реализуется аннотацией @Wither в Lombok-е, но, к сожалению, не подходит для унаследованных сущностей.

Думали про такой вариант. Неплохой, но отпал т.к.
1) ухудшается API discoverabilitiy — надо знать какой из apply дёрнуть чтобы настроить firstName, вместо .builder().firstName()
2) если наследование глубже 1 класса, то вообще страшно выходит
3) лямбду нельзя на инстанс "забиндить"

Я готов все свои карма поинты обменять на плюсы к Вашему комментарию, это просто прекрасно!

мы это конечно же добавим в будущем, просто хотел поделиться быстрым workaround-ом :)

параметров может быть не 3, а 30.

LocalStackContainer наследуется же от GenericContainer, можно любую env variaible указать с помощью .withEnv("FOO", "BAR").

Самое забавное — в моём личном опыте сложней всего для поддержки проекты доставались именно от товарищей, которые пропагандируют "правильные" способы, пишут на Java как на Scala, а ночью под подушкшой читают куски Haskell кода.
Так что думаю тут знаний "мудрейших" не достаточно, и надо уметь их применять там, где оно уместно, и если что-то можно сделать проще — то почему бы и нет?

как пользователь, мне б это не понравилось, и вот почему:
1) API discoverability — если я хочу настроить порт, то мне надо знать наперёд в каком из билдеров этот метод для настройки порта указан
2) многословность — в вашем примере вы по сути дела устанавливаете 3 параметра, но при этом код нагружен .builder(), .build() и их друзьями
3) результат .build() должен содержать параметры всех "билдеров" (к сожалению в вашем примере это невозможно продемонстрировать), и, если это был билдер BarBuilder, то результат должен быть знать о свойствах Bar, а не только Foo

в Spring Security норм, но им можно — у них не используется возвращаемый результат их DSL :)


Но даже если и адаптировать его под этот случай — DSL становится сложней читать из-за обилия .and()

оказывается не про корректную архитектуру приложения

Внезапно то как!


Ему не удобно

Мне удобно было бы вообще не иметь public API. Нет API — нет проблем. Клаааас.

Продублирую мой ответ из твиттера:
SuperBuilder работает только если код и сторонние библиотеки к этому коду используют Java и Lombok. В нашем случае это не всегда так.

Я до сих пор удивлён что никто не упомянул трюк с .and() как это делает Spring Security:
https://docs.spring.io/spring-security/site/docs/current/reference/html/jc.html#jc-httpsecurity

Ух ты как Java изменилась при Собянине


Допустим, я прочитал Ваш код. Но он не отвечает на вопрос! Как сделать удобный API для всего этого, чтобы пользователям было удобно?


Вас сразу выдаёт то, что вы приводите примеры объявления классов, а не пример их использования.
Когда мы у себя проектируем API наших DSL, мы начинаем с "как пользователь, я хочу использовать это вот так", а потом ищем варианты как это реализовать, на грани с возможностями языка, с учетом наших пользователей. А уже потом думаем, чтобы это ещё было поддерживаемо и читаемо.

Оно потому и "подойти иначе". Мы долго боролись за chaining, что забыли что есть другие способы, и что без него можно сделать вполне читаемый вариант :)

Это примерно то, что у нас сейчас (и в статье описано в секции про generic версию, только без 2х классов).
Такой подход имеет место быть (так например работает SuperBuilder в Lombok на сколько знаю), но, к сожалению, он очень тяжело даётся контрибьюторам в проект, особенно рекурсивные конструкции вида <SELF extends MyClass>.

Это, кстати, очень интересная тема. Одно дело — умные книжки про как можно и нельзя, а другое — как потом с таким кодом работать, особенно в OSS, где каждый контрибьютор важен.
Разработчики Testcontainers тоже не глупые и тоже разные книжки читали, но, как и во всём — лучшее враг хорошего :)

Не совсем решаемо — аргументы конструктора не именованны и читать вызовы конструкторов с 10 параметрами — то ещё развлечение

там внизу в комментарии есть problem definition. Ваш "идеальный" код никто не станет использовать, потому что он громоздкий и неудобный (по крайней мере в той библиотеке что я описываю).


Поэтому я и предлагаю переубедить, с примерами кода, иначе ваши комментарии выглядят как "я прочитал книжку, вы всё делаете неправильно, а я — умный".

Пример:


Есть проект — Testcontainers. В нём есть базовый класс GenericContainer.
Есть наследники типа KafkaContainer.
Есть даже промежуточные наследования: MySQLContainer -> JDBCContainer -> GenericContainer.


Должна быть возможность их конфигурировать, чтобы удобно, красиво и вот это вот всё.
Как бы вы решили эту проблему на ровном месте? :)

А можно с примерами Java кода?

А, да, тут абсолютно согласен.


Я там в конце статьи попытался написать "не стоит использовать везде", но такое стоит повторять — этот трюк не везде применим и может сделать больно в неправильных местах (capturing, serialization, classloading, вот это вот всё), особенно в production коде.


А вот для Testcontainers пока что выглядит очень даже норм :) Но всё ещё думаем...

Информация

В рейтинге
Не участвует
Откуда
Berlin, Berlin, Германия
Дата рождения
Зарегистрирован
Активность