Single Responsibility Principle in C#

Single Responsibility Principle in C# with real-time Example

In this article, I am going to discuss the Single Responsibility Principle in C# with one real-time example. Please read our previous article before proceeding to this article where we discussed the basics of SOLID Design Principle in C#. The letter S in SOLID stands for Single Responsibility Principle which is also known as SRP. As part of this article, we are going to 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 SRP.
  5. Example using the Single Responsibility Principle.
What is the Single Responsibility Principle in C#?

The Single Responsibility Principle states that “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 to do.

So we need to design the software in such a way 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) as long as they are related to a single responsibility or functionality.

So, with the help of SRP, the classes become smaller and cleaner and thus easier to maintain.

How can we achieve the Single Responsibility Principle in C#?

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

If we write the database logic, business logic as well as the display logic in a single class, then our class performing multiple responsibilities. Then it becomes very difficult to change one responsibility without breaking the other responsibilities. So, by mixing multiple responsibilities into a single class, we are getting the following disadvantage,

  1. Difficult to understand
  2. Difficult to test
  3. Chance of duplicating the logic of other parts of the application
Example of without the Single Responsibility Principle:

Please have a look at the following diagram what we want to implement in the following example.

Voilatiing Single Responsibility Principle in C#

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

namespace SOLID_PRINCIPLES.SRP
{
    public class Invoice
    {
        public long InvAmount { get; set; }
        public DateTime InvDate { 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)
            {
                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)
            {
                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)
            {
                System.IO.File.WriteAllText(@"c:\ErrorLog.txt", ex.ToString());
            }
        }
    }
}

Now let us discuss how to implement the above functionalities in such a way that, it should follow the Single Responsibility Principle.

Implementing the Single Responsibility Principle in C#

Please have a look at the following diagram.

Single Responsibility Principle in C#

As you can see in the above diagram, now we are going to create three classes. In the invoice class, only the invoice related functionalities are going to be implemented. The Logger class is going to be used only for logging purpose. Similarly, the Email class is going to handle Email activities. Now each class having only its own responsibilities, as a result, it follows the Single Responsibility Principle in C#.

Logger.cs

Add a class file with the name Logger.cs and then copy and paste the following code in it.

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
        }
    }
}

As you can see in the above class file, we are defining all the logging activities i.e. info, debug and error. 

MailSender.cs

Now, we need to add another class file with the name MailSender.cs and then copy and paste the following code.

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
        }
    }
}

In the above MailSender class, we are defining the send mail activities. 

Modifying the Invoice class:

Finally, modify the Invoice class as shown below.

using System.Net.Mail;
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.Message);
            }
        }
        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);
            }
        }
    }
}

As you can see, the Invoice class delegating the logging activity to the “Logger” class. In the same way, delegate the Email Sending activity to “MailSender” class. Now, the Invoice class now only concentrate on the Invoice related activities.

In the next article, I am going to discuss the Open-Closed Principle in C# with a real-time example. Here, in this article, I try to explain the Single Responsibility Principle in C# with a real-time example. I hope you understood the need and use of the Single Responsibility Principle.

2 thoughts on “Single Responsibility Principle in C#”

Leave a Reply

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