Motivación

Recientemente he buscado una manera de inicializar un objeto complejo sin pasar por un montón de parámetro en el constructor. He probado con el generador de patrón, pero no me gusta el hecho de que yo no soy capaz de comprobar en tiempo de compilación si realmente conjunto de todos los valores necesarios.

Tradicional generador de patrón de

Cuando uso el generador de patrón para crear mi Complex objeto, la creación es más «typesafe», porque es más fácil ver lo que un argumento es utilizado para:

new ComplexBuilder()
        .setFirst( "first" )
        .setSecond( "second" )
        .setThird( "third" )
        ...
        .build();

Pero ahora tengo el problema, que fácilmente se puede perder un parámetro importante. Puedo comprobar que en el interior de la build() método, pero eso es sólo en tiempo de ejecución. En tiempo de compilación no hay nada que me avisa, si me he perdido algo.

Mejorado el generador de patrón de

Ahora mi idea era crear un generador, que «recuerda» a mí si me he perdido un parámetro necesitan. Mi primer intento se parece a esto:

public class Complex {
    private String m_first;
    private String m_second;
    private String m_third;

    private Complex() {}

    public static class ComplexBuilder {
        private Complex m_complex;

        public ComplexBuilder() {
            m_complex = new Complex();
        }

        public Builder2 setFirst( String first ) {
            m_complex.m_first = first;
            return new Builder2();
        }

        public class Builder2 {
            private Builder2() {}
            Builder3 setSecond( String second ) {
                m_complex.m_second = second;
                return new Builder3();
            }
        }

        public class Builder3 {
            private Builder3() {}
            Builder4 setThird( String third ) {
                m_complex.m_third = third;
                return new Builder4();
            }
        }

        public class Builder4 {
            private Builder4() {}
            Complex build() {
                return m_complex;
            }
        }
    }
}

Como se puede ver, cada uno de los sets en el constructor de la clase devuelve un interno diferente constructor de la clase. Cada uno de los internos de la clase de generador proporciona exactamente un método setter y el último sólo proporciona una build() método.

Ahora la construcción de un objeto nuevo se parece a esto:

new ComplexBuilder()
    .setFirst( "first" )
    .setSecond( "second" )
    .setThird( "third" )
    .build();

…pero no hay manera de olvidar un parámetro necesitan. El compilador no lo aceptó.

Parámetros opcionales

Si tuviera parámetros opcionales, me gustaría utilizar la última interna de la clase de generador de Builder4 para establecer como un «tradicionales» constructor hace, se devuelve a sí mismo.

Preguntas

  • Es este un patrón bien conocido? ¿Tiene un nombre especial?
  • ¿Ve alguna de las trampas?
  • Tienes alguna idea para mejorar la aplicación – en el sentido de menos líneas de código?
  • Esto es en realidad por qué no me gusta el Generador de patrón. (Bueno, eso y es prolijo.)
  • Su «tradicional generador de patrón» se parece más a lo que conocemos como un [fluidez de la interfaz][1]. Cuando escucho «el generador de patrón», creo que de la [patrón de diseño «constructor»][2]. Es común referirse a una interfaz fluida como un generador de patrón, o me estoy perdiendo algo aquí? [1]: en.wikipedia.org/wiki/Fluent_interface, [2]: en.wikipedia.org/wiki/Builder_pattern
  • Josué Blog se refiere a este modelo como «Constructor» (pero no como un reemplazo de la GoF builder). Consulte rwhansen.blogspot.com/2007/07/…. Así que, no sé si es bueno usar el mismo nombre, no sé si es común, pero como lector de Efectivo de Java, no me parece engorroso.
  • Suena como que usted está tratando de obtener los tiempos de compilación comprueba que se obtiene con los constructores. Su una misma Java no admite argumentos con nombre como groovy etc que podría resolver este problema sin una clase de generador.
  • Por favor, edite la imagen, es ‘forma’ demasiado grandes.
  • En realidad se puede hacer aún mejor con los medicamentos genéricos: michid.wordpress.com/2008/08/13/…
  • Es una idea inteligente, pero se pierde una gran cantidad de flexibilidad que se obtiene con la tradicional generador de patrón. No sólo en términos de la orden en el que se especifique las propiedades, pero también cosas como la referencia a la en-progreso generador en una variable y ajuste de propiedades del generador de forma asincrónica. Usted no puede hacer eso con su patrón, porque el tipo de generador de cambios cada vez que se establece una propiedad. Realmente todo lo que has hecho se hace más detallado constructor, donde en lugar de buscar lo que cada parámetro medio, tienes que buscar el orden correcto de los métodos. El IDE puede ayudarle con cualquiera de ellos.

InformationsquelleAutor tangens | 2009-10-28

10 Comentarios

  1. 15

    No, no es nueva. Lo que en realidad estamos haciendo es crear una especie de DSL por la extensión del estándar generador de patrón para apoyar a las ramas, que es entre otras cosas una excelente manera de asegurarse de que el generador no produce un conjunto de conflictos de configuración para el objeto real.

    Personalmente creo que esta es una gran extensión para el generador de patrón y usted puede hacer todo tipo de cosas interesantes, por ejemplo, en el trabajo tenemos DSL constructores para algunas de nuestras pruebas de integridad de datos que nos permiten hacer cosas como assertMachine().usesElectricity().and().makesGrindingNoises().whenTurnedOn();. OK, tal vez no sea el mejor ejemplo pero creo que usted consigue el punto.

    • Tengo una pregunta sobre el generador de patrón de here. Quería ver si usted tiene alguna idea sobre mi pregunta.
  2. 23

    La tradicional generador de patrón ya se encarga de esto: simplemente tomar la obligatoriedad de los parámetros en el constructor. Por supuesto, nada impide que una persona que pasa null, pero tampoco su método.

    El gran problema que yo veo con tu método es que un combinatorical explosión de clases, con el número de parámetros obligatorios, o de obligar al usuario a establecer los parámetros en una particular sqeuence, lo cual es molesto.

    También, es un montón de trabajo adicional.

    • El cartel menciona específicamente tratando de evitar pasar una gran cantidad de parámetros en el constructor, presumiblemente debido a que no se compra muy buena la comprobación en tiempo de compilación – ya que si se pasa en 5 puntos, el compilador no puede decirle si usted tiene en el orden correcto.
    • Pero no hay ningún compilador en la tierra que me pueda decir que me han escrito foo(5, 6) en lugar de foo(6, 5). La fluidez de la interfaz propuesta en la pregunta hace que (marginalmente?) menos probable, pero no elimina la posibilidad por un tiro largo.
    • +1. Justo ayer estaba leyendo el Artículo 2 de Efectivo de Java (sobre el uso de los constructores en lugar de telescópica constructores), y también se recomienda para crear un constructor con la obligación de parámetros y hacer necesaria la validación dentro de instancia de la clase que está siendo construida.
    • Usted debe usar objetos de dominio en lugar de 5 y 6.
    • «Obligar al usuario a establecer los parámetros en una secuencia particular» – muy buen punto. Creo que una de las ventajas de la «fluidez Generador» o «Bloch Builder» es la capacidad para establecer los parámetros en el orden que quieras, y perder lo lleva un paso atrás hacia una telescópica constructor.
  3. 13
    public class Complex {
    private final String first;
    private final String second;
    private final String third;
    public static class False {}
    public static class True {}
    public static class Builder<Has1,Has2,Has3> {
    private String first;
    private String second;
    private String third;
    private Builder() {}
    public static Builder<False,False,False> create() {
    return new Builder<>();
    }
    public Builder<True,Has2,Has3> setFirst(String first) {
    this.first = first;
    return (Builder<True,Has2,Has3>)this;
    }
    public Builder<Has1,True,Has3> setSecond(String second) {
    this.second = second;
    return (Builder<Has1,True,Has3>)this;
    }
    public Builder<Has1,Has2,True> setThird(String third) {
    this.third = third;
    return (Builder<Has1,Has2,True>)this;
    }
    }
    public Complex(Builder<True,True,True> builder) {
    first = builder.first;
    second = builder.second;
    third = builder.third;
    }
    public static void test() {
    //Compile Error!
    Complex c1 = new Complex(Complex.Builder.create().setFirst("1").setSecond("2"));
    //Compile Error!
    Complex c2 = new Complex(Complex.Builder.create().setFirst("1").setThird("3"));
    //Works!, all params supplied.
    Complex c3 = new Complex(Complex.Builder.create().setFirst("1").setSecond("2").setThird("3"));
    }
    }
    • Que es una gran idea! De esta manera usted no está vinculado a un orden específico de la configuración de los parámetros, pero todavía se puede estar seguro de que todos los parámetros se establecen. Gracias por esta idea inspiradora!
  4. 12

    ¿Por qué no poner «necesario» de los parámetros en los constructores constructor?

    public class Complex
    {
    ....
    public static class ComplexBuilder
    {
    //Required parameters
    private final int required;
    //Optional parameters
    private int optional = 0;
    public ComplexBuilder( int required )
    {
    this.required = required;
    } 
    public Builder setOptional(int optional)
    {
    this.optional = optional;
    }
    }
    ...
    }

    Este patrón se describe en Efectivos De Java.

    • Porque al parecer todos ellos son necesarios y hay un montón de ellos. Aunque si ese es el caso, yo estaría preguntando si hay espacio para mejorar en otros lugares.
  5. 7

    Lugar de utilizar varias clases sólo quiero usar una clase y múltiples interfaces. Que lo que hace con su sintaxis sin necesidad de tanto escribir. También le permite ver todo el código relacionado cerca juntos, lo que hace que sea más fácil entender lo que está pasando con su código en un nivel más amplio.

  6. 5

    Mi humilde opinión, esto parece hinchado. Si usted han tener todos los parámetros, pasar en el constructor.

    • El punto de el Generador de patrón es que ponerlas en el constructor es problemático (seguimiento de pedido, y tal).
    • No el generador de patrón aparte de la secuenciación de los pasos concretos de tal manera que un ‘generador’ sabe cómo ejecutar los pasos individuales y un ‘director’ de la clase de secuencias de ellos? Es decir, es un caso especial de la plantilla de patrón.
  7. 5

    He visto/usado este:

    new ComplexBuilder(requiredvarA, requiedVarB).optional(foo).optional(bar).build();

    A continuación, pasar estos a su objeto de que los requiere.

  8. 2

    El Generador de Patrón se utiliza generalmente cuando usted tiene un montón de parámetros opcionales. Si usted encuentra que necesita muchos parámetros necesarios, considere estas opciones:

    • Su clase podría estar haciendo demasiado. Verificar que no se violen Principio De Responsabilidad Único. Pregúntese por qué usted necesita una clase con tantas necesario variables de instancia.
    • Usted constructor podría ser haciendo demasiado. El trabajo de un constructor para la construcción. (De no ser muy creativo al que le pusieron el nombre ;D ) al igual que las clases, los métodos tienen un Solo Principio de Responsabilidad. Si el constructor está haciendo mucho más que el campo de asignación, se necesita una buena razón para justificar que. Usted podría encontrar que usted necesita un Método De Fábrica en lugar de un Generador.
    • Sus parámetros pueden ser hacer demasiado poco. Pregúntate a ti mismo si tu parámetros pueden ser agrupados en una pequeña estructura (o la estructura-como objeto en el caso de Java). No tenga miedo de hacer clases pequeñas. Si usted encuentra que usted necesita para hacer una estructura o clase pequeña, no te olvides a refactorizar fuera funcionalidad que pertenece en la estructura, en lugar de su clase más grande.
  9. 1

    Para obtener más información sobre cuándo utilizar el Generador de Patrón y sus ventajas usted debe comprobar fuera de mi post para otra pregunta similar aquí

  10. 0

    Pregunta 1: en Relación con el nombre del patrón, me gusta el nombre de «Paso Builder»:

    Pregunta 2/3: en Cuanto a dificultades y recomendaciones, este se siente más complicado para la mayoría de las situaciones.

    • Va a aplicar un secuencia en cómo usted utiliza su generador, lo cual es inusual en mi experiencia. Pude ver cómo esto podría ser importante en algunos casos, pero nunca la he necesitado. Por ejemplo, no veo la necesidad de forzar una secuencia de aquí:

      Person.builder().firstName("John").lastName("Doe").build()
      Person.builder().lastName("Doe").firstName("John").build()

    • Sin embargo, muchas veces el constructor necesarios para aplicar algunas restricciones para evitar falsos objetos de construcción. Tal vez usted quiere asegurarse de que todos los campos requeridos son proporcionados o que las combinaciones de campos son válidos. Supongo que esta es la verdadera razón por la que queremos introducir la secuenciación en el edificio.

      En este caso, me gusta la recomendación de Joshua Bloch para realizar la validación en el build() método. Esto ayuda con la cruz de validación de campo, porque todo está disponible en este momento. Ver esta respuesta: https://softwareengineering.stackexchange.com/a/241320

    En resumen, yo no agregar ninguna complicación para el código sólo porque usted está preocupado acerca de la «falta» una llamada a un constructor método. En la práctica, esto es fácilmente capturado con un caso de prueba. Tal vez empezar con un Generador de vainilla y, a continuación, introducir este si sigues siendo mordido por falta de llamadas de método.

Dejar respuesta

Please enter your comment!
Please enter your name here