Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should a ViewModel be initialized via constructor, properties, or method call

I'm fighting a few different design concepts within the context of MVVM that mainly stem from the question of when to initialize a ViewModel. To be more specific in terms of "initializing" I'm referring to loading values such as selection values, security context, and other things that could in some cases cause a few second delay.

Possible strategies:

  1. Pass arguments to ViewModel constructor and do loading in the constructor.
  2. Only support a parameterless constructor on the ViewModel and instead support initialize methods that take parameters and do the loading.
  3. A combination of option 1 and 2 where arguments are passed to the ViewModel constructor but loading is deferred until the an Initialize method is called.
  4. A variation on option 3 where instead of parameters being passed to the ViewModel constructor they are set directly on properties.

Affect on ViewModel property getters and setters

In cases where initialization is deferred there is a need to know whether the ViewModel is in a state that is considered available for which the IsBusy property generally serves just as it does for other async and time consuming operations. What this also means though is that since most properties on the ViewModel expose values retrieved from a model object that we constantly have to write the following type of plumbing to make sure the model is available.

public string Name
{
    get 
    {  
        if (_customerModel == null) // Check model availability
        {
            return string.Empty;
        }

        _customerModel.Name;
    }
}

Although the check is simple it just adds to the plumbing of INPC and others types of necessities that make the ViewModel become somewhat cumbersome to write and maintain. In some cases it becomes even more problematic since there may not always be a reasonable default to return from the property getter such might be the case with a boolean property IsCommercialAccount where if there is no model available it doesn't make sense to return true or false bringing into question a whole slew of other design questions such as nullability. In the case of option 1 from above where we passed everything into the constructor and loaded it then we only need to concern ourselves with a NULL ViewModel from the View and when the ViewModel is not null it is guaranteed to be initialized.

Support for deferred initialization

With option 4 it is also possible to rely on ISupportInitialize which could be implemented in the base class of the ViewModel to provide a consistent way of signaling whether the ViewModel is initialized or not and also to kick off the initialization via a standard method BeginInit. This could also be used in the case of option 2 and 3 but makes less sense if all initialization parameters are set all in one atomic transaction. At least in this way, the condition shown above could turn into something like

How the design affects IoC

In terms of IoC I understand that options 1 and 3 can be done using constructor injection which is generally preferred, and that options 2 and 4 can be accomplished using method and property injection respectively. My concern however is not with IoC or how to pass in these parameters but rather the overall design and how it affects the ViewModel implementation and it's public interface although I'd like to be a good citizen to make IoC a bit easier if necessary in the future.

Testability

All three options seem to support the concept of testability equally which doesn't help much in deciding between these options although it's arguable that option 4 could require a more broad set of tests to ensure proper behavior of properties where that behavior depends on the initialization state.

Command-ability

Options 2, 3, and 4 all have the side effect of requiring code behind in the View to call initialization methods on the ViewModel however these could be exposed as commands if necessary. In most cases one would probably be loading calling these methods directly after construction anyways like below.

var viewModel = new MyViewModel();
this.DataContext = viewModel;
// Wrap in an async call if necessary
Task.Factory.StartNew(() => viewModel.InitializeWithAccountNumber(accountNumber));

Some other thoughts

I've tried variations on these strategies as I've been working with the MVVM design pattern but haven't concluded on a best practice yet. I would love to hear what the community thinks and attempt to find a reasonable consensus on the best way forward for initializing ViewModels or otherwise dealing with their properties when they are in an unavailable state.

An ideal case may be to use the State pattern where the ViewModel itself is swapped out with different ViewModel objects that represent the different states. As such we could have a general BusyViewModel implementation that represents the busy state which removes one of the needs for the IsBusy property on the ViewModel and then when the next ViewModel is ready it is swapped out on the View allowing that ViewModel to follow the stategy outlined in option 1 where it is fully initialized during construction. This leaves open some questions about who is responsible for managing the state transitions, it could for example be the responsibility of BusyViewModel to abstract something similar to a BackgroundWorker or a Task that is doing the initialization itself and will present the internal ViewModel when ready. Swapping the DataContext on the view on the other hand may require either handling an event in the View or giving limited access to the DataContext property of the View to BusyViewModel so it can be set in the traditional state pattern sense. If there is something similar that people are doing along these lines I would definitely like to know because my google searching hasn't turned up much yet.

like image 753
jpierson Avatar asked Jul 21 '12 23:07

jpierson


1 Answers

My general approach to object oriented design, whether I am creating a view model, or an other type of class is that; Everything that can be passed to the constructor, should be passed to the constructor. This reduces the need to have some sort of IsInitialized state and makes your objects less complex. Sometimes certain frameworks make it hard to follow this approach, IoC containers for example (although they should allow constructor injection), but I still adhere to it as a general rule.

like image 102
ColinE Avatar answered Oct 15 '22 20:10

ColinE