Pido disculpas por la larga pregunta, se lee un poco como una queja, ¡pero prometo que no lo es! He resumido mis preguntas a continuación
En el mundo MVC, las cosas son sencillas. El Modelo tiene estado, la Vista muestra el Modelo y el Controlador hace cosas con / con el Modelo (básicamente), un controlador no tiene estado. Para hacer cosas, el controlador tiene algunas dependencias en los servicios web, el repositorio, el lote. Cuando crea una instancia de un controlador, le importa suministrar esas dependencias, nada más. Cuando ejecuta una acción (método en el Controlador), usa esas dependencias para recuperar o actualizar el Modelo o llamar a algún otro servicio de dominio. Si hay algún contexto, digamos que si algún usuario quiere ver los detalles de un elemento en particular, pasa el Id de ese elemento como parámetro a la Acción. En ninguna parte del controlador hay alguna referencia a ningún estado. Hasta aquí todo bien.
Ingrese MVVM. Me encanta WPF, me encanta el enlace de datos. Me encantan los marcos que hacen que el enlace de datos a ViewModels sea aún más fácil (usando Caliburn Micro atm). Sin embargo, siento que las cosas son menos sencillas en este mundo. Vamos a hacer de nuevo el ejercicio: el modelo ha estado, la vista muestra el ViewModel, y el modelo de vista hace cosas para / con el Modelo (básicamente), un modelo de vista hace que el estado! (para aclarar, tal vez delega todas las propiedades a uno o más modelos, pero eso significa que deben tener una referencia al modelo de un modo u otro, que es el estado en sí mismo) Para hacerEl ViewModel tiene algunas dependencias en los servicios web, el repositorio, el lote. Cuando crea una instancia de un ViewModel, le importa suministrar esas dependencias, pero también el estado. Y esto, damas y caballeros, me molesta sin fin.
Siempre que necesite crear una instancia ProductDetailsViewModel
de ProductSearchViewModel
(de la cual llamó a la ProductSearchWebService
que a su vez regresó IEnumerable<ProductDTO>
, ¿todos siguen conmigo?), Puede hacer una de estas cosas:
- llame
new ProductDetailsViewModel(productDTO, _shoppingCartWebService /* dependcy */);
, esto es malo, imagine 3 dependencias más, esto significa que tambiénProductSearchViewModel
necesita asumir esas dependencias. También cambiar el constructor es doloroso. - llame
_myInjectedProductDetailsViewModelFactory.Create().Initialize(productDTO);
, la fábrica es solo un Func, son generados fácilmente por la mayoría de los marcos de IoC. Creo que esto es malo porque los métodos Init son una abstracción permeable. Tampoco puede usar la palabra clave de solo lectura para los campos que se establecen en el método Init. Estoy seguro de que hay algunas razones más. - llame
_myInjectedProductDetailsViewModelAbstractFactory.Create(productDTO);
Entonces ... este es el patrón (fábrica abstracta) que generalmente se recomienda para este tipo de problema. Pensé que era genial, ya que satisface mis ansias de escritura estática, hasta que realmente comencé a usarlo. La cantidad de código repetitivo es demasiado (creo, aparte de los ridículos nombres de variables que uso). Por cada ViewModel que necesite parámetros de tiempo de ejecución, obtendrá dos archivos adicionales (interfaz de fábrica e implementación), y deberá escribir las dependencias que no sean de tiempo de ejecución como 4 veces adicionales. Y cada vez que cambian las dependencias, también puedes cambiarlo en la fábrica. Parece que ya ni siquiera uso un contenedor DI. (Creo que Castle Windsor tiene algún tipo de solución para esto [con sus propios inconvenientes, corrígeme si me equivoco]). - hacer algo con tipos anónimos o diccionario. Me gusta mi escritura estática.
Así que sí. La combinación de estado y comportamiento de esta manera crea un problema que no existe en absoluto en MVC. Y siento que actualmente no hay una solución realmente adecuada para este problema. Ahora me gustaría observar algunas cosas:
- La gente realmente usa MVVM. Entonces, o no les importa todo lo anterior, o tienen alguna otra solución brillante.
- No he encontrado un ejemplo en profundidad de MVVM con WPF. Por ejemplo, el proyecto de muestra NDDD me ayudó inmensamente a comprender algunos conceptos DDD. Realmente me gustaría que alguien pudiera señalarme en la dirección de algo similar para MVVM / WPF.
- Tal vez estoy haciendo MVVM todo mal y debería poner mi diseño al revés. Quizás no debería tener este problema en absoluto. Bueno, sé que otras personas han hecho la misma pregunta, así que creo que no soy el único.
Para resumir
- ¿Estoy en lo cierto al concluir que tener el ViewModel como un punto de integración tanto para el estado como para el comportamiento es la razón de algunas dificultades con el patrón MVVM en su conjunto?
- ¿Usar el patrón de fábrica abstracto es la única / mejor manera de crear una instancia de ViewModel de forma estática?
- ¿Hay algo como una implementación de referencia en profundidad disponible?
- ¿Tener muchos ViewModels con estado / comportamiento es un olor de diseño?
Respuestas:
El problema de las dependencias al iniciar un nuevo modelo de vista se puede manejar con IOC.
Al configurar el contenedor ...
Cuando necesite su modelo de vista:
Cuando se utiliza un marco como Caliburn Micro, a menudo ya existe alguna forma de contenedor de COI.
fuente
Trabajo diariamente con ASP.NET MVC y he trabajado en un WPF durante más de un año y así es como lo veo:
MVC
Se supone que el controlador orquesta acciones (busque esto, agregue eso).
La vista es responsable de mostrar el modelo.
El modelo generalmente abarca datos (ej. UserId, FirstName), así como el estado (ej. Titles) y generalmente es específico de la vista.
MVVM
El modelo generalmente solo contiene datos (ej. UserId, FirstName) y generalmente se pasa
El modelo de vista abarca el comportamiento de la vista (métodos), sus datos (modelo) e interacciones (comandos), similar al patrón MVP activo en el que el presentador conoce el modelo. El modelo de vista es específico de vista (1 vista = 1 modelo de vista).
La vista es responsable de mostrar los datos y el enlace de datos al modelo de vista. Cuando se crea una vista, generalmente se crea su modelo de vista asociado.
Lo que debe recordar es que el patrón de presentación MVVM es específico de WPF / Silverlight debido a su naturaleza de enlace de datos.
La vista generalmente sabe con qué modelo de vista está asociada (o una abstracción de uno).
Le aconsejaría que trate el modelo de vista como un singleton, a pesar de que se instancia por vista. En otras palabras, debe ser capaz de crearlo a través de DI a través de un contenedor IOC y llamar a los métodos apropiados para decirlo; cargar su modelo basado en parámetros. Algo como esto:
Como ejemplo en este caso, no crearía un modelo de vista específico para el usuario que se está actualizando; en cambio, el modelo contendría datos específicos del usuario que se cargan a través de alguna llamada en el modelo de vista.
fuente
Respuesta corta a sus preguntas:
La versión larga:
Estamos enfrentando el mismo problema y encontramos algunas cosas que pueden ayudarlo. Aunque no conozco la solución "mágica", esas cosas alivian un poco el dolor.
Implemente modelos vinculables de DTO para el seguimiento y la validación de cambios. Esos "Data" -ViewModels no deben depender de los servicios y no provienen del contenedor. Pueden ser simplemente "nuevos" editados, pasados e incluso pueden derivarse del DTO. La conclusión es implementar un modelo específico para su aplicación (como MVC).
Desacoplar sus ViewModels. Caliburn facilita el acoplamiento de los ViewModels juntos. Incluso lo sugiere a través de su modelo de pantalla / conductor. Pero este acoplamiento dificulta la prueba de unidad de ViewModels, crea muchas dependencias y lo más importante: impone la carga de administrar el ciclo de vida de ViewModel en sus ViewModels. Una forma de desacoplarlos es usar algo como un servicio de navegación o un controlador ViewModel. P.ej
interfaz pública IShowViewModels {void Show (objeto inlineArgumentsAsAnonymousType, string regionId); }
Aún mejor es hacerlo mediante alguna forma de mensajería. Pero lo importante es no manejar el ciclo de vida de ViewModel desde otros ViewModels. En MVC los controladores no dependen el uno del otro, y en MVVM ViewModels no deberían depender el uno del otro. Intégrelos a través de otras formas.
INeedData<T1,T2,...>
como forzar parámetros de creación de tipo seguro, no vale la pena. Tampoco vale la pena crear fábricas para cada tipo de ViewModel. La mayoría de los contenedores IoC brindan soluciones a esto. Obtendrá errores en tiempo de ejecución, pero la desconexión y la capacidad de prueba de la unidad lo valen. Todavía realiza algún tipo de prueba de integración y esos errores se detectan fácilmente.fuente
La forma en que generalmente hago esto (usando PRISM) es que cada ensamblaje contiene un módulo de inicialización de contenedor, donde todas las interfaces e instancias se registran en el inicio.
Y dadas sus clases de ejemplo, se implementaría de esta manera, con el contenedor pasando completamente. De esta manera, cualquier dependencia nueva se puede agregar fácilmente ya que ya tiene acceso al contenedor.
Es bastante común tener una clase ViewModelBase, de la que derivan todos sus modelos de vista, que contiene una referencia al contenedor. Siempre que tenga el hábito de resolver todos los modelos de vista en lugar de
new()'ing
ellos, debería hacer que toda resolución de dependencia sea mucho más simple.fuente
A veces es bueno ir a la definición más simple en lugar de un ejemplo completo: http://en.wikipedia.org/wiki/Model_View_ViewModel tal vez leer el ejemplo ZK Java es más esclarecedor que el C # one.
Otras veces escucha tu instinto ...
¿Sus modelos son asignaciones de objetos por tabla? Quizás un ORM ayudaría a mapear objetos de dominio mientras maneja el negocio o actualiza varias tablas.
fuente