¿Constructor con todas las propiedades de clase o constructor predeterminado con setters?

StackOverflow https://stackoverflow.com/questions/830657

  •  06-07-2019
  •  | 
  •  

Pregunta

Los siguientes son los dos enfoques:

  • constructor con todas las propiedades de clase

Pros: Tengo que poner un número exacto de tipos de parámetros, de modo que si cometo un error, el compilador me lo advierte (por cierto, ¿hay alguna manera de evitar el problema de haber cambiado por error dos enteros en la lista de parámetros? )

Contras: si tengo muchas propiedades, la línea de instanciación puede llegar a ser realmente larga y podría abarcar más de dos o más líneas

  • setters y el constructor vacío predeterminado

Pros: Puedo ver claramente lo que estoy configurando, así que si estoy haciendo algo mal, puedo identificarlo tan pronto como lo estoy escribiendo (no puedo cometer el error anterior de cambiar dos variables del mismo tipo)

Contras: la creación de instancias de un objeto con muchas propiedades podría tomar varias líneas (no sé si esto es realmente una estafa) y si olvido establecer una propiedad, el compilador no dice nada.

¿Qué harás y por qué? ¿Conoces algún patrón de luz (considera que debe usarse cada vez que se instancia un objeto con más de 7 propiedades) para sugerir? Pregunto esto porque tiendo a disgustarme con los grandes constructores donde no puedo averiguar rápidamente dónde está la variable que estoy buscando, por otro lado, encuentro el "establecer todas las propiedades". vulnerable a perder algunas de las propiedades.

Siéntase libre de discutir mis suposiciones en pros y contras, ya que son solo pensamientos míos :)

Actualización: una pregunta que he encontrado que está relacionada con esto: Construir objetos grandes e inmutables sin usar constructores que tengan listas de parámetros largas

¿Fue útil?

Solución

Puede mirar el patrón de Constructor recomendado por Joshua Bloch, y descrito en Java efectivo . Hay una presentación con los puntos principales en http: //developers.sun .com / learning / javaoneonline / 2007 / pdf / TS-2689.pdf ; sin duda podrías encontrar una mejor referencia.

Básicamente, tiene otra clase, probablemente una clase interna, que proporciona métodos con el nombre de las propiedades que se establecen y que devuelven el generador original para que pueda encadenar las llamadas. Es una porción de código bastante legible.

Por ejemplo, supongamos que tengo un simple Message con algunas propiedades. El código del cliente que construye esto podría usar un generador para preparar un Mensaje de la siguiente manera:

Message message = new Message.Builder()
    .sender( new User( ... ) )
    .recipient( new User( ... ) )
    .subject( "Hello, world!" )
    .text( messageText )
    .build();

Un fragmento de Message.Builder podría ser similar al siguiente:

public class Builder {

    private User sender = null;
    // Other properties

    public Builder sender( User sender ) {
        this.sender = sender;
        return this;
    }
    // Methods for other properties

    public Message build() {
        Message message = new Message();
        message.setSender( sender );
        // Set the other properties
        return message;
    }

}

Otros consejos

Te has perdido el mayor profesional de tener un constructor con muchos parámetros: te permite crear tipos inmutables.

La forma normal de crear tipos inmutables sin gran maldad del constructor es tener un tipo auxiliar: un generador que mantiene los valores que desea en su objeto final, luego construye el objeto inmutable cuando esté listo.

Investigaciones académicas recientes (CMU y Microsoft) sobre la usabilidad de API sugieren que los constructores predeterminados con setters serían el camino a seguir en términos de usabilidad. Esto es de "Implicaciones de usabilidad de requerir parámetros en constructores de objetos" por Jeff Stylos y Steven Clarke y fue presentado en la Conferencia Internacional de Ingeniería de Software:

Resumen : La usabilidad de las API es cada vez más importante para la productividad del programador. En base a la experiencia con los estudios de usabilidad de API específicas, se exploraron técnicas para estudiar la usabilidad de las opciones de diseño comunes a muchas API. Se realizó un estudio comparativo para evaluar cómo los programadores profesionales usan las API con los parámetros requeridos en los constructores de objetos en lugar de los parámetros predeterminados sin parámetros. constructores Se planteó la hipótesis de que los parámetros requeridos crearían API más útiles y autodocumentadas al guiar a los programadores hacia el uso correcto de los objetos y prevenir errores. Sin embargo, en el estudio, se descubrió que, contrariamente a lo esperado, los programadores preferían fuertemente y eran más efectivos con API que no requerían parámetros del constructor. El comportamiento de los participantes se analizó utilizando el marco de dimensiones cognitivas, y reveló que los parámetros del constructor requeridos interfieren con las estrategias de aprendizaje comunes, causando un compromiso prematuro indeseable.

Lo mencionas en tu publicación, pero creo que este es un punto importante que merece más atención: a menos que cada parámetro de entrada sea de un tipo diferente, el gran problema con los grandes constructores es que es muy fácil para transponer un par de variables. El compilador es una red de seguridad poco confiable: detectará algunos errores, pero los que se escapen serán mucho más difíciles de identificar y depurar. Especialmente porque la lista de entrada para un gran constructor es bastante opaca a menos que tenga la API abierta en otra ventana.

Los captadores y establecedores son mucho más fáciles de depurar, especialmente si instituyen salvaguardas que arrojan una excepción de tiempo de ejecución si el objeto no está correctamente poblado. Y soy un gran admirador de "fácil de depurar".

Antes de este hilo, nunca había oído hablar del patrón Builder que menciona Rob. Nunca lo usé yo mismo (obviamente), pero es muy intrigante.

Prefiero tomar argumentos de constructor, por las razones de inmutabilidad mencionadas anteriormente. Si eso le da un constructor que toma muchos argumentos (digamos más de cuatro o menos), eso es un olor a código para mí: algunos de esos argumentos deberían agruparse en sus propios tipos.

Por ejemplo, si tiene algo como esto:

class Contact
{
    public Contact(string firstName, string lastName, string phoneNumber,
        string street, string city, string state, int zipCode) { ... }
}

Lo refactorizaría para:

class Contact
{
    public Contact(Person person, PhoneNumber number, Address address) { ... }
}

class Person
{
    public Person(string firstName, string lastName) { ... }
}

class PhoneNumber
{
    public PhoneNumber(string digits) { ... }
}

class Address
{
    public Address(string street, string city, string state, int zipCode) { ... }
}

Las clases demasiado grandes son un problema de diseño muy común en las bases de código OOP.

También hay otros aspectos. Si desea poder hacer ciertas cosas con su clase en tiempo de diseño en lugar de solo en tiempo de ejecución, por ejemplo, agregando su clase como un objeto en la Paleta de objetos (esto es Java usando Netbeans) debe proporcionar un constructor sin argumentos en para poder hacerlo.

También hay otras estrategias aquí. Antes de tratar de descubrir cómo lidiar con muchos parámetros, creo que es importante volver a visitar su diseño y ver si su clase está haciendo demasiado. Vea si puede agrupar algunos de los parámetros en una nueva clase y mover algún comportamiento a esa clase.

  

setters y el constructor vacío predeterminado

JRL lo tocó oblicuamente , pero una razón para considerar el uso de setters es hacer que el objeto se ajuste a especificación JavaBean . Esto hace que las instancias sean susceptibles de edición mediante herramientas de introspección y persistencia utilizando ciertas técnicas de serialización .

¿Quién dice que no puedes hacer las dos cosas? Yo diría que las propiedades obligatorias van al constructor, las opcionales se manejan con setters. Por cierto, ¿quién dice que siempre necesitas un setter por propiedad? Si dos propiedades pertenecen juntas conceptualmente, ¿por qué no establecerlas juntas?

También me gusta el patrón Builder, pero la regla más importante es: siempre usa tu cerebro y encuentra el diseño que mejor se adapte al problema específico. No hay una solución única para todos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top