Supongamos que tengo un objeto personalizado, Estudiante :
public class Student{
public int _id;
public String name;
public int age;
public float score;
}
Y una clase, Window , que se usa para mostrar información de un Estudiante :
public class Window{
public void showInfo(Student student);
}
Parece bastante normal, pero descubrí que Window no es fácil de probar individualmente, porque necesita un objeto real de Student para llamar a la función. Así que trato de modificar showInfo para que no acepte un objeto Student directamente:
public void showInfo(int _id, String name, int age, float score);
para que sea más fácil probar Windows individualmente:
showInfo(123, "abc", 45, 6.7);
Pero descubrí que la versión modificada tiene otros problemas:
Modificar estudiante (por ejemplo: agregar nuevas propiedades) requiere modificar la firma del método de showInfo
Si Student tuviera muchas propiedades, la firma del método de Student sería muy larga.
Entonces, usando objetos personalizados como parámetro o acepte cada propiedad en los objetos como parámetro, ¿cuál es más fácil de mantener?
showInfo
requiere una cadena real, un flotador real y dos entradas reales. ¿Cómo esString
mejor proporcionar unStudent
objeto real que proporcionar un objeto real ?int
parámetros. Desde el sitio de la llamada, no hay verificación de que realmente los esté pasando en el orden correcto. ¿Qué pasa si intercambiasid
yage
, ofirstName
ylastName
? Está introduciendo un posible punto de falla que puede ser muy difícil de detectar hasta que explote en su cara, y lo está agregando en cada sitio de llamada .showForm(bool, bool, bool, bool, int)
método - Me encantan esos ...Respuestas:
El uso de un objeto personalizado para agrupar parámetros relacionados es en realidad un patrón recomendado. Como refactorización, se llama Introducir objeto de parámetro .
Tu problema yace en otra parte. Primero, genérico no
Window
debe saber nada sobre el estudiante. En cambio, debe tener algún tipo deStudentWindow
conocimiento que solo se muestreStudents
. En segundo lugar, no hay absolutamente ningún problema en crear unaStudent
instancia para probarStudentWindow
siempre yStudent
cuando no contenga ninguna lógica compleja que pueda complicar drásticamente la pruebaStudentWindow
. Si tiene esa lógica, entoncesStudent
debería preferirse hacer una interfaz y burlarse de ella.fuente
Student
agrupación tiene sentido y es probable que surja en otras áreas de la aplicación.Student
, sería un Objeto completo de conservacióna.b.c
si su método tomaa
. Si su método llega al punto en el que necesita tener aproximadamente más de 4 parámetros o 2 niveles de profundidad de acceso a la propiedad, probablemente sea necesario tenerlo en cuenta. También tenga en cuenta que esta es una directriz, como todas las demás, requiere discreción del usuario. No lo sigas a ciegas.Tu dices que es
Pero solo puede crear un objeto de estudiante para pasar a su ventana:
No parece mucho más complejo llamar.
fuente
Student
refiere a aUniversity
, que se refiere a muchosFaculty
syCampus
s, conProfessor
syBuilding
s, ninguno de los cualesshowInfo
realmente usa, pero no ha definido ninguna interfaz que permita que las pruebas "sepan" eso y suministren solo al estudiante relevante datos, sin construir toda la organización. El ejemploStudent
es un objeto de datos simple y, como usted dice, las pruebas deberían estar felices de trabajar con él.En términos simples:
Editar:
Como @ Tom.Bowen89 afirma que no es mucho más complejo probar el método showInfo:
fuente
fuente
Steve McConnell en Code Complete abordó este mismo problema, discutiendo los beneficios y los inconvenientes de pasar objetos a métodos en lugar de usar propiedades.
Perdóname si me equivoco con algunos detalles, estoy trabajando de memoria ya que ha pasado más de un año desde que tuve acceso al libro:
Llega a la conclusión de que es mejor no usar un objeto, sino que solo envía aquellas propiedades absolutamente necesarias para el método. El método no debería tener que saber nada sobre el objeto fuera de las propiedades que usará como parte de sus operaciones. Además, con el tiempo, si alguna vez se cambia el objeto, esto podría tener consecuencias no deseadas en el método que usa el objeto.
También abordó que si terminas con un método que acepta muchos argumentos diferentes, entonces eso es probablemente una señal de que el método está haciendo demasiado y debería dividirse en más métodos más pequeños.
Sin embargo, a veces, a veces, realmente necesitas muchos parámetros. El ejemplo que da sería un método que construye una dirección completa, utilizando muchas propiedades de dirección diferentes (aunque esto podría obtenerse utilizando una matriz de cadenas cuando lo piensa).
fuente
Student
en este caso). Y así es como las pruebas informan el diseño , abrazando completamente la respuesta con más votos y manteniendo la integridad del diseño.Es mucho más fácil escribir y leer pruebas si pasa todo el objeto:
Para comparacion,
la línea podría escribirse, si pasa valores por separado, como:
donde la llamada al método real está enterrada en algún lugar como
Para ir al grano, que no puede poner la llamada al método real en la prueba es una señal de que su API es mala.
fuente
Debes pasar lo que tiene sentido, algunas ideas:
Más fácil de probar. Si los objetos necesitan ser editados, ¿qué requiere la menor refactorización? ¿Es útil reutilizar esta función para otros fines? ¿Cuál es la menor cantidad de información que necesito dar a esta función para cumplir su propósito? (Al dividirlo, puede permitirle volver a usar este código, tenga cuidado de no caer en el agujero de diseño para hacer que esta función funcione y luego cuele todo para usar este objeto exclusivamente).
Todas estas reglas de programación son solo guías para que pienses en la dirección correcta. Simplemente no construya una bestia de código: si no está seguro y solo necesita continuar, elija una dirección / suya o una sugerencia aquí, y si llega a un punto en el que piensa 'oh, debería haberlo hecho así way '- probablemente puedas volver y refactorizarlo con bastante facilidad. (Por ejemplo, si tiene la clase Profesor, solo necesita la misma propiedad establecida que Estudiante, y cambia su función para aceptar cualquier objeto del formulario Persona)
Me inclinaría más a mantener el objeto principal que se pasa, porque la forma en que codifique va a explicar más fácilmente lo que está haciendo esta función.
fuente
Una ruta común alrededor de esto es insertar una interfaz entre los dos procesos.
Esto se vuelve un poco complicado a veces, pero las cosas se ponen un poco más ordenadas en Java si usa una clase interna.
Luego puede probar la
Window
clase simplemente dándole unHasInfo
objeto falso .Sospecho que este es un ejemplo del Patrón Decorador .
Adicional
Parece haber cierta confusión causada por la simplicidad del código. Aquí hay otro ejemplo que puede demostrar mejor la técnica.
fuente
Student
yString
aquí para el tipo de retorno es solo para demostración. Con toda probabilidad habría parámetros adicionales agetInfo
como elPane
señalar a si el dibujo. El concepto aquí es pasar componentes funcionales como decoradores del objeto original .HasInfo
objetos.Student
Sabe ser uno.getInfo
vuelta al vacío, pásalaPane
para dibujar, entonces la implementación (en laStudent
clase) se acopla repentinamente al swing o lo que sea que estés usando. Si hace que devuelva alguna cadena y tome 0 parámetros, su interfaz de usuario no sabrá qué hacer con la cadena sin suposiciones mágicas y acoplamiento implícito. Si hace que engetInfo
realidad devuelva algún modelo de vista con propiedades relevantes, entonces suStudent
clase está nuevamente acoplada a la lógica de presentación. No creo que ninguna de estas alternativas sea deseableYa tiene muchas buenas respuestas, pero aquí hay algunas sugerencias más que pueden permitirle ver una solución alternativa:
Su ejemplo muestra un Estudiante (claramente un objeto modelo) que se pasa a una Ventana (aparentemente un objeto a nivel de vista). Un objeto de controlador o presentador intermediario puede ser beneficioso si aún no tiene uno, lo que le permite aislar su interfaz de usuario de su modelo. El controlador / presentador debe proporcionar una interfaz que se pueda usar para reemplazarlo para las pruebas de IU, y debe usar interfaces para referirse a los objetos del modelo y ver los objetos para poder aislarlo de ambos para las pruebas. Es posible que deba proporcionar alguna forma abstracta de crearlos o cargarlos (por ejemplo, objetos Factory, objetos de repositorio o similares).
Transferir partes relevantes de los objetos de su modelo a un Objeto de transferencia de datos es un enfoque útil para interactuar cuando su modelo se vuelve demasiado complejo.
Puede ser que su estudiante viole el Principio de segregación de interfaz. Si es así, podría ser beneficioso dividirlo en múltiples interfaces con las que sea más fácil trabajar.
Lazy Loading puede facilitar la construcción de gráficos de objetos grandes.
fuente
Esta es realmente una pregunta decente. El problema real aquí es el uso del término genérico "objeto", que puede ser un poco ambiguo.
Generalmente, en un lenguaje OOP clásico, el término "objeto" ha llegado a significar "instancia de clase". Las instancias de clase pueden ser bastante pesadas: propiedades públicas y privadas (y las intermedias), métodos, herencia, dependencias, etc. Realmente no querría usar algo así para simplemente pasar algunas propiedades.
En este caso, está utilizando un objeto como contenedor que simplemente contiene algunas primitivas. En C ++, los objetos como estos se conocían como
structs
(y todavía existen en lenguajes como C #). De hecho, las estructuras se diseñaron exactamente para el uso del que hablas: agruparon objetos relacionados y primitivas cuando tenían una relación lógica.Sin embargo, en los lenguajes modernos, realmente no hay diferencia entre una estructura y una clase cuando se escribe el código , por lo que está bien usar un objeto. (Detrás de escena, sin embargo, hay algunas diferencias que debe tener en cuenta; por ejemplo, una estructura es un tipo de valor, no un tipo de referencia). Básicamente, siempre que mantenga su objeto simple, será fácil Para probar manualmente. Sin embargo, los lenguajes y herramientas modernos le permiten mitigar esto un poco (a través de interfaces, marcos de imitación, inyección de dependencias, etc.)
fuente
Student
) en modelos de vista (StudentInfo
oStudentInfoViewModel
etc.), pero podría no ser necesario.