Real Swiss don’t need SRP, do they?


Introduction

You may ask yourself why I publish another article about the single responsibility principle (SRP). We already had some very good post about this principle at Los Techies, e.g. here, here and here to mention just a few. Well, the reason is that I consider this principle one of the most helpful ones to achieve higher quality of code and better design when applied consistently. And I want to approach this topic from a different standpoint than is usually done by other authors…

What does SRP mean? The theoretical explanation (you might have read or hear many times already) is

“There is one and only one reason to change a class.”

What does this mean? This means that we should start to think small. Each complex problem cannot or cannot easily be solved as a whole. It is much easier to first divide the problem in smaller sub-problems. Each sub-problem has a reduced complexity compared to the overall problem and can then be tackled separately. There is a proverb whose origin is from the Romans which says: “Divide et imperat”. Translated to English this means: “Divide and reign”. It was not possible to the Roman emperor to reign the whole empire alone. No, he divided the empire into independent regions and appointed a king or sovereign to each region. At the end the Roman emperor had not much more to do than orchestrate those kings or sovereigns that reported to him.

Now what does this mean for me as a developer? Each developer has to solve problems. Very often these problems are rather complex. Often the boundary conditions defining the problem even change. Now we should start thinking in terms of “divide et imperat”. Let’s find the sub-problems in the domain we are working in. Keep on dividing each sub-problem into sub-sub-problems until you reach the point where such a “mini-problem” has just one single task left. Let’s then solve each of those “mini-problem” in it’s own class. Since each class only has one single task to fulfill there is (as a consequence) only one reason left to change this class. We only have to change this class if the corresponding task changes.

Instead of tasks one often talks of responsibility. Each class should have a single responsibility. The responsibility is to just accomplish the assigned task.

I want to say it again: Applying the single responsibility principle leads to higher quality of code and to better design! Why? Because the code is

  • more readable, that is easier to understand
  • less error prone
  • more robust
  • better testable
  • better maintainable and extendable

Swiss people think differently…

But wait a moment. Swiss people are not descendants of the Romans…

A real Swiss does not need to follow the single responsibility principle. That’s something for others but not for us. As a representative sample I want to present you one of our most successful products: the Swiss army knife.

swiss-knife

One can clearly see that this product has not just one single responsibility. There are several responsibilities assembled in a single unit. We have 2 knifes, one can opener, one bottle opener, an awl and a corkscrew. This unit is very handy and fits well into the pocket of every real Swiss man.

  cybertool_lite-lg

Some people prefer to even pack more functionality into this unit as you can see in this second picture at right.

 

 

 

 

giantWell, this one is even better! But now I have to admit, that not every pocket is big enough for this tool. Thus only the strongest Swiss men get one.

 

 

 

 

 

 

 

SWISS_ALPENFLAGE_HELMETAnother tool comes to my mind when remembering the time I passed in the Swiss army. Our helmet is also considered to be a multi purpose tool. We primarily use it to protect our heads from injuries but it has as well served me many times as a pillow. It is even considered as an anti-hand-grenade tool. We were told that if a hand grenade is thrown at us and we have no time or possibility to throw it away then we should just put our helmet over it and burden it with our body. To be honest, I’ve never tried it…

 

 

CowWithBallHey, wait a moment. I can give you another sample where we clearly show to the rest of the world that the SRP is not for us. It’s our famous Swiss cows. They are not only good for providing us milk and eventually meet; no, they are also very good soccer players! Currently we have 3 of them in our national soccer team.

 

 

 Milka-Cow-25254

Not to forget the famous Milka cow! Here a cow is used as an advertising medium. This is a very important responsibility by its own.

 

 

Well, I could possibly continue to give you good samples of Swiss products that are really successful without respecting the SRP.

 

 

Why does the rest of the world consider SRP to be important?

Imagine one of the items of a Swiss army knife gets bent. It would possibly render the whole item useless or at least harm its functionality. I will have to throw away the whole knife. What a waste!

Or imagine that I am really happy with my Swiss army knife but just one element does not totally fit my needs. I would like to replace just this element with another one which is better suited to my needs. I cannot do it! It’s just not possible without (negatively) affecting all the other elements of the knife.

venezia-besteck_hqImagine having a nice dinner with your wife or husband in a first class restaurant. You certainly have had lots of knifes, forks and spoons. Each element serving for a single purpose. There are knifes to cut steaks or pizzas or knifes to eat fish and so on. Each item is optimized for it’s specific task. If one of these items gets broken or if it doesn’t fulfill it’s duty any more then it can be replaced without affecting the other items.

 190_gedeck_03

The same can be said for the glasses and dishes. It doesn’t make sense to have only one single glass for all kinds of beverages. If you like wine then you know what I mean. Red wine  tastes significantly better in bigger glasses than white wine.

The same can be said about the dishes. It just doesn’t make sense to serve soup in the same dish as a nice T-bone steak with baked potato is served.

 

Let’s start coding!

Too many developers still don’t respect the SRP. I consider this as one of the primary reasons why an application get’s unmanageable with the time. More and more the code base resembles a plate of Spaghetti. Consider the following sample.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
 
    private void btnBrowse_Click(object sender, EventArgs e)
    {
        openFileDialog1.Filter = "XML Document (*.xml)|*.xml|All Files (*.*)|*.*";
        var result = openFileDialog1.ShowDialog();
        if (result == DialogResult.OK)
        {
            txtFileName.Text = openFileDialog1.FileName;
            btnLoad.Enabled = true;
        }
    }
 
    private void btnLoad_Click(object sender, EventArgs e)
    {
        listView1.Items.Clear();
        var fileName = txtFileName.Text;
        using (var fs = new FileStream(fileName, FileMode.Open))
        {
            var reader = XmlReader.Create(fs);
            while (reader.Read())
            {
                if(reader.Name != "product") continue;
                var id = reader.GetAttribute("id");
                var name = reader.GetAttribute("name");
                var unitPrice = reader.GetAttribute("unitPrice");
                var discontinued = reader.GetAttribute("discontinued");
                var item = new ListViewItem(
                      new string[]{id, name, unitPrice, discontinued});
                listView1.Items.Add(item);
            }
        }
    }
}

A sample XML document could be

<?xml version="1.0" encoding="utf-8" ?>
<products>
  <product id="1" name="IPod Nano" unitPrice="129.55" discontinued="false"/>
  <product id="2" name="IPod Touch" unitPrice="259.10" discontinued="false"/>
  <product id="3" name="IPod" unitPrice="78.95" discontinued="true"/>
</products>

Such kind of code we can find all the time in any type of company. Such code is not only produced by part time developers but also by a lot of developers considering themselves as being professional developers. I would consider this not to be an exception but rather the norm.

The story behind this code is:

The user can select an XML document which contains a list of products from the file system. This XML document is then loaded and display it on screen.

I have kept the above code sample as short as possible. The structure of the XML is very simple, the product has only very few attributes and there is no error handling. In reality the above code would be much longer and convoluted.

Now let’s analyze which and how many responsibilities the above code has:

Refactoring step by step

Step 1: Defining a model

One of the first concepts one can find is an implicit model. Since we are importing product data from an XML document it makes sense to introduce a Product entity as our model

image

By carefully analyzing the above code snippet and the given XML document we can define the following model

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal UnitPrice { get; set; }
    public bool Discontinued { get; set; }
}

Step 2: Extracting the loading (and parsing) into a repository

We also can recognize a distinct concern of loading a list of products from a data source. In this case the data source is a XML document

image

Wouldn’t it make sense to have a specific component whose concern it is to load a list of products from a data source and just return a list of instances of type Product? Some thing like this:

public interface IProductRepository
{
    IEnumerable<Product> GetByFileName(string fileName);
}

The repository is responsible to load, parse and map the XML document and return just a list of Product items to the caller.

First Refactoring

If we now refactor the sample with the assumption of having a product entity and a product repository the code might look like this:

private IProductRepository repository;
 
private void btnLoad_Click(object sender, EventArgs e)
{
    listView1.Items.Clear();
    var fileName = txtFileName.Text;
    var products = repository.GetByFileName(fileName);
    foreach (Product product in products)
    {
        var item = new ListViewItem(new[]
                                        {
                                            product.Id.ToString(),
                                            product.Name,
                                            product.UnitPrice.ToString(),
                                            product.Discontinued.ToString()
                                        });
        listView1.Items.Add(item);
    }
}

Although our code already looks much more polished than before we can still not be happy. A view (and here the form is a view) should only contain logic that is strictly related to presenting data and delegating requests triggered by the user to a controller or presenter. Thus we introduce a pattern which separates the concerns of a) visualization, b) orchestration and c) (data-) model. A pattern that perfectly fits our needs is the Model-View-Presenter pattern (MVP). The presenter is the component that orchestrates the interactions between model, view and (external) services. In this pattern the presenter is in command.

Let’s analyze what should be the responsibility of the view

  • delegate the user’s request to choose an XML document to the presenter
  • delegate the user’s request to load the data from the selected XML document to the presenter
  • provide the name of the selected XML document to the presenter
  • accept a file name (of a selected XML document) from the presenter
  • display a given list of products provided by the presenter (in a ListView control)

Second refactoring

Assuming that we have such a ProductPresenter class we can then refactor the view (or form-) code like this

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
 
    private ProductPresenter presenter;
 
    private void btnBrowse_Click(object sender, EventArgs e)
    {
        presenter.BrowseForFileName();
    }
 
    private void btnLoad_Click(object sender, EventArgs e)
    {
        presenter.GetProducts();
    }
 
    public void ShowProducts(IEnumerable<Product> products)
    {
        listView1.Items.Clear();
        foreach (Product product in products)
        {
            var item = new ListViewItem(new[]
                                            {
                                                product.Id.ToString(),
                                                product.Name,
                                                product.UnitPrice.ToString(),
                                                product.Discontinued.ToString()
                                            });
            listView1.Items.Add(item);
        }
    }
 
    public string GetFileName()
    {
        return txtFileName.Text;
    }
 
    public void SetFileName(string fileName)
    {
        txtFileName.Text = fileName;
        btnLoad.Enabled = true;
    }
}

Note that the above code now contains only display related code or code that delegates a user request to the presenter. We have reached good separation of concerns so far.

Now we have to implement the presenter. As said before the presenter is responsible to orchestrate the collaboration of model, view and external/additional services. Thus the presenter should not contain any business logic. The presenter should be slim and slick! He delegates all work to other components. 

Avoid implementing a fat presenter which is considered to be an anti-pattern.

We have identified so far (see code above) that the presenter needs at least the following two methods

public class ProductPresenter
{
    public void BrowseForFileName()
    { ... }
 
    public IEnumerable<Product> GetProducts()
    { ... }
}

and the presenter accesses it’s view (that is in this case the form) via an interface

public interface IProductView
{
    void Initialize(ProductPresenter presenter);
    string GetFileName();
    void ShowProducts(IEnumerable<Product> products);
    void SetFileName(string fileName);
}

which has to be implemented by the form. That is

public partial class Form1 : Form, IProductView
{ ... }

Let’s have a look at the implementation of the presenter

public class ProductPresenter
{
    private readonly IOpenFileDialog openFileDialog;
    private readonly IProductRepository repository;
    private readonly IProductView view;
 
    public ProductPresenter()
    {
        view = new Form1();
        view.Initialize(this);
        repository = new ProductRepository();
        openFileDialog = new OpenFileDialogWrapper();
    }
 
    public void BrowseForFileName()
    {
        openFileDialog.Filter = "XML Document (*.xml)|*.xml|All Files (*.*)|*.*";
        var result = openFileDialog.ShowDialog();
        if (result == DialogResult.OK)
            view.SetFileName(openFileDialog.FileName);
    }
 
    public void GetProducts()
    {
        var products = repository.GetByFileName(view.GetFileName());
        view.ShowProducts(products);
    }
}

It’s obvious from the above code that the presenter does not do much more than orchestrate. It interacts with the view and the repository as well as with an open file dialog service (this is just a wrapper around the OpenFileDialog class of the .NET framework).

Please note the second line in the constructor. The presenter calls the Initialize() method of the view and passes itself as a reference. As such the view gets knowledge of it’s responsible presenter. Remember that the presenter is in command in the model-view-presenter triad!

Starting the application

How can the application be started? After the refactoring we do not give the command to the view/form but to the presenter. Thus we might have something like this:

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
 
        var presenter = new ProductPresenter();
        Application.Run((Form) presenter.View);
    }
}

In the Main() method we instantiate a product presenter which in turn internally creates an instance of it’s dedicated view (which is a form in our case). We then use the presenter’s view and pass it to the Run() method of the application object.

We have to add a property View to the presenter to complete it’s implementation

 
public IProductView View
{
  get { return view; }
}

Step 4: Implementing the repositiory

We still have to implement the repository. There is just one method we need to implement

public class ProductRepository : IProductRepository
{
    public IEnumerable<Product> GetByFileName(string fileName)
    {
        var products = new List<Product>();
        using (var fs = new FileStream(fileName, FileMode.Open))
        {
            var reader = XmlReader.Create(fs);
            while (reader.Read())
            {
                if (reader.Name != "product") continue;
                var product = new Product();
                product.Id = int.Parse(reader.GetAttribute("id"));
                product.Name = reader.GetAttribute("name");
                product.UnitPrice = decimal.Parse(reader.GetAttribute("unitPrice"));
                product.Discontinued = bool.Parse(reader.GetAttribute("discontinued"));
                products.Add(product);
            }
        }
        return products;
    }
}

Now I have every piece needed to make the application run.

Refactoring again

But wait a moment! There are still at least 3 concerns handled by the repository class. One is the retrieval of the data, another one the looping over the nodes of the XML document and the third one is the mapping of a XML node to a product. So let’s refactor again:

public class ProductRepository : IProductRepository
{
    private readonly IFileLoader loader;
    private readonly IProductMapper mapper;
 
    public ProductRepository()
    {
        loader = new FileLoader();
        mapper = new ProductMapper();
    }
 
    public IEnumerable<Product> GetByFileName(string fileName)
    {
        var products = new List<Product>();
        using (Stream input = loader.Load(fileName))
        {
            var reader = XmlReader.Create(input);
            while (reader.Read())
            {
                if (reader.Name != "product") continue;
                var product = mapper.Map(reader);
                products.Add(product);
            }
        }
        return products;
    }
}

Now I have a file loader which is responsible for loading the XML document and returning it as a stream to me. And I also have a mapper which is responsible to map a single XML node to a product. As a result the code of the repository has become very simple and manageable.

So let’s have a look at the implementation of the mapper component

public interface IProductMapper
{
    Product Map(XmlReader reader);
}
 
public class ProductMapper : IProductMapper
{
    public Product Map(XmlReader reader)
    {
        if (reader == null)
            throw new ArgumentNullException("XML reader used when mapping cannot be null.");
        if (reader.Name != "product")
            throw new InvalidOperationException("XML reader is not on a product fragment.");
 
        var product = new Product();
        product.Id = int.Parse(reader.GetAttribute("id"));
        product.Name = reader.GetAttribute("name");
        product.UnitPrice = decimal.Parse(reader.GetAttribute("unitPrice"));
        product.Discontinued = bool.Parse(reader.GetAttribute("discontinued"));
        return product;
    }
}

The mapper code is very straight forward. I have even introduced some basic error handling. Note that I could still go farther with SRP and introduce an XML attribute parser (helper-) class if I want to go to the max… but let’s just stop here for the moment!

The implementation of the (file-) loader is also very simple

public interface IFileLoader
{
    Stream Load(string fileName);
}
 
public class FileLoader : IFileLoader
{
    public Stream Load(string fileName)
    {
        return new FileStream(fileName, FileMode.Open);
    }
}

 

Class diagram of the fully refactored sample

The image below shows the class diagram of the fully refactored sample. There are many components involved in this little sample. But each component is very simple and has just one single responsibility.

Model

The sample code

You can find the code of the original and the fully refactored sample here. Just use a SVN client like TortoiseSVN to download the code.

Summary

You might be overwhelmed by the sheer amount of classes (and code) introduced by the refactoring. For this simple sample it is certainly an overhead not worth the investment. But don’t forget that real applications are way more complex than this simple example. The more complex and the bigger an application becomes the more important is SRP. With the aid of the SRP a complex problem can the be reduced to many small sub-problems which are easy to solve in isolation. Just remember the sample of the Roman empire I gave you in the introduction of this post.

In my daily work, when respecting and applying the SRP I always got huge benefits. My code is more robust, more stable, better understandable and maintainable. At the end I get a better and much cleaner design.

Coming from place far far away