En un proyecto mío, tengo dos paquetes llenos de DTO, POJO con solo getters y setters. Si bien es importante que sean beans Java simples (por ejemplo, porque Apache CXF los usa para crear XSD de servicios web, etc.), también es horrible y propenso a errores programar de esa manera.Problema del generador Java Builder
Foo foo = new Foo();
foo.setBar("baz");
foo.setPhleem(123);
return foo;
prefiero interfaces fluidas y objetos constructor, así que uso experto/gmaven para crear automáticamente los constructores para los dtos. Así que para el código anterior, un FooBuilder
se genera de forma automática, lo que puedo usar como esto:
Foo foo = new FooBuilder()
.bar("baz")
.phleem(123)
.build();
también genera automáticamente pruebas unitarias para los constructores generados. Una prueba de unidad generaría los dos códigos anteriores (versión de constructor y versión no de constructor) y asevera que ambas versiones son equivalentes en términos de equals()
y hashcode()
. La forma en que puedo lograr eso es tener un Mapa globalmente accesible con valores predeterminados para cada tipo de propiedad. Algo como esto:
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);
}
}
Otro aspecto no trivial es que los pojos a veces tienen miembros de colección. por ejemplo:
foo.setBings(List<Bing> bings)
pero en mi constructor quisiera esto para generar dos métodos de este caso: un método set y un método add:
fooBuilder.bings(List<Bing> bings); // set method
fooBuilder.addBing(Bing bing); // add method
He resuelto esto añadiendo una anotación de costumbre los campos de propiedad en Foo
@ComponentType(Bing.class)
private List<Bing> bings;
el constructor constructor (sic) lee la anotación y utiliza el valor como el tipo genérico de los métodos para generar.
Ahora estamos cada vez más cerca de la pregunta (lo siento, la brevedad no es uno de mis méritos :-)).
Me he dado cuenta de que este enfoque de constructor podría utilizarse en más de un proyecto, por lo que estoy pensando en convertirlo en un plugin de maven. Tengo muy claro cómo generar un complemento maven, por lo que no es parte de la pregunta (ni tampoco cómo generar un código fuente Java válido). Mi problema es: ¿cómo puedo hacer frente a los dos problemas anteriores sin introducir ningún dependencias comunes (entre el proyecto y Plugin):
<Question>
necesito una clase predeterminados (o un mecanismo similar) para obtener valores predeterminados para las pruebas de unidades generadas (esta es una parte clave del concepto, no me fiaría de los constructores generados automáticamente si no se probaran por completo). Ayúdeme a encontrar una forma buena y genérica para resolver este problema, dado que cada proyecto tendrá sus propios objetos de dominio.
Necesito una forma común de comunicar los tipos genéricos al generador generador. La versión basada en la anotación actual que estoy usando no es satisfactoria, ya que tanto el proyecto como el complemento deben tener en cuenta la misma anotación.
</Question>
alguna idea?
BTW: Sé que el verdadero punto clave de usar constructores es hacer que los objetos sean inmutables. No puedo hacer que el mío sea inmutable, porque los beans Java estándar son necesarios, pero utilizo AspectJ para exigir que ni los métodos set ni los constructores se llamen a ninguna parte en mi base de código excepto en los constructores, por lo que para fines prácticos, los objetos resultantes son inmutables .
También: Sí, conozco los plugins IDE de Generador-generador existentes. Eso no se ajusta a mi propósito, quiero una solución automatizada, que esté siempre actualizada cuando el código subyacente haya cambiado.
Matt B solicitó información sobre cómo genero mis constructores. Esto es lo que hago:
Leí una clase por reflexión, uso Introspector.getBeanInfo(clazz).getPropertyDescriptors()
para obtener una matriz de descriptores de propiedad. Todos mis constructores tienen una clase base AbstractBuilder<T>
donde T
sería Foo
en el caso anterior. Aquí está the code of the Abstract Builder class. Para cada propiedad en la matriz PropertyDescriptor
, se genera un método con el nombre de la propiedad. Esta sería la implementación de FooBuilder.bar(String)
:
public FooBuilder bar(String bar){
setProperty("bar", bar);
return this;
}
el método build()
en AbstractBuilder
una instancia del objeto y asigna todas las propiedades en su mapa de la propiedad.
Aparte comentario: cuando necesito generar código Java (especialmente grandes conjuntos de pruebas unitarias), uso grep/sed + python ... – khachik
Me gusta usar una tecnología que entienda el idioma. Lo cual lo limita a a) reflexión b) un analizador de código fuente c) una herramienta de código de bytes como ASM –
Por curiosidad, ¿qué complemento Maven genera clases de Generador para usted? –