2009-06-10 32 views
14

Me pregunto cómo acercarme a la herencia con Ver modelos en el patrón MVVM. En mi aplicación tengo un modelo de datos similar al siguiente:Herencia de MVVM con modelos de vista

class CustomObject 
{ 
    public string Title { get; set; } 
} 

class CustomItem : CustomObject 
{ 
    public string Description { get; set; } 
} 

class CustomProduct : CustomItem 
{ 
    public double Price { get; set; } 
} 

en mi aplicación tengo una clase ViewModelBase y luego iba a tener las siguientes Visualización de modelos:

  • CustomObjectViewModel
  • CustomItemViewModel
  • CustomProductViewModel

Un implementatio áspera n de la CustomObjectViewModel se parecería a lo siguiente:

class CustomObjectViewModel : ViewModelBase 
{ 
    private readonly CustomObject _customObject; 

    public CustomObjectViewModel(CustomObject customObject) 
    { 
     _customObject = customObject; 
    } 

    public string Title 
    { 
     // implementation excluded for brevity 
    } 
} 

Parece lógico que mis Visualización de modelos se extenderían a sí mismos de la misma manera como lo hizo mi modelo (CustomItemViewModel extiende CustomObjectViewModel y así sucesivamente). Sin embargo, he notado que a medida que baje el árbol de herencia agregaré referencias adicionales al mismo objeto. Esto me parece excesivo y me preguntaba cómo abordar este problema y si era posible hacerlo mucho más limpio.

Respuesta

17

Generalmente, le recomendaría no tener herencia entre las diferentes clases de ViewModel, sino tenerlas heredadas directamente de una clase base abstracta común.
Esto es para evitar introducir una complejidad innecesaria al contaminando las interfaces de las clases ViewModel con miembros que provienen de un nivel más alto en la jerarquía, pero no son completamente cohesivos al propósito principal de la clase.
El acoplamiento que viene con la herencia también probablemente dificultará el cambio de una clase ViewModel sin afectar a ninguna de sus clases derivadas.

Si sus clases ViewModel siempre harán referencia a un único objeto de modelo, se puede utilizar genéricos para encapsular esta regla en la clase base:

public abstract class ViewModelBase<TModel> 
{ 
    private readonly TModel _dataObject; 

    public CustomObjectViewModel(TModel dataObject) 
    { 
     _dataObject = dataObject; 
    } 

    protected TModel DataObject { get; } 
} 

public class CustomObjectViewModel : ViewModelBase<CustomObject> 
{ 
    public string Title 
    { 
     // implementation excluded for brevity 
    } 
} 

public class CustomItemViewModel : ViewModelBase<CustomItem> 
{ 
    public string Title 
    { 
     // implementation excluded for brevity 
    } 

    public string Description 
    { 
     // implementation excluded for brevity 
    } 
} 
+0

He utilizado el patrón de genéricos como se ilustra aquí, y tengo que decir que realmente no he tenido cualquier problema con tener una jerarquía de herencia para ViewModels. –

+3

Sin duda funcionará sin problemas en muchos casos. Sin embargo, en mi opinión, veo que las clases de ViewModel están estrechamente relacionadas con Views. La herencia entre las clases de ViewModel tendría sentido si sus Vistas asociadas comparten alguna UI común. –

+0

Estoy enfrentando el mismo escenario que Richard experimentó, excepto que mi pregunta es sobre la vista. Tengo una clase abstracta con 10 campos, y sus diversas subclases le agregan muchos más campos (cada uno lo agrega), ¿cómo debo crear las distintas vistas? No quiero redeclarar los campos base en cada uno de ellos (todavía no estoy tan seguro del ViewModel en sí, agradecería cualquier ayuda sobre él). – Shimmy

3

estaría interesado en ver si hay una mejor respuesta para esto, pero cuando he tenido el mismo problema que siempre he cerrado una conversión explícita del objeto como una propiedad privada, así:

class CustomObjectViewModel : ViewModelBase 
{ 
    protected readonly CustomObject CustomObject; 

    public CustomObjectViewModel(CustomObject customObject) 
    { 
     CustomObject = customObject; 
    } 

    public string Title 
    { 
     // implementation excluded for brevity 
    } 
} 

class CustomItemViewModel : CustomObjectViewModel 
{ 
    protected CustomItem CustomItem { get { return (CustomItem)CustomObject; } } 

    public CustomItemViewModel(CustomItem customItem) 
     :base(customItem) 
    { 
    } 
} 

Funciona, y es lo mejor que he creado, pero nunca me he sentido muy limpio.

+0

Creo que es una buena idea ... de esta manera no se replica el objeto modelo en ViewModels heredados –

5

relacionadas con el comentario anterior de Enrico. ViewModels no debe combinarse estrechamente con las vistas, debe ser al revés. Las vistas deben estar ligeramente acopladas a ViewModels. Un ViewModel no debe conocer una vista, esto le permite probar fácilmente un ViewModel. Todas las interacciones entre Views con ViewModel deben implementarse a través de propiedades en ViewModel (propiedades ICommand para acciones y otras propiedades para enlace de datos).

Lo cierto es que ViewModel está estrechamente relacionado con el modelo, por lo que el uso de genéricos anteriores permite una gran capacidad de extensión. Es el patrón que recomendaría.

Al proporcionar una clase de ViewModel que básicamente solo expone Propiedades, debería permitirle implementarlo en cualquier tipo de marco de presentación y aprovechar todo el código que ha utilizado anteriormente. En otras palabras, si se implementa correctamente, puede soltar su ensamblaje ViewModels en una ASP.Aplicación NET MVC y vincula la vista a las propiedades y no tiene cambio de código.

Un buen artículo sobre los conceptos básicos de MVVM es: this one. Realmente creo que MVVM es lo mejor que hay para el desarrollo de la interfaz de usuario. Obviamente, no todos podemos usarlo porque requiere construir una aplicación desde cero utilizando el enfoque MVVM, pero cuando estás construyendo una aplicación nueva eso no es un problema.

La única queja que tengo con ICommand es que está en la Asamblea PresentationCore que es básicamente para WPF. Si Microsoft quería un acoplamiento flojo, debería estar en otro conjunto por completo.

+2

Déjenme aclarar mi punto: con "acoplado" no me refiero a que la clase ViewModel dependa de la Vista. Sin embargo, debe exponer datos/comportamiento que sea relevante para lo que se muestra en la vista particular que se está usando con –

+1

Acerca de su queja: esto ha cambiado en .NET4.5: ahora es parte de System.dll http: // msdn .microsoft.com/es-es/library/system.windows.input.icommand.aspx – jan

4

Creo que el problema aquí es que debe haber un ViewModel para cada Vista, y no un ViewModel para cada modelo.

La razón para esto es bastante obvia, ya que solo puede establecer que un objeto sea el DataContext, ese objeto debe ser el ViewModel para esa Vista.

Cuestiones relacionadas