2008-10-21 16 views
82

En algunos de nuestros proyectos, hay una jerarquía de clases que agrega más parámetros a medida que avanza en la cadena. En la parte inferior, algunas de las clases pueden tener hasta 30 parámetros, 28 de los cuales se pasan al superconstructor.Gestión de constructores con muchos parámetros en Java

Reconozco que usar DI automatizado a través de algo como Guice sería agradable, pero debido a algunas razones técnicas, estos proyectos específicos están limitados a Java.

Una convención de organizar los argumentos alfabéticamente por tipo no funciona porque si se refactoriza un tipo (el Círculo que pasaba para el argumento 2 ahora es una Forma) puede estar repentinamente fuera de servicio.

Esta pregunta puede ser específica y llena de "Si ese es su problema, lo está haciendo mal a nivel de diseño" críticas, pero estoy buscando cualquier punto de vista.

Respuesta

219

El Builder Diseño podría ayudar. Consideremos el siguiente ejemplo

public class StudentBuilder 
{ 
    private String _name; 
    private int _age = 14;  // this has a default 
    private String _motto = ""; // most students don't have one 

    public StudentBuilder() { } 

    public Student buildStudent() 
    { 
     return new Student(_name, _age, _motto); 
    } 

    public StudentBuilder name(String _name) 
    { 
     this._name = _name; 
     return this; 
    } 

    public StudentBuilder age(int _age) 
    { 
     this._age = _age; 
     return this; 
    } 

    public StudentBuilder motto(String _motto) 
    { 
     this._motto = _motto; 
     return this; 
    } 
} 

Esto nos permite escribir código como

Student s1 = new StudentBuilder().name("Eli").buildStudent(); 
Student s2 = new StudentBuilder() 
       .name("Spicoli") 
       .age(16) 
       .motto("Aloha, Mr Hand") 
       .buildStudent(); 

Si dejamos fuera un campo requerido (presumiblemente Se requiere el nombre), entonces podemos tener el constructor del estudiante una excepción. Y nos permite tener argumentos predeterminados/opcionales sin necesidad de realizar un seguimiento de ningún tipo de orden de argumento, ya que cualquier orden de esas llamadas funcionará igual de bien.

+8

Por supuesto, con las importaciones estáticas ni siquiera tiene que "ver" estos "constructores" en absoluto. Por ejemplo, podría tener el nombre de métodos estáticos (String name) que devuelve un constructor y Student (StudentBuilder) que devuelve un alumno. De ahí Estudiante (nombre ("Joe"). Edad (15) .motto ("Me he mojado")); –

+2

@oxbow_lakes: en su ejemplo, ¿qué clase tiene el nombre del método estático (String name)? – user443854

+0

Técnicamente hablando, es posible usar la clase de Estudiante para construir un nuevo alumno. He agregado los métodos dentro de la clase de Estudiante y funcionó bien. De esta forma no necesitaba tener otra clase de constructores. No estoy seguro si esto es deseable sin embargo. ¿Hay alguna razón para usar otra clase (StudentBuilder) para construirlo? – WVrock

1

La refactoración para reducir el número de parámetros y la profundidad de su jerarquía de herencia es prácticamente todo lo que puedo pensar, porque nada realmente ayudará a mantener los parámetros de 20 y 20 años de forma recta. Tendrá que atender cada llamada mientras mira la documentación.

Una cosa que podría hacer, es agrupar algunos parámetros lógicamente agrupados en su propio objeto de nivel superior, pero eso tiene sus propios problemas.

3

La mejor solución es no tener demasiados parámetros en el constructor. Solo los parámetros realmente necesarios en el constructor son los parámetros que se necesitan para inicializar correctamente el objeto. Puede tener constructores con múltiples parámetros, pero también un constructor con solo los parámetros mínimos. Los constructores adicionales llaman a este simple constructor y luego a los setters para establecer los otros params. De esta forma puede evitar el problema de cadena con más y más params, pero también tiene algunos constructores de conveniencia.

4

Como está limitado a Java 1.4, si desea DI, entonces Spring sería una opción muy decente. DI solo es útil en lugares donde los parámetros del constructor son servicios o algo que no varía durante el tiempo de ejecución.

Si tiene todos esos diferentes constructores debido al hecho de que desea opciones variables sobre cómo construir un objeto, debe considerar seriamente usar el patrón de Constructor.

+0

Los parámetros son en su mayoría servicios como usted mencionó, por lo que DI es lo que necesitaría. Creo que el patrón de Builder mencionado en otras dos respuestas es exactamente lo que esperaba. –

14

Lo que probablemente quieras hacer es tener una clase de Constructor. De allí tendría que hacer algo como esto:

MyObject obj = new MyObjectBuilder().setXxx(myXxx) 
            .setYyy(myYyy) 
            .setZzz(myZzz) 
            // ... etc. 
            .build(); 

Consulte la página 8 y siguientes de this Josh Bloch presentation (PDF), o this review of Effective Java

21

¿Puede encapsular parámetros relacionados dentro de un objeto?

por ejemplo, si los parámetros son como

 

MyClass(String house, String street, String town, String postcode, String country, int foo, double bar) { 
    super(String house, String street, String town, String postcode, String country); 
    this.foo = foo; 
    this.bar = bar; 
 

entonces en vez podría tener:

 

MyClass(Address homeAddress, int foo, double bar) { 
    super(homeAddress); 
    this.foo = foo; 
    this.bar = bar; 
} 
 
5

Bueno, con el Builder podría ser uno solución.

Pero una vez que llegue a 20 o 30 parámetros, supongo que existe una gran relación entre los parámetros. Entonces (como se sugiere) envolverlos en objetos de datos lógicamente sanos probablemente tenga más sentido. De esta forma, el objeto de datos ya puede verificar la validez de las restricciones entre los parámetros.

Para todos mis proyectos en el pasado, una vez que llegué al punto de tener demasiados parámetros (¡y eso fue 8 no 28!) Pude desinfectar el código creando un mejor modelo de datos.

Cuestiones relacionadas