Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVVM Inheritance With View Models

I am wondering about how to approach inheritance with View Models in the MVVM pattern. In my application I have a Data Model that resembles the following:

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

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

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

In my application I have a ViewModelBase class and then was going to have the following View Models:

  • CustomObjectViewModel
  • CustomItemViewModel
  • CustomProductViewModel

A rough implementation of the CustomObjectViewModel would resemble the following:

class CustomObjectViewModel : ViewModelBase
{
    private readonly CustomObject _customObject;

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

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

It seems logical to me that my View Models would extend themselves in the same manner as my Model did (CustomItemViewModel extends CustomObjectViewModel and so on). However, I have noticed that as I go down the inheritance tree I'll be adding additional references to the same object. This seems rather excessive to me and was wondering how to approach this problem and if it were possible to make it much cleaner.

like image 287
Richard McGuire Avatar asked Jun 10 '09 19:06

Richard McGuire


People also ask

Should ViewModel inherit from model?

So my question is, why it is wrong to inherit from a Model class? Your ViewModel should be separate from your Model (i.e. Entity in this context). Remember a "ViewModel" is a model for the view. In other words it can display same, compact or transformed information of the original model.

Can a ViewModel have multiple models?

ViewModel is nothing but a single class that may have multiple models. It contains multiple models as a property. It should not contain any method. In the above example, we have the required View model with two properties.

Can we pass view to ViewModel?

ViewModel as the bridge between the View and the Model. TL;DR: We can pass parameters to our ViewModel, use it as a data holder, also to share data between Fragments, and to persist its state across process recreation. This is part of a multi-part series regarding Advanced ViewModels on Android.

What is the use of ViewModel in MVVM?

The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.


4 Answers

Generally I would recommend you not to have inheritance between different ViewModel classes, but instead having them inherit directly from a common abstract base class.
This is to avoid introducing unnecessary complexity by polluting the ViewModel classes' interfaces with members that come from higher up in the hierarchy, but are not fully cohesive to the class's main purpose.
The coupling that comes with inheritance will also likely make it hard to change a ViewModel class without affecting any of its derived classes.

If your ViewModel classes always will reference a single Model object, you could use generics to encapsulate this rule into the base class:

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
    }
}
like image 182
Enrico Campidoglio Avatar answered Oct 05 '22 12:10

Enrico Campidoglio


Related to the comment above of Enrico. ViewModels should not be tightly coupled with views, it should be the other way around. Views should be loosely coupled to ViewModels. A ViewModel should not know about a view, this allows you to unit test a ViewModel easily.All interactions between Views with the ViewModel should be implemented via properties in the ViewModel(ICommand properties for actions and other properties for databinding).

The one thing that is true is that the ViewModel is tightly coupled with the Model, so the use of generics above allows alot of extensibility. It is the pattern that I would recommend.

By providing a ViewModel class that basically just exposes Properties it should allow you to plop that into any kind of presentation framework and leverage all the code you've used previously. In other words, if properly implemented you could drop your ViewModels assembly into a ASP.NET MVC app and tie the view to the properties and have no code change.

A good article on MVVM basics is : this one. I really think that MVVM is the best thing out there for UI development. Obviously we can't all use it because it requires building an app from the ground up using the MVVM approach, but when you're building a new app that is not an issue.

The one gripe I have with ICommand is that it is in the PresentationCore Assembly which is basically for WPF. If Microsoft wanted loose coupling it should be in another assembly altogether.

like image 23
Jose Avatar answered Oct 05 '22 12:10

Jose


I'd be interested to see if there is a better answer for this, but when I have had the same problem I've always enclosed an explicit cast of the object as a private property like so:

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)
    {
    }
}

It works, and it's the best I've come up with, but has never felt very clean to me.

like image 28
Martin Harris Avatar answered Oct 05 '22 12:10

Martin Harris


I think the issue here is that there should be one ViewModel for each View, and not one ViewModel for each model.

The reason for this is fairly obvious, as you can only set one object to be the DataContext, that object should be the ViewModel for that View.

like image 28
Cameron MacFarland Avatar answered Oct 05 '22 12:10

Cameron MacFarland