Tuesday, August 15, 2017

SOLID - 1. The Single Responsibility Principle

With this article I am starting a series about five of the most important principles in software development.

These 5 principles came to be called SOLID after their popularization through Robert C. Martin ("Uncle Bob"). They are part of his famous book "Agile Principles, Patterns and Practices".

But after all, why FIVE principles? Because SOLID is an acronym. It stands for:



These principles, when properly applied, help to eliminate the design smells of your code, allowing greater ease of maintenance and extension. In this first post, I will explain the Single Responsibility Principle, sometimes also referred to as “Separation of concern”.

You can read up on my other articles on SOLID here:


The Single Responsibility Principle

This principle is nothing more than a different perspective on one of the most fundamental principles of object oriented software design: cohesion. Its definition says: A class must have only one reason to change.

We will try to understand what this means and any problems caused by violation of this principle.

A class with more than one reason to change has more than one responsibility, that is, it is not cohesive. This introduces serveral problems:

  • It is difficult to understand and therefore difficult to maintain.
  • It cannot easily be reused.
  • With responsibilities intertwined in the same class, it can be difficult to change one of these responsibilities without compromising others (rigidity) and it may end up breaking other parts of the software (fragility).
  • The class ends up having an excessive number of dependencies, and therefore is more subject to changes due to changes in other classes aswell.

Let's take a look at a common violation of SRP. Imagine a class "ShoppingList":

public class ShoppingList
{
    // ..
    public void AddProduct(Product product) { }

    public void RemoveProduct(Product product) { }

    public MemoryStream GenerateExcelExport() { }
}

This example shows an evident violation of the Single Responsibility Principle. It mixes responsibilities that should be distinct components of the software. While the first two methods make sense in the context of the shopping list’s responsibilities, the latter is related to exporting data in a particular format, which this class should not be concerned with at all.

We can solve this by moving the export logic into its own class:

public class ShoppingList
{
    // ..
    public void AddProduct(Product product) { }

    public void RemoveProduct(Product product) { }
}


public class ShoppingListExporter : AbstractExcelExport<ShoppingList>
{
    public override MemoryStream GenerateExport(ShoppingList entity)
    {
        // Export logic..
    }
}

Now, changes can be applied independently. 

Both classes only have one reason to change. A revision to the business rules of handling the shopping list will only impact the “ShoppingList” class and changes related to the export format will only leave the “ShoppingListExporter” class subject to change. As a result, the code is much better to maintain and each component can be easily exchanged.

Ignorance of the Single Responsibility Principle sometimes leads to so called “God”-classes with thousands of lines of code, gigantic methods and a huge number of dependencies that make them almost impossible to maintain.

Other common examples of violations against SRP include classes that mix business and persistence logic or view models that present business rules.


Beware, some examples are less obvious...

Even if a class is only doing tasks related to its domain, it may still be concerned with too many things. When implementing a class, ask yourself: "How can I further split up the responsibilies of this class?" If you cannot find an answer to this question, your class (propably) follows the Single Responsibilty Principle pretty well.

Look at the whole picture...

But don't stop there. The SRP should be applied throughout all layers of your software, from methods to classes to namespaces to projects to the solution itself.


Conclusion

SRP is one of the most important (and also most violated) principles that exists in object oriented programming. By remembering it and applying it thoughfully, you will design smaller, more cohesive, easier-to-understand code that is highly maintainable.