Проблема с генератором Java Builder
В моем проекте у меня есть два пакета, полные DTO, POJO только с геттерами и сеттерами. Хотя важно, что они являются простыми Java-бинами (например, потому что Apache CXF использует их для создания XSD-файлов Web-сервисов и т. Д.), Они также ужасны и подвержены ошибкам.
Foo foo = new Foo();
foo.setBar("baz");
foo.setPhleem(123);
return foo;
Я предпочитаю плавные интерфейсы и объекты компоновщика, поэтому я использую maven / gmaven для автоматического создания компоновщиков для DTO. Так что для приведенного выше кода, FooBuilder
автоматически генерируется, который я могу использовать так:
Foo foo = new FooBuilder()
.bar("baz")
.phleem(123)
.build();
Я также автоматически генерирую модульные тесты для сгенерированных Строителей. Модульный тест сгенерирует оба вышеуказанных кода (версия для сборщика и не для сборщика) и подтвердит, что обе версии эквивалентны с точки зрения equals()
а также hashcode()
, Способ, которым я могу достичь этого, - это иметь глобально доступную Карту со значениями по умолчанию для каждого типа свойства. Что-то вроде этого:
public final class Defaults{
private Defaults(){}
private static final Map<Class<?>, Object> DEFAULT_VALUES =
new HashMap<Class<?>, Object>();
static{
DEFAULT_VALUES.put(String.class, "baz");
// argh, autoboxing is necessary :-)
DEFAULT_VALUES.put(int.class, 123);
// etc. etc.
}
public static getPropertyValue(Class<?> type){
return DEFAULT_VALUES.get(type);
}
}
Другим нетривиальным аспектом является то, что у pojos иногда есть члены коллекции. например:
foo.setBings(List<Bing> bings)
но в моем сборщике я хотел бы, чтобы это генерировало два метода из этого случая: метод set и метод add:
fooBuilder.bings(List<Bing> bings); // set method
fooBuilder.addBing(Bing bing); // add method
Я решил это, добавив пользовательскую аннотацию к полям свойств в Foo
@ComponentType(Bing.class)
private List<Bing> bings;
Builder Builder (sic) читает аннотацию и использует значение как общий тип генерируемых методов.
Сейчас мы приближаемся к вопросу (извините, краткость не является одним из моих достоинств:-)).
Я понял, что этот подход к компоновке может использоваться в более чем одном проекте, поэтому я думаю превратить его в плагин maven. Я совершенно ясно о том, как сгенерировать плагин maven, так что это не является частью вопроса (равно как и о том, как генерировать действительный исходный код Java). Моя проблема: как я могу справиться с двумя вышеупомянутыми проблемами без введения каких-либо общих зависимостей (между Project и Plugin):
<Question>
Мне нужен класс Defaults (или аналогичный механизм) для получения значений по умолчанию для сгенерированных модульных тестов (это ключевая часть концепции, я бы не стал доверять автоматически сгенерированным компоновщикам, если бы они не были полностью протестированы). Пожалуйста, помогите мне найти хороший и общий способ решения этой проблемы, учитывая, что у каждого проекта будут свои доменные объекты.
Мне нужен общий способ сообщения универсальных типов генератору компоновщика. Текущая версия на основе аннотаций, которую я использую, не является удовлетворительной, поскольку и проект, и плагин должны знать одну и ту же аннотацию.
</Question>
Есть идеи?
Кстати, я знаю, что реальный ключевой момент использования компоновщиков - сделать объекты неизменяемыми. Я не могу сделать мой неизменяемым, потому что стандартные Java-бины необходимы, но я использую AspectJ для обеспечения того, чтобы ни set-методы, ни конструкторы не вызывались нигде в моей кодовой базе, кроме как в сборщиках, поэтому для практических целей результирующие объекты являются неизменяемыми,
Также: Да, я знаю о существующих плагинах IDE Builder-generator. Это не соответствует моей цели, я хочу автоматизированное решение, которое всегда актуально, когда изменяется базовый код.
Мэтт Б попросил немного информации о том, как я создаю своих строителей. Вот что я делаю:
Я читаю класс за отражение, использую Introspector.getBeanInfo(clazz).getPropertyDescriptors()
чтобы получить массив дескрипторов свойств. Все мои строители имеют базовый класс AbstractBuilder<T>
где T
было бы Foo
в вышеуказанном случае. Вот код класса Abstract Builder. За каждую собственность в PropertyDescriptor
массив, метод генерируется с именем свойства. Это будет реализация FooBuilder.bar(String)
:
public FooBuilder bar(String bar){
setProperty("bar", bar);
return this;
}
build()
метод в AbstractBuilder
создает экземпляр объекта и назначает все свойства в его карте свойств.
2 ответа
Вы смотрели на Дизеля? Это генератор Builder.
- Он обрабатывает универсальные типы, поэтому он может быть полезен здесь для вопроса 2
- Он генерирует все интерфейсы и реализацию, основанную на файле описания XML. Вы можете с помощью самоанализа сгенерировать этот XML (или даже перейти непосредственно к более низкому API)
- Он поставляется как плагин Maven.
POJO - это объект, который не следует за спуком Java Bean. то есть. у него нет сеттеров / геттеров.
JavaBeans не обязаны иметь сеттеры, если вы не хотите, чтобы они вызывались, не генерируйте их. (Ваш конструктор может вызвать пакет локальным или частным конструктором для создания ваших неизменяемых объектов)