Single Responsibility Principle in C#

SPONSOR AD

Single Responsibility Principle in C# with Examples

In this article, I will discuss the Single Responsibility Principle in C# with Examples. Please read our previous article before proceeding to this article, where we discussed the basics of the SOLID Design Principle in C#. S in SOLID stands for the Single Responsibility Principle or SRP. As part of this article, we will discuss the following pointers in detail.

  1. What is the Single Responsibility Principle in C#?
  2. How can we achieve the Single Responsibility Principle in C#?
  3. Example without using the Single Responsibility Principle.
  4. Problems of not following the Single Responsibility Principle.
  5. Example using the SRP in C#.
What is the Single Responsibility Principle in C#?

The Single Responsibility Principle in C# states, “Each software module or class should have only one reason to change“. In other words, we can say that each module or class should have only one responsibility. 

So, we need to design the software so that everything in a class or module should be related to a single responsibility. That does not mean your class should contain only one method or property. You can have multiple members (methods or properties) if they are related to a single responsibility or functionality. So, with the help of SRP in C#, the classes become smaller, cleaner, and, thus, easier to maintain. Before Proceeding further and understanding the Single Responsibility Principle, first, we need to understand What is Responsibility.

What is Responsibility?

An application can have many functionalities (features). For example, suppose you are developing an e-commerce application. In that case, that application may have many features or functionalities such as Registering users, providing login functionality, displaying the product list, allowing the user to place an order, Providing Payment Functionality, Shipping the Order, Billing the Order, Logging the Order Information for Auditing and for Security purpose, sending the Order Invoice to the customer, etc. You can consider these functionalities or features as responsibilities. Another point you need to remember is that changing the functionality means changing the class responsible for that functionality.

SPONSOR AD
How can we apply the Single Responsibility Principle in C#?

We can improve the application code readability, maintainability, and flexibility By applying the Single Responsibility Principle (SRP). SRP is one of the SOLID principles that states a class should have only one reason to change. That means a class should not mixed with multiple responsibilities. So, each class should be designed to perform a specific task onlyLet us understand how we can apply the Single Responsibility Principle in C# while designing the classes:

  • Identify Responsibilities: First, we need to Identify the different functionalities or responsibilities that a class should have, such as data access, validation, logging, caching, serialization, user authentication, etc.
  • Decompose into Smaller Classes: Once we Identify the responsibilities, we must create separate classes for each responsibility. We should not mix multiple functionalities or responsibilities in a single class.
  • Separate Concerns: Finally, we need to make sure that each class should only perform one specific task.
Examples to Understand the Single Responsibility Principle using C#:

Let us understand the Single Responsibility Principle in C# with an Example. Suppose we need to design an Invoice class. As we know, an Invoice class calculates various amounts based on its data. The Invoice class does not know how to retrieve the data or how to format the data for displaying, printing, logging, sending an email, etc. 

If we write the database, business logic, and display logic in a single class, our class performs multiple responsibilities. Then, changing one responsibility without breaking the other responsibilities becomes very difficult. So, by mixing multiple responsibilities into a single class, we are getting the following disadvantages,

  1. Difficult to Understand
  2. Difficult to Test
  3. Chance of Duplicating the Logic of Other Parts of the Application
Example Without using Single Responsibility Principle in C#:

Let us see the example without following the Single Responsibility Principle in C#. We will see the problems if we do not follow the Single Responsibility Principle. Then, we will see how we can overcome the problem using the Single Responsibility Principle so that you will better understand SRP.  Please look at the following diagram we want to implement in our example.

Voilatiing Single Responsibility Principle in C#

As you can see in the above image, we will create an Invoice class with four functionalities, i.e., Adding and Deleting Invoices, Error Logging, and Sending Emails. As we are putting all the above four functionalities into a single class or module, we are violating the Single Responsibility Principle in C#. This is because Sending Emails and Error Logging is not a part of the Invoice module. The following is the complete code, and it is self-explained, so please go through the comments.

using System;
using System.Net.Mail;
namespace SOLID_PRINCIPLES.SRP
{
    public class Invoice
    {
        public long InvoiceAmount { get; set; }
        public DateTime InvoiceDate { get; set; }

        public void AddInvoice()
        {
            try
            {
                // Here we need to write the Code for adding invoice
                // Once the Invoice has been added, then send the  mail
                MailMessage mailMessage = new MailMessage("EMailFrom", "EMailTo", "EMailSubject", "EMailBody");
                this.SendInvoiceEmail(mailMessage);
            }
            catch (Exception ex)
            {
                //Error Logging
                System.IO.File.WriteAllText(@"c:\ErrorLog.txt", ex.ToString());
            }
        }

        public void DeleteInvoice()
        {
            try
            {
                //Here we need to write the Code for Deleting the already generated invoice
            }
            catch (Exception ex)
            {
                //Error Logging
                System.IO.File.WriteAllText(@"c:\ErrorLog.txt", ex.ToString());
            }
        }

        public void SendInvoiceEmail(MailMessage mailMessage)
        {
            try
            {
                // Here we need to write the Code for Email setting and sending the invoice mail
            }
            catch (Exception ex)
            {
                //Error Logging
                System.IO.File.WriteAllText(@"c:\ErrorLog.txt", ex.ToString());
            }
        }
    }
}

With this design, if we want to modify the logging functionality or if we want to modify the sending email functionality, then we need to modify the Invoice class. This violates the Single Responsibility Principle as we are changing the Invoice Class for other functionality. If we make the changes, we must test the logging, email, and invoicing functionality. Now let us discuss how to implement the above functionalities so that it should follow the Single Responsibility Principle using C# Language.

SPONSOR AD
Example Implementing Single Responsibility Principle in C#

Let us implement the same example by following SRP. Please have a look at the following diagram.

Single Responsibility Principle in C#

As shown in the above diagram, we will create three classes. In the invoice class, only invoice-related functionalities are going to be implemented. The Logger class is going to be used only for logging purposes. Similarly, the Email class is going to handle Email activities. Now, each class has only its own responsibilities. As a result, it follows the Single Responsibility Principle in C#. If you want to modify the email functionality, you must change the Email class only, not the Invoice and Logging class. Similarly, if you want to modify the Invoice functionalities, you must change the Invoice class only, not the Email and Logging class. Let us proceed and see how to implement this using C#.

Logger.cs

Add a class file named Logger.cs and copy and paste the following code into it. As you can see in the below class, we define all the logging activities, i.e., Info, Debug, and Error. The point that you need to remember is the class can contain multiple methods as long as all the methods belong to the same responsibility. Here, we are creating an Interface with the name ILogger with three abstract methods (by default, interface methods are abstract). Then, we implement the ILogger interface methods within the Logger class. Info, Debug, and Error will perform different logging activities, putting all these methods within the Logger class.

using System;
namespace SOLID_PRINCIPLES.SRP
{
    public interface ILogger
    {
        void Info(string info);
        void Debug(string info);
        void Error(string message, Exception ex);
    }

    public class Logger : ILogger
    {
        public Logger()
        {
            // here we need to write the Code for initialization 
            // that is Creating the Log file with necesssary details
        }
        public void Info(string info)
        {
            // here we need to write the Code for info information into the ErrorLog text file
        }
        public void Debug(string info)
        {
            // here we need to write the Code for Debug information into the ErrorLog text file
        }
        public void Error(string message, Exception ex)
        {
            // here we need to write the Code for Error information into the ErrorLog text file
        }
    }
}
MailSender.cs

We need to add another class file named MailSender.cs and copy and paste the following code. In the below MailSender class, we are defining the send mail activities. So, here, you can see, for sending email, whatever properties are required we are putting here along with the SendEmail method. For simplicity, we have not provided the logic to send the email, but in real-time, you need to write the logic within the SendEmail method to send an email.

namespace SOLID_PRINCIPLES.SRP
{
    public class MailSender
    {
        public string EMailFrom { get; set; }
        public string EMailTo { get; set; }
        public string EMailSubject { get; set; }
        public string EMailBody { get; set; }
        public void SendEmail()
        {
            // Here we need to write the Code for sending the mail
        }
    }
}
Modifying Invoice Class:

Finally, modify the Invoice class as shown below. Within this class, we are only defining the Invoice-related activities. As you can see, the Invoice class delegates the logging activity to the “Logger” class. In the same way, it also delegates the Email-sending activity to the “MailSender” class. Now, the Invoice class only concentrates on Invoice-related activities.

using System.Net.Mail;
using System;
namespace SOLID_PRINCIPLES.SRP
{
    public class Invoice
    {
        public long InvAmount { get; set; }
        public DateTime InvDate { get; set; }
        private ILogger fileLogger;
        private MailSender emailSender;
        public Invoice()
        {
            fileLogger = new Logger();
            emailSender = new MailSender();
        }
        public void AddInvoice()
        {
            try
            {
                fileLogger.Info("Add method Start");
                // Here we need to write the Code for adding invoice
                // Once the Invoice has been added, then send the  mail
                emailSender.EMailFrom = "emailfrom@xyz.com";
                emailSender.EMailTo = "emailto@xyz.com";
                emailSender.EMailSubject = "Single Responsibility Princile";
                emailSender.EMailBody = "A class should have only one reason to change";
                emailSender.SendEmail();
            }
            catch (Exception ex)
            {
                fileLogger.Error("Error Occurred while Generating Invoice", ex);
            }
        }
        public void DeleteInvoice()
        {
            try
            {
                //Here we need to write the Code for Deleting the already generated invoice
                fileLogger.Info("Delete Invoice Start at @" + DateTime.Now);
            }
            catch (Exception ex)
            {
                fileLogger.Error("Error Occurred while Deleting Invoice", ex);
            }
        }
    }
}

We must design the application following the Single Responsibility Principle in C#. Now, each class has its own responsibilities. That means now, Each software module or class should have only one reason to change.

In the next article, I will discuss Multiple Real-Time Examples of the Single Responsibility Principle (SRP) in C#. In this article, I explain the Single Responsibility Principle in C# with Examples. I hope you enjoy this article’s Single Responsibility Principle in C# with Examples.

SPONSOR AD
SPONSOR AD

8 thoughts on “Single Responsibility Principle in C#”

  1. Thank you for your good and clear explanation,
    but I think what should have been considered was the “person/s” closely associated with this “responsibility”. And the class or module is responsible and responsive to this person/s’s request

Leave a Reply

Your email address will not be published. Required fields are marked *