Estoy creando una aplicación WPF usando el patrón MVVM. En este momento, mis modelos de vista llama a la capa de servicio para recuperar modelos (por qué no es relevante para el modelo de vista) y convertirlos en modelos de vista. Estoy usando la inyección del constructor para pasar el servicio requerido al modelo de vista.
Es fácilmente comprobable y funciona bien para modelos de vista con pocas dependencias, pero tan pronto como intento crear modelos de vista para modelos complejos, tengo un constructor con MUCHOS servicios inyectados (uno para recuperar cada dependencia y una lista de todos los valores disponibles para enlazar a un itemSource por ejemplo). Me pregunto cómo manejar múltiples servicios como ese y aún tener un modelo de vista que pueda probar fácilmente en la unidad.
Estoy pensando en algunas soluciones:
Crear un singleton de servicios (IServices) que contenga todos los servicios disponibles como interfaces. Ejemplo: Services.Current.XXXService.Retrieve (), Services.Current.YYYService.Retrieve (). De esa manera, no tengo un gran constructor con una tonelada de parámetros de servicios en ellos.
Creando una fachada para los servicios utilizados por viewModel y pasando este objeto en el ctor de mi viewmodel. Pero entonces, tendré que crear una fachada para cada uno de mis modelos de vista complejos, y podría ser un poco demasiado ...
¿Cuál crees que es la forma "correcta" de implementar este tipo de arquitectura?
fuente
new
para crear otros modelos de vista, pero piense en algo tan simple como una aplicación MDI en la que al hacer clic en un botón o menú de "nuevo documento" se agregará una nueva pestaña o se abrirá una nueva ventana. El shell / conductor debe poder crear nuevas instancias de algo , incluso si está oculto detrás de una o unas pocas capas de indirección.Respuestas:
De hecho, ambas soluciones son malas.
Este es esencialmente el patrón de localización de servicios , que es un antipatrón. Si hace esto, ya no podrá comprender de qué depende realmente el modelo de vista sin mirar su implementación privada, lo que hará que sea muy difícil de probar o refactorizar.
Esto no es tanto un antipatrón, sino un olor a código. Esencialmente, está creando un objeto de parámetro , pero el objetivo del patrón de refactorización de PO es tratar con conjuntos de parámetros que se usan con frecuencia y en muchos lugares diferentes , mientras que este parámetro solo se usaría una vez. Como mencionas, crearía una gran cantidad de código sin beneficio real, y no funcionaría bien con muchos contenedores IoC.
De hecho, ambas estrategias anteriores están pasando por alto el problema general, que es que el acoplamiento es demasiado alto entre los modelos de vista y los servicios . El simple hecho de ocultar estas dependencias en un localizador de servicios u objeto de parámetro no cambia en realidad cuántos otros objetos depende el modelo de vista.
Piense en cómo probaría uno de estos modelos de vista. ¿Qué tan grande será tu código de configuración? ¿Cuántas cosas hay que inicializar para que funcione?
Muchas personas que comienzan con MVVM intentan crear modelos de vista para una pantalla completa , lo que es fundamentalmente el enfoque equivocado. MVVM tiene que ver con la composición , y una pantalla con muchas funciones debe estar compuesta de varios modelos de vista diferentes, cada uno de los cuales depende de uno o unos pocos modelos / servicios internos. Si necesitan comunicarse entre sí, debe hacerlo a través de pub / sub (intermediario de mensajes, bus de eventos, etc.)
Lo que realmente necesita hacer es refactorizar sus modelos de vista para que tengan menos dependencias . Luego, si necesita tener una "pantalla" agregada, cree otro modelo de vista para agregar los modelos de vista más pequeños. Este modelo de vista agregada no tiene que hacer mucho por sí mismo, por lo que también es bastante fácil de entender y probar.
Si ha hecho esto correctamente, debería ser obvio simplemente mirando el código, porque tendrá modelos de vista cortos, concisos, específicos y comprobables.
fuente
Podría escribir un libro sobre esto ... de hecho lo soy;)
En primer lugar, no existe una forma universalmente "correcta" de hacer las cosas. Tienes que tener en cuenta otros factores.
Es posible que sus servicios sean demasiado finos. Ajustar los servicios con fachadas que proporcionan la interfaz que usaría un modelo de vista específico o incluso un grupo de modelos de vista relacionados podría ser una mejor solución.
Aún más simple sería envolver los servicios en una única Fachada que usan todos los modelos de vista. Por supuesto, puede ser una interfaz muy grande con muchas funciones innecesarias para el escenario promedio. Pero yo diría que no es diferente a un enrutador de mensajes que maneja cada mensaje en su sistema.
De hecho, lo que he visto evolucionar en muchas arquitecturas es un bus de mensajes construido alrededor de algo como el patrón de Agregador de eventos. La prueba es fácil allí porque su clase de prueba solo registra un oyente con el EA y dispara el evento apropiado en respuesta. Pero ese es un escenario avanzado que lleva tiempo crecer. Yo digo comenzar con la fachada unificadora y seguir desde allí.
fuente
¿Por qué no combinar ambos?
Cree una fachada y ponga todos los servicios que usan sus modelos de vista. Entonces puede tener una fachada única para todos sus modelos de vista sin la mala palabra S.
O puede usar la inyección de propiedades en lugar de la inyección del constructor. Pero entonces, debe asegurarse de que se inyecten correctamente.
fuente