Java: Clase abstracta con patrón builder
Espero que esto no sea una duplicación; sé que hay algunas preguntas con nombres similares, pero ninguna de ellas parecía resolver mi problema.
Tengo múltiples clases que son exactamente iguales, excepto por un método específico. Por lo tanto, decidí crear una clase padre abstracta, llamémosla “A”. “A” ejecuta todo el código y tiene un método “calculate()”, que todas las subclases implementan.
Hasta aquí todo bien. El problema es que todas las subclases tienen muchos parámetros opcionales, así que en lugar de crear muchos constructores diferentes, decidí utilizar el siguiente patrón de construcción:
public abstract class A {
private int foo;
private int bar;
public static class Builder {
private int foo;
private int bar;
public Builder(int foo) {
this.foo = foo;
}
public Builder bar(int bar) {
this.bar = bar;
return this;
}
public A build() {
return new A(this);
}
}
private A(Builder builder) {
this.foo = builder.foo;
this.bar = builder.bar;
}
}
Utilicé este patrón específico en todo mi proyecto y funcionó bien, siempre y cuando la clase no sea abstracta. El problema es que el método “build()” devuelve un objeto “A”, pero “A”, por supuesto, no se puede instanciar.
Entonces, me parece que hay dos soluciones, pero ninguna de ellas parece óptima:
- Mover la clase “Builder” hacia las subclases de “A”, para que puedan ser instanciadas. Esto resultaría en mucho código redundante, ya que todos los atributos que establece el constructor son idénticos.
-
Hacer que “A” no sea abstracta. Pero eso permitiría que otro usuario la instancie, a pesar de que ese no es su propósito previsto.
¿Me estoy perdiendo algo? Siento que debería haber una solución más elegante, pero no puedo pensar en una en este momento… Gracias.
davy.ai
Respuesta
Puedes usar los genéricos autorreferenciales en Java para resolver este problema. Necesitas modificar tu clase
A
y su constructor de la siguiente manera:Aquí,
T extends A<t>
especifica que una subclase deA
debe ser pasada como parámetro de tipoT
. Esto asegura que cualquier constructor construido para una subclase deA
solo puede construir objetos de esa subclase. El constructor ahora tiene un método abstractobuild()
que devuelve el tipoT
, y un método protegidobuild(T object)
que establece los campos del objeto. Los constructores de la claseA
se han modificado para adaptarse a este cambio, y el métodocalculate()
se deja abstracto para ser definido en las subclases.Ahora, puedes crear el constructor para una subclase de
A
de la siguiente manera:Aquí, la clase
B
extiendeA<b>
para especificar el parámetro de tipo correcto. La claseBuilder
extiendeA.Builder<b, builder="">
para especificar que construye objetos deB
. El métodobuild()
se sobrescribe para construir un nuevo objetoB
y establecer sus campos utilizando el métodobuild()
del constructor de la superclase y los propios campos del constructor. Los constructores de la claseB
se modifican para adaptarse a este cambio, y se define el métodocalculate()
según sea necesario.Esta solución elimina la necesidad de código de constructor redundante y asegura que solo se construyan las subclases previstas de
A
.</b,></b,>