vista patrones mvc modelo entre diferencia controlador arquitectura c# wpf design-patterns mvvm

c# - patrones - mvc vs mvvm



MVVM: ViewModel y conexión de lógica de negocios (4)

Después de hacer algunos proyectos usando el patrón MVVM, todavía estoy luchando con el rol de ViewModel:

Lo que hice en el pasado: usar el modelo solo como un contenedor de datos. Poniendo la lógica para manipular los datos en ViewModel. (Esa es la lógica de negocios ¿no?) Con: La lógica no es reutilizable.

Lo que estoy intentando ahora: mantener el modelo de vista lo más delgado posible. Mover toda la lógica a la capa de modelo. Solo manteniendo la lógica de presentación en ViewModel. Con: hace que la notificación de UI sea realmente dolorosa si se modifican los datos dentro de la capa de modelo.

Así que les daré un ejemplo para hacerlo más claro:

Escenario: herramienta para cambiar el nombre de los archivos. Clases: Archivo: Representando cada archivo; Regla: contiene la lógica de cómo cambiar el nombre de un archivo;

Si estoy siguiendo el enfoque 1: Creando un ViewModel para File, Rule y la Vista -> RenamerViewModel. Poner toda la lógica en el RenamerViewModel: que contiene una lista de FileViewModel y RuleViewModel y la lógica en curso. Fácil y rápido, pero no reutilizable.

Si estoy siguiendo el enfoque 2: Creando una nueva Clase de Modelo -> Renamer, que contiene una Lista de Archivo, Regla y la Lógica en curso para interactuar sobre cada Archivo y aplicar cada Regla. Crear un modelo de vista para archivo, regla y renamer. Ahora, RenaMelViewModel solo contiene una instancia de Renamer Model, más dos ObservableCollections para ajustar la Lista de archivos y reglas del Renamer. Pero toda la lógica está en el modelo Renamer. Entonces, si el Modelo Renamer se activa para manipular algunos datos por llamadas a métodos, ViewModel no tiene ninguna pista sobre qué datos se manipula. Porque el modelo no contiene ninguna notificación de cambio de propiedad y lo evitaré. Por lo tanto, la Lógica empresarial y de presentación está separada, pero esto dificulta notificar a la IU.


Ambos enfoques son válidos, pero hay un tercer enfoque: implementar un servicio entre el modelo y las capas de VM. Si desea mantener en ridículo a sus modelos, un servicio puede proporcionarle a un intermediario independiente de la interfaz de usuario que pueda hacer cumplir sus reglas comerciales de una manera reutilizable.

Debido a que el modelo no contiene ninguna Notificación de cambio de propiedad, lo evitaré

¿Por qué estás evitando esto? No me malinterpreten, tiendo a mantener mis modelos tan tontos como sea posible, pero implementar notificaciones de cambios en su modelo a veces puede ser útil, y usted toma una dependencia solo en System.ComponentModel cuando lo hace. Es completamente independiente de la interfaz de usuario.


Poner la lógica de negocios dentro del modelo de vista es una muy mala manera de hacer las cosas, así que voy a decir rápidamente que nunca lo haga y pasar a discutir la segunda opción.

Poner la lógica dentro del modelo es mucho más razonable y es un buen enfoque de partida. ¿Cuáles son los inconvenientes? Tu pregunta dice

Entonces, si el Modelo Renamer se activa para manipular algunos datos por llamadas a métodos, ViewModel no tiene ninguna pista sobre qué datos se manipula. Porque el modelo no contiene ninguna notificación de cambio de propiedad y lo evitaré.

Bueno, hacer que su modelo implemente INotifyPropertyChanged le permitirá pasar a cosas mejores. Sin embargo, es verdad que a veces no es posible hacer eso; por ejemplo, el modelo puede ser una clase parcial donde las propiedades son autogeneradas por una herramienta y no generan notificaciones de cambio. Eso es desafortunado, pero no el fin del mundo.

Si quieres comprar algo, entonces alguien tiene que pagarlo; si no es el modelo el que otorga tales notificaciones, entonces solo le quedan dos opciones:

  1. El modelo de vista sabe qué operaciones en el modelo (posiblemente) causan cambios y actualiza su estado después de cada operación.
  2. Alguien más sabe qué operaciones provocan cambios y notifican al modelo de vista que actualice su estado después de que el modelo esté envolviendo los cambios.

La primera opción es nuevamente una mala idea, porque en realidad está volviendo a poner "lógica comercial" dentro del modelo de vista. No es tan malo como poner toda la lógica de negocios en el modelo de vista, pero aún así.

La segunda opción es más prometedora (y lamentablemente más trabajo de implementar):

  • Ponga parte de su lógica comercial en una clase separada (un "servicio"). El servicio implementará todas las operaciones comerciales que desee realizar trabajando con instancias modelo según corresponda.
  • Esto significa que el servicio sabe cuándo pueden cambiar las propiedades del modelo (esto está bien: modelo + servicio == lógica de negocios).
  • El servicio proporcionará notificaciones sobre modelos modificados a todas las partes interesadas; sus viewmodels tomarán una dependencia del servicio y recibirán estas notificaciones (para que sepan cuándo se ha actualizado "su" modelo).
  • Como el servicio también implementa las operaciones comerciales, esto sigue siendo muy natural (por ejemplo, cuando se invoca un comando en el modelo de vista, la reacción llama a un método apropiado en el servicio; recuerde, el propio modelo de vista no conoce la lógica comercial).

Para obtener más información sobre dicha implementación, vea también mis respuestas here y here .


También puede implementar IDataErrorInfo en ambos: Model y ViewModel, pero al realizar la validación solo en Model, esto facilitará la implementación de las reglas de negocio solo en Model ...

Ex:

ViewModel:

... private Person person; ... string IDataErrorInfo.this[string propertyName] { get { string error = (person as IDataErrorInfo)[propertyName]; return error; } }

Modelo:

public class Person:INotifyPropertyChanged,IDataErrorInfo { ... string IDataErrorInfo.this[string propertyName] { get { return this.GetValidationError(propertyName); } } ... string GetValidationError(string propertyName) { if(propertyName == "PersonName") //do the validation here returning the string error } }

Además, eche un vistazo al Patrón MVCVM, realmente lo estoy usando y es bastante bueno abstraer la lógica de negocios a una clase de controlador en lugar del modelo o modelo de vista


Yo hago lo siguiente

  1. Ver solo con la lógica de vista XAML

  2. ViewModel que maneja los manejadores de clics y crea nuevos modelos de vista. Maneja eventos enrutados, etc.

  3. Modelo que es mi contenedor de datos y lógica de negocios con respecto a la validación de los datos del modelo.

  4. Servicios que pueblan el modelo con datos. Por ejemplo, llame a un servidor web, cárguelo del disco, guárdelo en el disco, etc. Dependiendo del ejemplo, a menudo mi modelo y servicio implementarán IPropertyChanged. O pueden tener controladores de eventos en su lugar.

Cualquier aplicación compleja imo necesita otra capa. Lo llamo modelo + servicio, vista, modelo de vista. El servicio abstrae su lógica comercial y toma una instancia modelo como dependencia o crea un modelo.