How to Apply the Single Responsibility Principle to View Models in Silverlight and WPF

When you’re using the MVVM pattern with WPF or Silverlight it’s very easy to a have ViewModels that do too much.

In this part of the Silverlight Refactoring series we will convert a ViewModel with multiple responsibilities so that it adheres to the Single Responsibility Principle (SRP).

The code used in this post can be downloaded here.

Why should you care?

Currently the ViewModel looks like this

using System;
using System.ComponentModel;
using System.Windows.Input;

namespace SilverlightCalculator
{
    public class CalculatorViewModel : INotifyPropertyChanged
    {
        private string _firstValue;
        private string _secondValue;
        private string _result;

        private readonly ICommand _calculateCommand;       

        public event PropertyChangedEventHandler PropertyChanged;

        public CalculatorViewModel()
        {
            _calculateCommand = new RelayCommand(Calculate){IsEnabled = true};
        }

        public void Calculate()
        {
            Result = (Convert.ToInt32(FirstValue) + Convert.ToInt32(SecondValue)).ToString();
        }

        public string FirstValue
        {
            get { return _firstValue; }
            set
            {
                _firstValue = value;
                OnPropertyChanged("FirstValue");
            }
        }

        public string SecondValue
        {
            get { return _secondValue; }
            set
            {
                _secondValue = value;
                OnPropertyChanged("SecondValue");
            }
        }

        public string Result
        {
            get { return _result; }
            private set
            {
                _result = value;
                OnPropertyChanged("Result");
            }
        }

        public ICommand CalculateCommand
        {
            get { return _calculateCommand; }
        }

        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this,
                    new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

As you can see the logic to add two numbers is in the Calculate method of the ViewModel.

Therefore the ViewModel is responsible for calculating two numbers.

A ViewModel without separation of concerns is difficult to test, maintain and contains code you can’t reuse.

Although we’re only adding two numbers here, brushing the issue under the carpet will have consequences later down the line.

For example in addition to adding numbers what if another developer was given the task of storing the result in a database.

If the ViewModel was as it is now, with no separation of concerns, there is a chance they would put the code to store the value in the calculate method like this

public void Calculate()
{
    // Calculate result
    Result = (Convert.ToInt32(FirstValue) + Convert.ToInt32(SecondValue)).ToString();

    // Store value in database
    DatabaseConnection connection = new DatabaseConnection();
    DatabaseCommand command = new DatabaseCommand("TSQL TO STORE RESULT", connection);
    command.ExecuteNonQuery();
}

Now the ViewModel would be responsible for two things. Next it would be three and this would continue until someone decides to separate the concerns or it gets to a stage where it’s impossible to work with let alone read.

Refactoring the ViewModel to follow the single responsibility principle

Let’s start by creating a class for calculating numbers.

public class Calculator
{
    public int Add(int firstValue, int secondValue)
    {
        return firstValue + secondValue;
    }
}

Notice how the parameters passed to the method are integers, the calculator doesn’t accept string values.

What we are saying here is while it’s acceptable for the ViewModel to use strings for storing numbers the domain layer (or business logic if you prefer) does not.

In other words the add method does nothing more than add two numbers together. Validation should be handled elsewhere.

This makes the unit tests for the calculator very easy to write

[TestFixture]
public class When_calculating_numbers
{
    [Test]
    public void Should_be_able_to_add_two_numbers_together()
    {
        // Arrange
        Calculator calculator = new Calculator(); 

        // Act
        var result = calculator.Add(5, 5);

        //Assert
        Assert.That(result, Is.EqualTo(10));
    }
}

The code below shows how the ViewModel could create an instance of the Calculator class and call the Add Method. Notice how I say could and not should. We’ll see why later in the series.

public void Calculate()
{
    Calculator calculator = new Calculator();
    Result = calculator.Add(Convert.ToInt32(FirstValue), Convert.ToInt32(SecondValue)).ToString();
}

So what have we achieved?

As good boy scouts we have ‘left the ViewModel cleaner than we found it’.

By following the single responsibility principle we have created a ViewModel which is a better canvas for other developers to work with.

If you have arrived here from a search engine, this post is part of series about refactoring Silverlight applications. So if you’re thinking why is the calculator class tightly coupled to the ViewModel? Find out how this can be resolved by applying SOLID design principles using MEF in Silverlight and WPF.

Comments

  • http://profiles.google.com/hanson.andrew Andrew Hanson

    Forgive the comment on an old post, but I’m sort of struggling with this exact problem right now and had a question for you. I’m sold on the single responsibility importance, and everything so no argument there. My question is how does this new Calculator object fit into the overall hierarchy of the application.
    Let’s assume the application you are describing above had a form with a button and when the button was clicked somehow the calculator object needs to be instantiated, and the add method called with the first value and second value properties of the view model with the result being stored in the result property of the view model.
    Your button click is bound to a routed command of some kind, so my question is where does this routed command go? If it goes to the View Model, you’re still tying a dependency between the view model and the calculator object. It would be better if the view model and calculator objects knew nothing of each other. I just can’t resolve where my command should be handled so my View Model and my Logic objects are both testable.

  • Anonymous

    Andrew,

    Yes I could have added (injected) in something else into the ViewModel that would be invoked when the calculate button was clicked on.

    For the example used in the post adding this would have been overkill.

    If you do inject something in to do the calculation, I would write a test to assert it was called when the calculate button was clicked, but wouldn’t test what should do in the same unit tests for the ViewModel.