validar from descarga datechooser chooser java design-patterns inheritance builder

java - from - Patrón de construcción y herencia



jcalendar java netbeans descarga (5)

Como no puede usar medicamentos genéricos, ahora probablemente la tarea principal sea aflojar el tipeo de alguna manera. No sé cómo procesar esas propiedades después, ¿pero qué ocurre si usa un HashMap para almacenarlas como pares clave-valor? Por lo tanto, solo habrá un método de contenedor (clave, valor) en el generador (o el generador podría no ser necesario).

La desventaja serían los lanzamientos de tipos adicionales al procesar los datos almacenados.

Si este caso es demasiado flojo, puede conservar las propiedades existentes, pero tiene un método de conjunto general, que utiliza la reflexión y las búsquedas del método setter sobre la base del nombre ''clave''. Aunque creo que la reflexión sería una exageración.

Tengo una jerarquía de objetos que aumenta en complejidad a medida que se profundiza el árbol de herencia. Ninguno de estos son abstractos, por lo tanto, todas sus instancias tienen un propósito más o menos sofisticado.

Como la cantidad de parámetros es bastante alta, me gustaría usar el patrón de construcción para establecer propiedades en lugar de codificar varios constructores. Como necesito atender a todas las permutaciones, las clases de hoja en mi árbol de herencia tendrían constructores telescópicos.

He buscado aquí una respuesta cuando encuentro algunos problemas durante mi diseño. Primero, déjame darte un ejemplo simple y superficial para ilustrar el problema.

public class Rabbit { public String sex; public String name; public Rabbit(Builder builder) { sex = builder.sex; name = builder.name; } public static class Builder { protected String sex; protected String name; public Builder() { } public Builder sex(String sex) { this.sex = sex; return this; } public Builder name(String name) { this.name = name; return this; } public Rabbit build() { return new Rabbit(this); } } } public class Lop extends Rabbit { public float earLength; public String furColour; public Lop(LopBuilder builder) { super(builder); this.earLength = builder.earLength; this.furColour = builder.furColour; } public static class LopBuilder extends Rabbit.Builder { protected float earLength; protected String furColour; public LopBuilder() { } public Builder earLength(float length) { this.earLength = length; return this; } public Builder furColour(String colour) { this.furColour = colour; return this; } public Lop build() { return new Lop(this); } } }

Ahora que tenemos un código para continuar, las imágenes quiero construir un Lop :

Lop lop = new Lop.LopBuilder().furColour("Gray").name("Rabbit").earLength(4.6f);

Esta llamada no se compilará ya que la última llamada encadenada no se puede resolver, el Builder no define el método earLength . Así que de esta manera se requiere que todas las llamadas estén encadenadas en un orden específico que es muy poco práctico, especialmente con un árbol de jerarquía profunda.

Ahora, durante mi búsqueda de una respuesta, me encontré con Subclasificar una clase de Java Builder que sugiere el uso del Patrón genérico curiosamente recursivo . Sin embargo, como mi jerarquía no contiene una clase abstracta, esta solución no funcionará para mí. Pero el enfoque se basa en la abstracción y el polimorfismo para funcionar, razón por la cual no creo que pueda adaptarlo a mis necesidades.

Un enfoque con el que me he acostumbrado actualmente es anular todos los métodos del Builder superclase en la jerarquía y simplemente hacer lo siguiente:

public ConcreteBuilder someOverridenMethod(Object someParameter) { super(someParameter); return this; }

Con este enfoque, puedo asegurar que me devuelven una instancia en la que puedo realizar llamadas en cadena. Si bien esto no es tan grave como el Telescoping Anti-pattern, es un segundo cercano y lo considero un poco "hacky".

¿Hay alguna otra solución a mi problema de la que no tengo conocimiento? Preferiblemente una solución consistente con el patrón de diseño. ¡Gracias!


Esta forma parece funcionar. No es muy ordenado, pero parece que evita tus problemas:

class Rabbit<B extends Rabbit.Builder<B>> { String name; public Rabbit(Builder<B> builder) { this.name = builder.colour; } public static class Builder<B extends Rabbit.Builder<B>> { protected String colour; public B colour(String colour) { this.colour = colour; return (B)this; } public Rabbit<B> build () { return new Rabbit<>(this); } } } class Lop<B extends Lop.Builder<B>> extends Rabbit<B> { float earLength; public Lop(Builder<B> builder) { super(builder); this.earLength = builder.earLength; } public static class Builder<B extends Lop.Builder<B>> extends Rabbit.Builder<B> { protected float earLength; public B earLength(float earLength) { this.earLength = earLength; return (B)this; } @Override public Lop<B> build () { return new Lop<>(this); } } } public class Test { public void test() { Rabbit rabbit = new Rabbit.Builder<>().colour("White").build(); Lop lop1 = new Lop.Builder<>().earLength(1.4F).colour("Brown").build(); Lop lop2 = new Lop.Builder<>().colour("Brown").earLength(1.4F).build(); //Lop.Builder<Lop, Lop.Builder> builder = new Lop.Builder<>(); } public static void main(String args[]) { try { new Test().test(); } catch (Throwable t) { t.printStackTrace(System.err); } } }

Aunque he creado con éxito Rabbit y Lop (en ambas formas) no puedo en esta etapa encontrar la forma de crear una instancia de uno de los objetos de Builder con su tipo completo.

La esencia de este método depende del lanzamiento a (B) en los métodos del Builder . Esto le permite definir el tipo de objeto y el tipo de Builder y retenerlo dentro del objeto mientras está construido.

Si alguien pudiera resolver la sintaxis correcta para esto (que está mal), lo agradecería.

Lop.Builder<Lop.Builder> builder = new Lop.Builder<>();


Esto es ciertamente posible con el genérico recursivo, pero debe pasar el tipo genérico completamente hacia abajo. La sintaxis es un poco engorrosa, pero ciertamente puedes hacerlo.

abstract class GenericMammalBuilder // extend this for <B extends GenericMammalBuilder<B>> { // Mammal subtype builders String sex; String name; B ofSex(String sex) { this.sex = sex; return (B)this; } B ofName(String name) { this.name = name; return (B)this; } } final class MammalBuilder // use this to extends GenericMammalBuilder<MammalBuilder> { // build new Mammal instances Mammal build() { return new Mammal(this); } } abstract class GenericRabbitBuilder // extend this for <B extends GenericRabbitBuilder<B>> // Rabbit subtype builders extends GenericMammalBuilder<B> { // e.g. LopBuilder Color color; B ofColor(Color color) { this.color = color; return (B)this; } } final class RabbitBuilder // use this to build extends GenericRabbitBuilder<RabbitBuilder> { // new Rabbit instances Rabbit build() { return new Rabbit(this); } }

El problema con el genérico recursivo es que el parámetro debe ser un IE de subtipo parametrizado si tiene:

class A<T extends A<T>> {} class B<T extends B<T>> extends A<T> {} class C extends B<C> {}

Puedes hacer una new C(); pero no new B<B>(); . (Erróneamente, también puede hacer una new B<C>(); )

Entonces necesitas algún tipo de clase de "hormigón provisional". Esta es una manera de elegir su propio escenario malvado IMO.


Hice algo de experimentación y encontré que esto funciona bastante bien para mí. Tenga en cuenta que prefiero crear la instancia real al inicio y llamar a todos los instaladores en esa instancia. Esto es solo una preferencia.

Las principales diferencias con la respuesta aceptada es que

  1. Paso un parámetro que indica el tipo de devolución
  2. No hay necesidad de un Resumen ... y un constructor final.
  3. Creo un método de conveniencia ''newBuilder''.

El código:

public class MySuper { private int superProperty; public MySuper() { } public void setSuperProperty(int superProperty) { this.superProperty = superProperty; } public static SuperBuilder<? extends MySuper, ? extends SuperBuilder> newBuilder() { return new SuperBuilder<>(new MySuper()); } public static class SuperBuilder<R extends MySuper, B extends SuperBuilder<R, B>> { private final R mySuper; public SuperBuilder(R mySuper) { this.mySuper = mySuper; } public B withSuper(int value) { mySuper.setSuperProperty(value); return (B) this; } public R build() { return mySuper; } } }

y luego una subclase se ve así:

public class MySub extends MySuper { int subProperty; public MySub() { } public void setSubProperty(int subProperty) { this.subProperty = subProperty; } public static SubBuilder<? extends MySub, ? extends SubBuilder> newBuilder() { return new SubBuilder(new MySub()); } public static class SubBuilder<R extends MySub, B extends SubBuilder<R, B>> extends SuperBuilder<R, B> { private final R mySub; public SubBuilder(R mySub) { super(mySub); this.mySub = mySub; } public B withSub(int value) { mySub.setSubProperty(value); return (B) this; } } }

y una clase subsub

public class MySubSub extends MySub { private int subSubProperty; public MySubSub() { } public void setSubSubProperty(int subProperty) { this.subSubProperty = subProperty; } public static SubSubBuilder<? extends MySubSub, ? extends SubSubBuilder> newBuilder() { return new SubSubBuilder<>(new MySubSub()); } public static class SubSubBuilder<R extends MySubSub, B extends SubSubBuilder<R, B>> extends SubBuilder<R, B> { private final R mySubSub; public SubSubBuilder(R mySub) { super(mySub); this.mySubSub = mySub; } public B withSubSub(int value) { mySubSub.setSubSubProperty(value); return (B)this; } } }

Para verificar que funcione completamente usé esta prueba:

MySubSub subSub = MySubSub .newBuilder() .withSuper (1) .withSub (2) .withSubSub(3) .withSub (2) .withSuper (1) .withSubSub(3) .withSuper (1) .withSub (2) .build();


Si alguien todavía se topa con el mismo problema, sugiero la siguiente solución, que cumple con el patrón de diseño "Prefiere la composición sobre la herencia".

Clase de padres

El elemento principal es la interfaz que debe implementar el Creador de clases padre:

public interface RabbitBuilder<T> { public T sex(String sex); public T name(String name); }

Aquí está la clase de padres modificada con el cambio:

public class Rabbit { public String sex; public String name; public Rabbit(Builder builder) { sex = builder.sex; name = builder.name; } public static class Builder implements RabbitBuilder<Builder> { protected String sex; protected String name; public Builder() {} public Rabbit build() { return new Rabbit(this); } @Override public Builder sex(String sex) { this.sex = sex; return this; } @Override public Builder name(String name) { this.name = name; return this; } } }

La clase de niños

El Child Class Builder debe implementar la misma interfaz (con diferente tipo genérico):

public static class LopBuilder implements RabbitBuilder<LopBuilder>

Dentro de la clase de niño, Builder el campo que hace referencia al Builder padres:

private Rabbit.Builder baseBuilder;

esto garantiza que se Builder métodos de Builder padre en el elemento secundario, sin embargo, su implementación es diferente:

@Override public LopBuilder sex(String sex) { baseBuilder.sex(sex); return this; } @Override public LopBuilder name(String name) { baseBuilder.name(name); return this; } public Rabbit build() { return new Lop(this); }

El constructor de Builder:

public LopBuilder() { baseBuilder = new Rabbit.Builder(); }

El constructor de la clase secundaria construida:

public Lop(LopBuilder builder) { super(builder.baseBuilder); }