tutorial español wpf design-patterns mvvm

wpf - español - ¿MVVM viola DRY?



mvvm wpf (8)

Creo que sí, vanilla MVVM no viola DRY. Pero comencé la biblioteca PDX, que creo que puede resolver este problema en muchos casos. Escribí esta publicación que creo que aborda esta cuestión.

Básicamente, mi objetivo (uno de ellos) es tener Viewmodels que no se preocupen por la Notificación UI. El proyecto PDX aún está en su infancia, pero si está leyendo esta pregunta, puede que le resulte útil y agradecería cualquier comentario que pueda tener.

Parece que ViewModels que hago se parecen sospechosamente a otras clases y parecen requerir mucha repetición de código, por ejemplo, en un proyecto actual que tengo:

  • SmartForm : modelo que representa un formulario de datos para completar, tiene propiedades:
    • Código de identificación
    • Título
    • Descripción
    • colección de SmartFormFields
    • etc.
  • SmartFormControlView View
  • SmartFormControlViewModel ViewModel
    • Código de identificación
    • Título
    • Descripción
    • colección de SmartFormFields
    • etc.

Así que mi ViewModel es básicamente el mismo que mi modelo , solo con todas las características de OnPropertyChanged para enlazar con la Vista.

Parece que a medida que refacto y extiendo esto, cada pequeño cambio que haga en mi modelo, tengo que hacer un cambio de espejo en ViewModel .

Esto parece violar una regla básica de patrones No repetir .

¿Estoy implementando el patrón MVVM incorrectamente o es solo una característica inherente de MVVM que siempre hay una repetición 1-a-1 entre Model y ViewModel?


Eric Evans, en su libro "Domain Driven Design" menciona que la refacturación de modelos no debe ser demasiado difícil, y que un cambio de concepto no debe abarcar demasiados módulos, de lo contrario, refactorizar el modelo se vuelve prohibitivo, entonces, si me preguntas, teniendo el tipo de modelo de ''copiado'' en ViewModel ciertamente dificulta la refactorización del modelo.

Eric menciona que se debe dar más peso a la cohesión y el aislamiento del Modelo, que a un orden en la división de capas basado en preocupaciones técnicas (acceso a la base de datos, POCOS, presentación). Que la preocupación más importante es que el Modelo de Dominio es una buena representación del Dominio Comercial, por lo tanto, es el más importante para el Modelo de Dominio estar en una sola capa aislada, y no abarcar varios módulos, para que sea fácil actualizado (refactorizado).

Teniendo en cuenta lo que acabo de decir, usaría el mismo objeto Model en ViewModel, y si quiero reducir el nivel de "acceso" a un objeto Model, entonces "pasaré" una referencia a una interfaz implementada por el Objeto modelo Por ejemplo:

// The primary Model classes public partial class OrderItem { public int Id { get; } public int Quantity { get; set; } public Product Item { get; set; } public int Total { get; set; } public void ApplyDiscount(Coupon coupon) { // implementation here } } public partial class Product { public int Id { get; } public string Name { get; set; } public string Description { get; set; } public decimal Price { get; set; } } // The shared views of those model classes public partial class OrderItem : IOrderItemDTO { public IProductDTO Item { get { return this.product; } } } public partial class Product : IProductDTO { } // Separate interfaces... // You enforce the rules about how the model can be // used in the View-ViewModel, without having to rewrite // all the setters and getters. public interface IOrderItemDTO { int Id { get; } int Quantity { get; set; } IProductDTO Item { get; } int Total { get; } } public interface IProductDTO { string Name { get; } string Description { get; } decimal Price { get; } } // The viewmodel... public class OrderItemViewModel { IOrderItemDTO Model { get; set; } }


Es una observación interesante ... de hecho, a menudo es necesario modificar ViewModel para reflejar los cambios en el Modelo.

Sería bueno si pudiera ser automático ... en realidad creo que podría ser posible, implementando ICustomTypeDescriptor en ViewModel: GetProperties devolvería todas las propiedades del modelo a través de la reflexión. Sin embargo, no estoy seguro de que tenga sentido, porque el modelo puede no consistir en propiedades en absoluto: podrían ser métodos, campos o cualquier otra cosa, y no todo en el modelo sería útil en ViewModel.


Otros han proporcionado buenos comentarios sobre los roles de los componentes de los patrones MVC / MVVM. Me gustaría ofrecer una observación fundamental que explique la repetitividad, independientemente del patrón que seleccione.

En general, habrá algún tipo de repetición entre su capa de datos, capa de negocio y capa de UI. Después de todo, en general, debe mostrar cada propiedad al usuario final (UI), modelar su comportamiento (capa Business) y conservar el valor (capa de datos).

Como han señalado otros, la propiedad podría tratarse de forma un poco diferente en cada capa, lo que explica la necesidad fundamental de cierta duplicación.

Cuando trabajo en sistemas lo suficientemente grandes (o en pequeños proyectos con el equipo adecuado), tiendo a modelar este tipo de información en UML, y uso la generación de código (a menudo junto con clases parciales) para manejar los aspectos repetitivos. Como un ejemplo simple, la propiedad Apellido podría tener un requisito (en mi modelo UML) que limite los datos a 50 caracteres. Puedo generar código para imponer ese límite en mi capa UI (por ejemplo, limitando la entrada físicamente), generar código en mi capa empresarial para volver a verificar esa limitación ("nunca confío en la UI") quizás lanzando una excepción si los datos son demasiado largos, y generar mi capa de persistencia (por ejemplo, columna NVARCHAR (50), archivo de mapeo ORM apropiado, etc.).

Actualización de 2012

Las anotaciones de datos de Microsoft y su soporte en la capa de la interfaz de usuario (por ejemplo, ASP.Net MVC ) y en la capa de datos ( Entity Framework ) contribuyen en gran medida a implementar muchas de las inquietudes que antes generaba el código.


Personalmente, no creo que viole DRY ya que el modelo y el modelo de visualización (prefiero el término presentador) no apuntan a la misma información. Por ejemplo, su VM y M tienen una propiedad de título, pero la propiedad de título de su máquina virtual también podría incluir validación, mientras que la propiedad de título de su modelo podría asumir validez.

Si bien es cierto que la máquina virtual puede contener todas las propiedades del modelo, también existe la posibilidad de tener validaciones (por ejemplo, el título debe estar en blanco), dependencias de datos, propiedades vinculables de la interfaz de usuario (iconos, colores, pinceles, etc.) que no son parte de la vista.

Esencialmente, todos los patrones de UI tienen una "duplicación" similar en la forma en que usted lo indica: es decir, modificaciones en cascada. Intente cambiar un modelo en MVC sin cambiar el controlador.

Dicho esto, MVVM (o cualquier patrón de interfaz de usuario diseñado para separar la interfaz de usuario, la lógica y el estado) puede ser demasiado tedioso para casos simples como su ejemplo. Cuando la lógica se convierte en poco más que paso de estado, el valor que separa el controlador / presentador / modelo de vista disminuye.

En su caso específico, si realmente no hay ninguna lógica, validación o propiedades específicas de UI, su VM no está emergiendo, y su modelo no tiene que ser persistente, serializado o hecho compatible con una estructura existente ( o agregar la lógica para hacerlo en su VM es trivial), consideraría mucho combinar la M y la VM para evitar la creación de propiedades cuyo único propósito sea obtener / establecer las propiedades del modelo subyacente.


Solo conozco MVC y en MVC una Model-Class que contiene GUI es un error. SmartForm parece ser una forma, lo que significa que no es un modelo. No sé lo que intentas programar pero te doy un ejemplo para un Modelo:

Toma un calendario. Puede preguntar a la clase qué fecha es hoy, qué mes, cuántos días tiene cada mes, ... Sin embargo, no tiene representación gráfica. La Vista (CalenderViewMonth o Whater que desee) imprime un solo mes en la pantalla. Conoce un calendario y le pregunta qué escribir en las diferentes celdas.

Esencialmente, es posible que tenga algo mal en su modelado / comprensión de MVVM (que es una variante .NET moderna de MVC).

Editar:

Acabo de buscar MVVM en Wikipedia. El modelo es como el Modelo en MVC. Ver como la Vista en MVC también - solo representación gráfica. ViewModel es el pegamento entre una vista genérica y un modelo especializado. Algún tipo de adaptador. No debe haber violación de DRY.


Una cosa que parece haberse omitido aquí y que su ejemplo simplista no expone es el hecho de que sus Vistas a menudo agregarán datos contenidos en múltiples tipos de Modelos de Dominio. En este caso, sus modelos de vista contendrán referencias a una serie de modelos de dominio (de diferentes tipos), agregando así un grupo de datos relacionados que una determinada vista puede querer exponer.


Una solución simple es tener una clase base abstracta de ViewModel (VM) que exponga el modelo. Puede elegir esta VM en escenarios donde tenga sentido.

es decir

public abstract class ViewModelBase<T> { public T Model { get; set; } }

Si su modelo tiene INotifyPropertyChanged implementado, su vista obtendrá el evento. Lo que esto hace es darle acceso a su Vista a cada propiedad de su Modelo que no es lo que quiere algunas veces.

también puede utilizar inicializadores de propiedades como este (que personalmente he almacenado en fragmentos de código):

public abstract class SampleViewModel { public int MyProperty { get { return Model.MyProperty; } set { Model.MyProperty = value; OnPropertyChanged("MyProperty"); } } }

En la mayoría de las circunstancias, usted verá que será el que realice los cambios en su máquina virtual y, cuando lo haga, cualquier control que esté vinculado a esa propiedad se le informará que sucedió algo.

Espero que ayude.