java - sirve - ¿La mejor práctica para pasar muchos argumentos al método?
jbutton java (15)
Code Complete * sugiere un par de cosas:
- "Limite el número de parámetros de una rutina a aproximadamente siete. Siete es un número mágico para la comprensión de las personas" (p 108).
- "Coloque los parámetros en orden de entrada, modificación y salida ... Si varias rutinas usan parámetros similares, coloque los parámetros similares en un orden consistente" (p 105).
- Poner estado o variables de error al último.
- Como mencionó tvanfosson , pase solo las partes de las variables estructuradas (objetos) que necesita la rutina. Dicho esto, si está utilizando la mayoría de la variable estructurada en la función, simplemente pase toda la estructura, pero tenga en cuenta que esto promueve el acoplamiento hasta cierto punto.
* Primera edición, sé que debería actualizar. Además, es probable que algunos de estos consejos hayan cambiado desde que se escribió la segunda edición cuando OOP empezaba a ser más popular.
Ocasionalmente, tenemos que escribir métodos que reciban muchos argumentos, por ejemplo:
public void doSomething(Object objA , Object objectB ,Date date1 ,Date date2 ,String str1 ,String str2 )
{
}
Cuando encuentro este tipo de problema, a menudo encapsula argumentos en un mapa.
Map<Object,Object> params = new HashMap<Object,Object>();
params.put("objA",ObjA) ;
......
public void doSomething(Map<Object,Object> params)
{
// extracting params
Object objA = (Object)params.get("objA");
......
}
Esta no es una buena práctica, encapsular params en un mapa es totalmente un desperdicio de eficiencia. Lo bueno es, la firma limpia, fácil de agregar otros parámetros con la menor modificación. ¿Cuál es la mejor práctica para este tipo de problema?
Cree una clase de bean y configure todos los parámetros (método setter) y pase este objeto bean al método.
En Java efectivo , Capítulo 7 (Métodos), Ítem 40 (Firmas de métodos de diseño cuidadosamente), Bloch escribe:
Hay tres técnicas para acortar listas de parámetros excesivamente largas:
- divide el método en múltiples métodos, cada uno de los cuales requiere solo un subconjunto de los parámetros
- crear clases de ayuda para mantener un grupo de parámetros (generalmente clases de miembros estáticos)
- adaptar el patrón del Constructor desde la construcción del objeto hasta la invocación del método.
Para más detalles, te animo a comprar el libro, realmente vale la pena.
Esto a menudo es un problema al construir objetos.
En ese caso, use el patrón de objetos del constructor , funciona bien si tiene una gran lista de parámetros y no siempre los necesita a todos.
También puede adaptarlo a la invocación del método.
También aumenta mucho la legibilidad.
public class BigObject
{
// public getters
// private setters
public static class Buider
{
private A f1;
private B f2;
private C f3;
private D f4;
private E f5;
public Buider setField1(A f1) { this.f1 = f1; return this; }
public Buider setField2(B f2) { this.f2 = f2; return this; }
public Buider setField3(C f3) { this.f3 = f3; return this; }
public Buider setField4(D f4) { this.f4 = f4; return this; }
public Buider setField5(E f5) { this.f5 = f5; return this; }
public BigObject build()
{
BigObject result = new BigObject();
result.setField1(f1);
result.setField2(f2);
result.setField3(f3);
result.setField4(f4);
result.setField5(f5);
return result;
}
}
}
// Usage:
BigObject boo = new BigObject.Builder()
.setField1(/* whatever */)
.setField2(/* whatever */)
.setField3(/* whatever */)
.setField4(/* whatever */)
.setField5(/* whatever */)
.build();
También puede poner la lógica de verificación en los métodos de configuración de Builder .. () y build ().
Esto es a menudo una indicación de que su clase tiene más de una responsabilidad (es decir, su clase hace demasiado).
Ver el Principio de Responsabilidad Individual.
para mas detalles.
Hay un patrón llamado como objeto Parameter .
Idea es usar un objeto en lugar de todos los parámetros. Ahora, incluso si necesita agregar parámetros más adelante, solo necesita agregarlo al objeto. La interfaz del método permanece igual.
Podrías crear una clase para contener esa información. Sin embargo, tiene que ser lo suficientemente significativo, pero mucho mejor que usar un mapa (OMG).
Primero, trataría de refactorizar el método. Si usa tantos parámetros, puede ser demasiado largo de cualquier forma. Romperlo mejoraría el código y posiblemente reduciría la cantidad de parámetros para cada método. También es posible que pueda refactorizar toda la operación a su propia clase. En segundo lugar, buscaría otras instancias en las que estoy usando el mismo (o superconjunto) de la misma lista de parámetros. Si tiene varias instancias, entonces probablemente indique que estas propiedades pertenecen juntas. En ese caso, crea una clase para mantener los parámetros y usarlo. Por último, evaluaría si la cantidad de parámetros hace que valga la pena crear un objeto de mapa para mejorar la legibilidad del código. Creo que esta es una decisión personal: hay dolor en cada sentido con esta solución y en la que el punto de equilibrio puede variar. Para seis parámetros, probablemente no lo haría. Para 10 probablemente lo haría (si ninguno de los otros métodos funcionó primero).
Se llama "Introducir objeto de parámetro". Si se encuentra pasando la misma lista de parámetros en varios lugares, simplemente cree una clase que los contenga a todos.
XXXParameter param = new XXXParameter(objA, objB, date1, date2, str1, str2);
// ...
doSomething(param);
Incluso si no se encuentra pasando la misma lista de parámetros con tanta frecuencia, esa fácil refactorización aún mejorará la legibilidad de su código, lo que siempre es bueno. Si miras tu código 3 meses después, será más fácil comprender cuándo necesitas corregir un error o agregar una función.
Es una filosofía general, por supuesto, y como no me has proporcionado ningún detalle, tampoco puedo darte más consejos detallados. :-)
Si está pasando demasiados parámetros, intente refactorizar el método. Tal vez esté haciendo muchas cosas que no se supone que debe hacer. Si ese no es el caso, intente sustituir los parámetros con una sola clase. De esta forma, puede encapsular todo en una única instancia de clase y pasar la instancia y no los parámetros.
Si tiene muchos parámetros opcionales puede crear API fluida: reemplace el método simple con la cadena de métodos
exportWithParams().datesBetween(date1,date2)
.format("xml")
.columns("id","name","phone")
.table("angry_robots")
.invoke();
Al usar la importación estática, puede crear API fluidas internas:
... .datesBetween(from(date1).to(date2)) ...
Una buena práctica sería refactorizar. ¿Qué pasa con estos objetos significa que deben pasarse a este método? ¿Deberían estar encapsulados en un solo objeto?
Usar un mapa con Cadenas mágicas es una mala idea. Pierdes cualquier comprobación de tiempo de compilación, y no está muy claro cuáles son los parámetros requeridos. Debería escribir documentación muy completa para compensarlo. ¿Recordarás en unas pocas semanas qué son esas cuerdas sin mirar el código? ¿Qué pasa si hiciste un error tipográfico? Usar el tipo equivocado? No lo sabrá hasta que ejecute el código.
En cambio, usa un modelo. Haz una clase que será un contenedor para todos esos parámetros. De esa forma mantendrás el tipo de seguridad de Java. También puede pasar ese objeto a otros métodos, ponerlo en colecciones, etc.
Por supuesto, si el conjunto de parámetros no se utiliza en otro lugar o se pasa de largo, un modelo dedicado puede ser excesivo. Hay que encontrar un equilibrio, así que usa el sentido común.
Usar un mapa es una forma simple de limpiar la firma de la llamada, pero luego tiene otro problema. Debe buscar dentro del cuerpo del método para ver qué espera el método en ese mapa, cuáles son los nombres clave o qué tipos tienen los valores.
Una manera más limpia sería agrupar todos los parámetros en un bean de objeto, pero eso aún no soluciona el problema por completo.
Lo que tienes aquí es un problema de diseño. Con más de 7 parámetros para un método, comenzará a tener problemas para recordar qué representan y qué orden tienen. Desde aquí, obtendrás muchos errores simplemente llamando al método en un orden de parámetros incorrecto.
Necesita un mejor diseño de la aplicación, no es una mejor práctica para enviar muchos parámetros.
Mire su código y vea por qué se pasan todos esos parámetros. A veces es posible refactorizar el método en sí.
Usar un mapa deja su método vulnerable. ¿Qué pasa si alguien que usa su método escribe mal el nombre de un parámetro, o publica una cadena donde su método espera un UDT?
Definir un objeto de transferencia . Le proporcionará una comprobación de tipo al menos; incluso es posible que realice una validación en el punto de uso en lugar de hacerlo dentro de su método.