En el diseño de mi programa, a menudo llego al punto en que tengo que pasar instancias de objetos a través de varias clases. Por ejemplo, si tengo un controlador que carga un archivo de audio, y luego lo pasa a un reproductor, y el jugador lo pasa al reproductor Ejecutable, que lo pasa de nuevo a otro lugar, etc. Parece un poco malo, pero no saber cómo evitarlo ¿O está bien hacer esto?
EDITAR: Quizás el ejemplo del reproductor no sea el mejor porque podría cargar el archivo más tarde, pero en otros casos eso no funciona.
fuente
Es interesante que nadie haya hablado sobre objetos inmutables todavía. Yo diría que pasar un objeto inmutable a través de las diferentes capas es realmente algo bueno en lugar de crear muchos objetos de corta duración para cada capa.
Hay algunas buenas discusiones sobre la inmutabilidad de Eric Lippert en su blog.
Por otro lado, diría que pasar objetos mutables entre capas es un mal diseño. Básicamente, está construyendo una capa con la promesa de que las capas circundantes no la alterarán de manera de romper su código.
fuente
Pasar instancias de objetos es algo normal. Reduce la necesidad de mantener el estado (es decir, las variables de instancia) y desacopla el código de su contexto de ejecución.
Un problema que puede enfrentar es la refactorización, cuando debe cambiar las firmas de varios métodos a lo largo de la cadena de invocación en respuesta a los requisitos de parámetros cambiantes de un método cerca de la parte inferior de esa cadena. Sin embargo, puede mitigarse con el uso de herramientas modernas de desarrollo de software que ayudan en la refactorización.
fuente
Tal vez sea menor, pero existe el riesgo de asignar esta referencia en algún lugar de una de las capas, lo que podría causar una referencia colgante o una pérdida de memoria más adelante.
fuente
Si está pasando objetos simplemente porque es necesario en un área remota de su código, el uso de la inversión de control y los patrones de diseño de inyección de dependencia junto con, opcionalmente, un contenedor de IoC apropiado puede resolver los problemas relacionados con el transporte de instancias de objetos. Lo he usado en un proyecto de tamaño mediano y nunca más consideraría escribir un gran código de servidor sin usarlo.
fuente
Pasar datos a través de un montón de capas no es algo malo, es realmente la única forma en que un sistema en capas puede funcionar sin violar la estructura en capas. La señal de que hay problemas es cuando estás pasando tus datos a varios objetos en la misma capa para lograr tu objetivo.
fuente
Respuesta rápida: no hay nada de malo en pasar instancias de objetos. Como también se mencionó, el punto es omitir la asignación de esta referencia en todas las capas que potencialmente pueden causar una referencia colgante o pérdidas de memoria.
En nuestros proyectos, utilizamos esta práctica para pasar DTO (objeto de transferencia de datos) entre capas y es una práctica muy útil. También reutilizamos nuestros objetos dto para construir más complejos una vez, como información resumida.
fuente
Principalmente soy un desarrollador web de interfaz de usuario, pero me parece que su incomodidad intuitiva podría ser menos sobre el paso de la instancia y más sobre el hecho de que va a ser un poco procesal con ese controlador. ¿Debería su controlador estar sudando todos estos detalles? ¿Por qué incluso hace referencia a más del nombre de otro objeto para reproducir el audio?
En el diseño de OOP, tiendo a pensar en términos de lo que es perenne y de lo que es más probable que esté sujeto a cambios. El tema de cambiar cosas es lo que querrás tender a poner en tus cajas de objetos más grandes para que puedas mantener interfaces consistentes incluso cuando los jugadores cambien o se agreguen nuevas opciones. O te encuentras con ganas de intercambiar objetos de audio o componentes al por mayor.
En este caso, su controlador necesita identificar que existe la necesidad de reproducir un archivo de audio y luego tener una forma consistente / perenne de reproducirlo. El material del reproductor de audio, por otro lado, podría cambiar fácilmente a medida que la tecnología y las plataformas se alteran o se agregan nuevas opciones. Todos esos detalles deben ubicarse debajo de una interfaz de un objeto compuesto más grande, IMO, y no debería tener que volver a escribir su controlador cuando cambian los detalles de cómo se reproduce el audio. Luego, cuando pasa una instancia de objeto con los detalles como la ubicación del archivo en el objeto más grande, todo ese intercambio se realiza en el interior de un contexto apropiado donde es menos probable que alguien haga algo tonto con él.
Entonces, en este caso, no creo que sea esa instancia de objeto que se arroje lo que podría estar molestando. Es que el Capitán Picard está corriendo hacia la sala de máquinas para encender el núcleo de urdimbre, corriendo de regreso al puente para trazar las coordenadas, y luego presionando el botón "golpearlo" después de encender los escudos en lugar de simplemente decir "Tomar nosotros al planeta X en Warp 9. Hazlo así ". y dejando que su tripulación solucione los detalles. Porque cuando lo maneja de esa manera, puede capitanear cualquier barco de la flota sin conocer el diseño de cada barco y cómo funciona todo. Y esa es, en última instancia, la mayor victoria de diseño de OOP para disparar, IMO.
fuente
Es un diseño bastante común que termina sucediendo, aunque puede tener problemas de latencia si su aplicación es sensible a ese tipo de cosas.
fuente
Este problema se puede resolver con variables de ámbito dinámico, si su idioma las tiene, o almacenamiento local de subprocesos. Estos mecanismos nos permiten asociar algunas variables personalizadas con una cadena de activación o hilo de control, para que no tengamos que pasar estos valores al código que no tiene nada que ver con ellos solo para que puedan comunicarse a otro código que los necesita
fuente
Como las otras respuestas han señalado, este no es un diseño inherentemente pobre. Puede crear un acoplamiento estrecho entre las clases anidadas y las que las anidan, pero aflojar el acoplamiento puede no ser una opción válida si anidar las referencias proporciona un valor al diseño.
Una posible solución es "aplanar" las referencias anidadas en la clase de controlador.
En lugar de pasar un parámetro varias veces a través de objetos anidados, puede mantener en la clase de controlador referencias a todos los objetos anidados.
Cómo se implementa exactamente esto (o si es una solución válida) depende del diseño actual del sistema, como por ejemplo:
Este es un problema que encontré en un patrón de diseño MVC para un cliente GXT. Nuestros componentes GUI contenían componentes GUI anidados para varias capas. Cuando se actualizaron los datos del modelo, terminamos pasándolos a través de varias capas hasta que llegaron a los componentes apropiados. Creó un acoplamiento no deseado entre los componentes de la GUI porque si queríamos que una nueva clase de componente de la GUI aceptara datos del modelo, teníamos que crear métodos para actualizar los datos del modelo en todos los componentes de la GUI que contenían la nueva clase.
Para solucionarlo, mantuvimos en la clase Vista un mapa de referencias a todos los componentes GUI anidados para que cada vez que se actualizaran los datos del modelo, la Vista pudiera enviar los datos del modelo actualizados directamente a los componentes GUI que lo necesitaban, fin de la historia . Esto funcionó bien porque solo había instancias únicas de cada componente de la GUI. Podría ver que no funciona tan bien si hubiera varias instancias de algunos componentes de la GUI, lo que dificulta identificar qué copia necesita actualizarse.
fuente
Lo que está describiendo es un patrón de diseño llamado Cadena de responsabilidad . Apple usa este patrón para su sistema de manejo de eventos, por lo que vale.
fuente