joydip_kanjilal
Contributor

Implementing the Single Responsibility Principle in C#

opinion
Jul 09, 20155 mins
Software Development

Take advantage of the Single Responsibility Principle to reduce coupling and increase cohesion when designing applications

SOLID is a popular acronym used to refer to a set of five principles of software architecture. In this post, I’ll present a discussion on the Single Responsibility Principle with code examples to illustrate the concepts.

The five SOLID principles are Single Responsibility (SRP), Open/Close, Liskov’s Substitution, Interface Segregation, and Dependency Inversion. The Single Responsibility Principle states that a class should have one and only one reason for change, i.e., a subsystem, module, class or a function shouldn’t have more than one reason for change. If there are two reasons for a class to change, the functionality should be split into two classes with each class handling one responsibility.  When a class has more than one responsibility, such responsibilities are coupled and this coupling leads to designs that are fragile and can break over time.

The SRP was fist defined by Robert C. Martin in his book “Agile Software Development Principles, Patterns and Practices”. The same book was later republished as a C# version as “Agile Software Development Principles, Patterns and Practices in C#”.

The benefits of SRP include:

  1. Reduction in complexity of code
  2. Increased readability, extensibility and maintenance
  3. Reusability and reduced errors
  4. Better testability
  5. Reduced coupling

The classes and interfaces in an application often become bloated when they have to perform too many different responsibilities. The basic idea behind SRP is that two or more separate responsibilities should be separated in distinct classes or modules – it is a bad design approach to couple different responsibilities in the same class if such responsibilities might change over time. Albeit being the simplest of all the SOLID principles, the SRP is one of the most difficult to be implemented the right way. Responsibility in the context of SRP can be defined as a reason for change. When a class has more than one responsibility, it has more than one reason for change. If there are multiple reasons for a class to change, it breaks SRP.

To isolate the responsibilities you would need to define a class or an interface for each responsibility. When you design your classes, the name of the class should denote its responsibility. As an example, you can have a class Repository that performs operations on the database – you can use it to persist and retrieve data to and from the underlying database. Now, what if you have two methods in your class those have different responsibilities? Does this violate SRP? In my opinion this wouldn’t violate SRP — you should understand that SRP implies that classes should have a single responsibility at the correct level of abstraction.

A bit of code

Let’s dig into some code now. Consider the following interface that represents the base interface for an implementation of a generic repository.

public interface IRepository where T : class

{

    void Create(T entity);

    void Save();

}

The Repository class implements the IRepository interface and implements the Create and Save methods as shown next.

public  class Repository : IRepository where T : class where D : DbContext, new()

{

    D entities = new D();

    public  void Create(T entity)

    {

        entities.Set().Add(entity);

    }

    public  void Save()

    {

        entities.SaveChanges();

    }

}

So far so good. Now suppose you need to write some error handling code in your Repository class. Here’s how the modified Repository class would look like.

public class Repository : IRepository where T : class where D : DbContext, new()

{

    D entities = new D();

    public void Create(T entity)

    {

        try

        {

          entities.Set().Add(entity);

        }

        catch(Exception ex)

        {

          File.WriteAllText(@”D:Error.log”, ex.ToString());

        }

    }

    public void Save()

    {      

      try

        {

           entities.SaveChanges();

        }

        catch(Exception ex)

        {

          File.WriteAllText(@”D:Error.log”, ex.ToString());

        }

    }

}

OK, so what’s the problem in this approach? Refer to the modified Repository class. This breaks SRP since the new Repository class now performs two different types of operations, i.e., it now handles two responsibilities — it stores data to the underlying data store and also logs the errors (if any) to a text file. A Repository class is not supposed to log errors as it is not the purpose of it. I prefer using repositories primarily to isolate business logic code from the data access logic. In all possibilities logging errors is not what a repository class is supposed to do. Also, there might be a possibility that over a period of time you would have to log the errors to many different log targets, i.e., file, database, event viewer, etc. A better approach should be to isolate the code that performs logging in the Repository class to another class.

class FileLogger

    {

       public string Path

       {

         get; set;

       }

       public void Log(string message)

        {

            File.WriteAllText(Path, message);

        }

    }

If you need to update the logic that handles logging, you can just make the changes in the FileLogger class without having to change the Repository class.

joydip_kanjilal
Contributor

Joydip Kanjilal is a Microsoft Most Valuable Professional (MVP) in ASP.NET, as well as a speaker and the author of several books and articles. He received the prestigious MVP award for 2007, 2008, 2009, 2010, 2011, and 2012.

He has more than 20 years of experience in IT, with more than 16 years in Microsoft .Net and related technologies. He has been selected as MSDN Featured Developer of the Fortnight (MSDN) and as Community Credit Winner several times.

He is the author of eight books and more than 500 articles. Many of his articles have been featured at Microsoft’s Official Site on ASP.Net.

He was a speaker at the Spark IT 2010 event and at the Dr. Dobb’s Conference 2014 in Bangalore. He has also worked as a judge for the Jolt Awards at Dr. Dobb's Journal. He is a regular speaker at the SSWUG Virtual Conference, which is held twice each year.

More from this author