В моем проекте у меня есть два пакета, полных DTO, POJO только с геттерами и сеттерами. Хотя важно, чтобы они являются простыми Java-компонентами (например, потому что Apache CXF использует их для создания XSD веб-сервисов и т. д.), это также ужасно и подвержено ошибкам программировать подобное.
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, Object> DEFAULT_VALUES =
new HashMap, 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 bings)
, но в моем конструкторе я хотел бы, чтобы это сгенерировало два метода из этого случая: метод набора и метод добавления:
fooBuilder.bings(List bings); // set method
fooBuilder.addBing(Bing bing); // add method
Я решил это, добавив пользовательскую аннотацию к полям свойств в Foo
@ComponentType(Bing.class)
private List bings;
Построитель Builder (sic) считывает аннотацию и использует значение в качестве универсального типа генерируемых методов.
Теперь мы приближаемся к вопросу (извините, краткость не является одним из моих достоинств: -)).
Я понял, что этот конструкторский подход может использоваться более чем в одном проекте, поэтому я думаю о превратив его в плагин maven. Я прекрасно понимаю, как создать плагин maven, так что это не является частью вопроса (как и то, как сгенерировать действительный исходный код Java). Моя проблема: как я могу справиться с двумя вышеуказанными проблемами, не вводя какие-либо общие зависимости (между Project и Plugin):
Мне нужен класс Defaults (или аналогичный механизм) для получения значений по умолчанию для сгенерированные модульные тесты (это ключевая часть концепции, я бы не стал доверять автоматически сгенерированным сборщикам, если бы они не были полностью протестированы). Пожалуйста, помогите мне придумать хороший и общий способ решения этой проблемы, учитывая, что каждый проект будет иметь свои собственные объекты домена.
Мне нужен общий способ передачи общих типов генератору компоновщика. Текущая версия на основе аннотаций, которую я использую, не является удовлетворительной, поскольку и проект, и плагин должны знать об одной и той же аннотации.
Есть идеи?
Кстати: Я знаю, что настоящий ключ Смысл использования конструкторов - сделать объекты неизменяемыми. Я не могу сделать свой неизменяемым, потому что стандартные java beans необходимы, но я использую AspectJ, чтобы обеспечить, чтобы ни set-методы, ни конструкторы не вызывались где-либо в моей кодовой базе, кроме построителей, поэтому для практических целей результирующие объекты неизменяемы .
Также: Да, мне известны существующие плагины IDE Builder-generator. Это не соответствует моей цели, мне нужно автоматическое решение, которое ' s всегда в актуальном состоянии при изменении базового кода.
Мэтт Б. запросил некоторую информацию о том, как я генерирую свои компоновщики. Вот что я делаю:
Я читаю класс для каждого отражения, использую Introspector.getBeanInfo (clazz) .getPropertyDescriptors ()
, чтобы получить массив дескрипторов свойств. Все мои конструкторы имеют базовый класс AbstractBuilder
, где T
будет Foo
в приведенном выше случае. Вот код класса Abstract Builder . Для каждого свойства в массиве PropertyDescriptor
создается метод с именем свойства. Это будет реализация FooBuilder.bar (String)
:
public FooBuilder bar(String bar){
setProperty("bar", bar);
return this;
}
метод build ()
в AbstractBuilder
создает экземпляр объекта и назначает все свойства в его карте свойств.