java - sourcemaking - Parámetros del constructor-Regla de oro
refactoring java (9)
¿Qué hay de pasar un objeto Param => Value Map al constructor? Si la persona que llama omite cualquier parámetro crítico, haga que el constructor lance una Excepción.
Significa que las malas llamadas al constructor solo se detectarán en tiempo de ejecución en lugar de compilar, lo que es un inconveniente. Pero un enfoque de captador / definidor tiene el mismo problema, y debería ser mucho más fácil trabajar con él.
En general, ¿cuál es el número máximo de parámetros que debe aceptar un constructor de clase? Estoy desarrollando una clase que requiere muchos datos de inicialización (actualmente 10 parámetros). Sin embargo, un constructor con 10 parámetros no se siente bien. Eso me lleva a creer que debería crear un getter / setter para cada dato. Desafortunadamente, el patrón de captador / definidor no obliga al usuario a ingresar los datos y, sin él, la caracterización del objeto es incompleta y, por lo tanto, inútil. ¿Pensamientos?
Con tantos parámetros, es hora de considerar el patrón Builder . Cree una clase de constructor que contenga todos esos captadores y definidores, con un método build () que devuelva un objeto de la clase que realmente está intentando construir.
Ejemplo:
public class ReallyComplicatedClass {
private int int1;
private int int2;
private String str1;
private String str2;
// ... and so on
// Note that the constructor is private
private ReallyComplicatedClass(Builder builder) {
// set all those variables from the builder
}
public static class Builder {
private int int1;
private int int2;
private String str1;
private String str2;
// and so on
public Builder(/* required parameters here */) {
// set required parameters
}
public Builder int1(int newInt) {
int1 = newInt;
return this;
}
// ... setters for all optional parameters, all returning ''this''
public ReallyComplicatedClass build() {
return new ReallyComplicatedClass(this);
}
}
}
Y en su código de cliente:
ReallyComplicatedClass c = new ReallyComplicatedClass.Builder()
.int1(myInt1)
.str2(myStr2)
.build();
Consulte las páginas 7 a 9 de Effective Java Reloaded [pdf], la presentación de Josh Bloch en JavaOne 2007. (Este también es el artículo 2 de Effective Java 2nd Edition , pero no lo tengo a mano, así que no puedo citarlo).
Depende.
Si algunos parámetros son del mismo tipo y se pueden mezclar, toleraría un número bastante pequeño (digamos 5).
Si los parámetros son de diferentes tipos, por lo que no se pueden mezclar, toleraría algunos más. Sin embargo, diez estarían cerca del límite.
Necesitaría saber más sobre lo que hace la clase y cuáles son los parámetros, pero existe la posibilidad de que la clase tenga demasiadas responsabilidades. ¿Sería posible dividir la clase en clases independientes más pequeñas?
El uso de configuradores no resuelve el problema de la clase que tiene muchas dependencias / parámetros. Simplemente mueve el problema a un lugar diferente y no fuerza los parámetros que se ingresan.
Para los métodos, trato de seguir los consejos del libro Clean Code para no tener más de 3 parámetros por método (IIRC). Para los constructores, es posible que tenga más parámetros, ya que normalmente el constructor será llamado por mi marco de inyección de dependencias y no por mí.
El patrón de construcción mencionado por mmyers también es una buena solución cuando se construyen objetos complejos, y no hay manera de hacerlos menos complejos.
No creo que puedas decir que un número apropiado es "siete, no más" o "cinco".
Una buena regla de oro para los constructores es pasar a un objeto su identidad , no su estado . Esos parámetros que usted pasa son aquellos que son esenciales para la existencia del objeto, y sin los cuales la mayoría de las operaciones del objeto pueden no ser posibles.
Si realmente tiene una clase con una identidad natural muy complicada, por lo que requiere muchos parámetros, considere el diseño de su clase.
Un ejemplo de un mal constructor es:
public NightWatchman(int currentFloor, int salary, int hapiness) {...}
Aquí se está construyendo el NightWatchman con algunos valores predeterminados que casi seguramente cambiarán en poco tiempo. Parece gracioso que el objeto reciba información sobre sus valores de una manera y luego los tenga de otra manera (a través de sus colocadores) en el futuro.
Un ejemplo de un mejor constructor es:
public GateWatchman(Gate watchedGate, boolean shootOnSight) {...}
La puerta que vigila el vigilante es información necesaria para que exista una. En la clase lo marcaría como final privado . He elegido pasar la variable shootOnSight al constructor, porque aquí era importante que en todo momento el objeto supiera si disparar a los ladrones. Aquí, la identidad se está utilizando como tipo.
Podría tener una clase llamada ShootingGateWatchman y PoliceCallingGateWatchman , es decir, el parámetro se interpreta como parte de la identidad de los objetos.
Por lo general, diría que no más de cinco, de la regla 7 +/- 2 de la memoria a corto plazo , y algo de pesimismo sobre la capacidad de atención del programador. Nota: varargs una lista de varargs como una entidad.
Si realmente está limitado a construir el objeto de una sola vez, generalmente puede recopilar parámetros relacionados en objetos de valor simple y pasarlos al constructor. Trate de asegurarse de que los objetos de valor tengan algún sentido conceptual y no sean solo colecciones aleatorias de información ...
Puede decidir cuándo es suficiente y usar el Objeto de Parámetro de Introducción para su constructor (o cualquier otro método para esa materia).
sugeriría encontrar dependencias entre los parámetros y luego crear estructuras o clases para mantenerlas y pasarlas a su constructor en lugar de un conjunto de cosas que no parecen estar relacionadas a primera vista.
Code Complete 2 recomienda un límite bastante razonable de siete parámetros para cualquier método.
Intente establecer valores predeterminados razonables para algunos de los miembros. Esto le permitirá utilizar getter / setters sin preocuparse de que la caracterización esté incompleta.