SOLID – Single Responsibility Principle With C#


The Single Responsibility Principle is one of the SOLID design principles. We can define it in following ways:

  1. A reason to change: A class or method should have only one reason to change.
  2. Single Responsibility: A class or method should have only a single responsibility.

When a class has more than one responsibility, there are also more reasons to change that class.

Figure 1: Single Responsibility Principle

Single Responsibility Principle Benefits


The SRP has many benefits to improve code complexity and maintenance. Some benefits of SRP are following:

  1. Reduction in complexity of code: Code is based on its functionality. A method holds logic for single functionality or task. so it reduces the code complexity.
  2. Increased readability, extensibility, and maintenance: As each method has single functionality so it is easy to read and maintain.
  3. Reusability and reduced error: As code separates based functionality so if the same functionality uses somewhere else in an application then don’t write it again.
  4. Better testability: In the maintenance, when a functionality changes then we don’t need to test entire model.
  5. Reduced coupling: It reduced the dependency code. A method’s code doesn’t depend on other methods.

Single Responsibility Principle Example with C#


In this article for discussion and explanation purpose I am introducing to you, two fictional characters Mark (a .NET developer) and Bob (a Tech Lead).

The story begins on the day when Bob contacts Mark and asks him to take up a role of an architect in his project called “Employee Management System”. We will see how Mark progresses his knowledge about Single Responsibility Principle day by day and how finally he come up with a great solution.

Initial Development


Bob(Tech Lead) explains requirements to Mark(a .Net developer). One of the primary requirement was to build a module which will have employee registration functionality.

How Mark Started

Mark defines a class named EmployeeService. It holds both employee data and registration operation as per following code snippet.

Mark uses local storage to store data rather than database so he defines an another class named StaticData. It has a property to store employee list. The following code snippet for same.

To keep it simple, Mark chooses a console application for UI interaction. It could be a web, windows or mobile application. The following code snippet for user interaction. It’s entry point for the application.

Code Demonstration

Since module was ready, Mark demonstrated the same to Bob, and Bob appreciates about the architecture. All things are looking good and working as per expectation such as:

  1. He defines a separate class for employee’s functionality named EmployeeService rather than performs the operation in the UI/entry program.
  2. he method performs registration process. Data store in fields and pass via object rather than parameters.
  3. He defines a separate class for employee storage which is static.

But, it didn’t end the application. He added new requirements in this registration process. He said,

  1. We need to store employee email address with existing fields.
  2. When an employee registers then he/she gets an email.

Bob requested Mark to implement these changes in this existing application.

Problem with Preceding Design

There are two requirements. Both requirements have changes, those are totally different. One is changing on data; whereas another one is impacting the functionality. Hence, we have two different types of reason to change a single class. So, it violates the SRP Principle.

Mark realized that he should develop code in this way that data and functionality can be separated.

Improve Development


Mark has an understanding of the requirements. He knows very well where he needs to change in the existing code so he doesn’t have worry about it. But he needs to implement these changes without violating SRP. In essence, he knows that there should be two classes one for employee data and another for employee functionality. So, both changes will be implemented in two classes.

Mark starts application development as per new requirements. He defines a new class named Employee for data as per following code snippet.

As he uses local storage to store data so he updates StaticData class as well. The following code snippet for same.

Mark defines registration and mail sending functionality in the EmployeeService class. This class holds two methods one for registration of employee while another method for sending mail to the employee. Each method defines as per functionality so that these don’t violate SRP. The following code snippet for same.

The preceding code is developed in .NET Core that’s why .NET Core MailKit Nuget package is used to send an email. This email send code is used for demonstration purpose. It will be worked with valid credentials and domain.

As Mark uses same console application for UI interaction so he updates it as per following code snippet.

Code Demonstration

Since module was ready, Mark demonstrated the same to Bob, and Bob again appreciates about the architecture. All things are looking good and working as per expectation such as

  1. He defines separate classes for both employee data and functionality.
  2. The methods are separated based on functionalities.
  3. He defines a separate class for employee storage which is static.

Problem with Preceding Design

The preceding code is working perfectly but it is still violating the Single Responsibility Principle. The EmployeeService class holds two methods one for registration of employee while another method for sending mail to the employee. The email sending functionality is not related to employee entity directly. The email send and employee registration are totally different functionalities. If we change email sending process such as change provider, add attachment and use SSL etc then EmplyeeService class should not be changed but it isn’t possible in preceding code.

Bob explained Mark that a class should have only reason to change and have a single responsibility. The EmployeeService class has two responsibilities so it violates SRP principle. It doesn’t mean that a class can’t have more than one method. A class can have multiple methods but these are related to one entity of the application. In other words, EmployeeService class can have multiple methods related to the Employee entity such as registration, update and delete etc..Mark understood that what he did wrong here and how violates the SRP principle. Now, he knows about Single Responsibility Principle. He starts to update in existing code for SRP.

Final Development


Mark has an understanding that a class should have a single responsibility. He knows very well where he needs to change in the existing code. But he needs to implement these changes without violating SRP.

Mark knows that he needn’t build the entire application. He needs to change in the email sending functionality. He needs to make it independently so that it could be used another place in the application. So, there are two changes in the application. Email sending code should be removed from EmployeeService class. He needs to define a new class for email sending functionality.

He defines a new class named EmailService to send email as per following code snippet.

Mark uses preceding code to send an email and call it in EmployeeService as per following code snippet.

There is no change in rest of the application.

Code Demonstration

Mark demonstrated the same to Bob. Now, it follows Single Responsibility Principle. As he defined classes based on responsibilities and method based on individual functionality such

  1. He defines separate classes for employee data, functionality, email sending, UI interaction and data storage.
  2. Each class has a single responsibility and there is only one reason to change a class and method.

Now, Mark has implemented Single Responsibility Principle in the application using C#. Bob seems happy because all his requirements seem satisfied and Mark is also happy because now he has become champ in Single Responsibility Principle.

Conclusion


As per Single Responsibility Principle, if a class has two reasons to change then the class should be split into more than one classes based on the functionalities.Each class will handle only one responsibility and in future, if we need to make one more change we are going to make it in the new class which handles it.

If we put more than one functionality in one class then it introduces coupling between two functionality. So, if we change one functionality there is a chance we broke coupled functionality, which requires another round of testing to avoid any bug on the production environment.

It reduces bug fixes and testing time once an application goes into maintenance phase. It follows DRY principle.