Tenemos muchos lugares en el código fuente de nuestra aplicación, donde una clase tiene muchos métodos con los mismos nombres y diferentes parámetros. Esos métodos siempre tienen todos los parámetros de un método 'anterior' más uno más.
Es el resultado de una larga evolución (código heredado) y este pensamiento (creo):
" Hay un método M que hace la cosa A. Necesito hacer A + B. OK, lo sé ... Agregaré un nuevo parámetro a M, crearé un nuevo método para eso, moveré el código de M al nuevo método con un parámetro más, haga A + B allí y llame al nuevo método desde M con un valor predeterminado del nuevo parámetro " .
Aquí hay un ejemplo (en lenguaje similar a Java):
class DocumentHome {
(...)
public Document createDocument(String name) {
// just calls another method with default value of its parameter
return createDocument(name, -1);
}
public Document createDocument(String name, int minPagesCount) {
// just calls another method with default value of its parameter
return createDocument(name, minPagesCount, false);
}
public Document createDocument(String name, int minPagesCount, boolean firstPageBlank) {
// just calls another method with default value of its parameter
return createDocument(name, minPagesCount, false, "");
}
public Document createDocument(String name, int minPagesCount, boolean firstPageBlank, String title) {
// here the real work gets done
(...)
}
(...)
}
Siento que esto está mal. No solo eso no podemos seguir agregando nuevos parámetros como este para siempre, sino que el código es difícil de extender / cambiar debido a todas las dependencias entre los métodos.
Aquí hay algunas formas de hacerlo mejor:
Introducir un objeto de parámetro:
class DocumentCreationParams { String name; int minPagesCount; boolean firstPageBlank; String title; (...) } class DokumentHome { public Document createDocument(DocumentCreationParams p) { // here the real work gets done (...) } }
Establezca los parámetros para el
DocumentHome
objeto antes de llamarcreateDocument()
@In DocumentHome dh = null; (...) dh.setName(...); dh.setMinPagesCount(...); dh.setFirstPageBlank(...); Document newDocument = dh.createDocument();
Separe el trabajo en diferentes métodos y llámelos según sea necesario:
@In DocumentHome dh = null; Document newDocument = dh.createDocument(); dh.changeName(newDocument, "name"); dh.addFirstBlankPage(newDocument); dh.changeMinPagesCount(new Document, 10);
Mis preguntas:
- ¿El problema descrito es realmente un problema?
- ¿Qué opinas sobre las soluciones sugeridas? ¿Cuál preferirías (según tu experiencia)?
- ¿Se te ocurre alguna otra solución?
Respuestas:
Tal vez intente el patrón constructor ? (nota: resultado de Google bastante aleatorio :)
No puedo dar un resumen completo de por qué prefiero el constructor sobre las opciones que ofrece, pero ha identificado un gran problema con mucho código. Si cree que necesita más de dos parámetros para un método, es probable que su código esté estructurado incorrectamente (¡y algunos argumentarían uno!).
El problema con un objeto params es (a menos que el objeto que cree sea de alguna manera real) simplemente suba el problema un nivel y termine con un grupo de parámetros no relacionados que forman el 'objeto'.
Sus otros intentos me parecen como alguien que busca el patrón de construcción pero no llega a llegar allí :)
fuente
DocumentoFactory
). Tener un lugarbuilder
diferente es difícil controlar los cambios futuros en la construcción del documento (como agregar un nuevo campo obligatorio al documento, por ejemplo) y agregar código adicional en las pruebas para satisfacer las necesidades del creador de documentos en las clases que están usando el constructor.Usar un objeto de parámetro es una buena manera de evitar la sobrecarga (excesiva) de métodos:
Sin embargo, no iría demasiado lejos con eso.
Tener una sobrecarga aquí y allá no es algo malo. Es compatible con el lenguaje de programación, así que úselo para su ventaja.
No tenía conocimiento del patrón de construcción, pero lo he usado "por accidente" en algunas ocasiones. Lo mismo se aplica aquí: no exagere. El código en su ejemplo se beneficiaría de él, pero pasar mucho tiempo implementándolo para cada método que tiene un solo método de sobrecarga no es muy eficiente.
Solo mis 2 centavos.
fuente
Sinceramente, no veo un gran problema con el código. En C # y C ++ puede usar parámetros opcionales, eso sería una alternativa, pero que yo sepa, Java no admite ese tipo de parámetros.
En C #, puede hacer que todas las sobrecargas sean privadas y un método con parámetros opcionales es público para llamar a las cosas.
Para responder a su pregunta, parte 2, tomaría el objeto de parámetro o incluso un diccionario / HashMap.
Al igual que:
Como descargo de responsabilidad, primero soy un programador de C # y JavaScript y luego un programador de Java.
fuente
Creo que este es un buen candidato para el patrón de construcción. El patrón de construcción es útil cuando desea crear objetos del mismo tipo, pero con diferentes representaciones.
En su caso, tendría un constructor con los siguientes métodos:
Luego puede usar el constructor de la siguiente manera:
En la nota al margen, no me importan algunas sobrecargas simples: qué diablos, .NET framework las usa en todas partes con ayudantes HTML. Sin embargo, volvería a evaluar lo que estoy haciendo si tengo que pasar más de dos parámetros a cada método.
fuente
Creo que la
builder
solución puede funcionar en la mayoría de los escenarios, pero en casos más complejos, su constructor también será complejo de configurar , porque es fácil cometer algunos errores en el orden de los métodos, qué métodos deben exponerse o no , etc. Entonces, muchos de nosotros preferiremos una solución más simple.Si solo crea un generador simple para crear un documento y difunde este código en diferentes partes (clases) de la aplicación, será difícil:
Pero esto no responde a la pregunta OP.
La alternativa a la sobrecarga
Algunas alternativas:
createDocument
, como:createLicenseDriveDocument
,createDocumentWithOptionalFields
, etc. Por supuesto, esto puede conducir a que los nombres de métodos gigantes, así que esto no es Una solución para todos los casos.Document
, como:Document.createLicenseDriveDocument()
.createDocument(InterfaceDocument interfaceDocument)
y crear diferentes implementaciones paraInterfaceDocument
. Por ejemplo:createDocument(new DocumentMinPagesCount("name"))
. Por supuesto, no necesita una sola implementación para cada caso, porque puede crear más de un constructor en cada implementación, agrupando algunos campos que tengan sentido en esa implementación. Este patrón se llama constructores telescópicos .O simplemente quédese con la solución de sobrecarga . Aun siendo, a veces, una solución fea, no hay muchos inconvenientes en su uso. En ese caso, prefiero usar métodos de sobrecarga en una clase separada, como una
DocumentoFactory
que se puede inyectar como dependencia de las clases que necesitan crear documentos. Puedo organizar y validar los campos sin la complejidad de crear un buen generador y mantener el código en un solo lugar también.fuente