SOLID principles C#


SOLID
Hi everybody, In this post  I am going to cover the following topics.
1. Reasons behind most of the unsuccessful application.
2. Solutions.
3. Intro – SOLID principles.
4. SRP
5. OPCP
6. LSP
7. ISP
8. DIP
Reason behind most of the unsuccessful application.
Developers start building applications with good and tidy designs by using their knowledge and experience. But over the time, Applications might start producing bugs. Application design has to be altered for every change request or new feature request. After some time we might have to put lot of effort even for simple tasks and it might requires full working knowledge of the entire system. But we can’t blame change or new feature requests. They are part of the software development.We can’t stop them and refuse them either. So who is the culprit here?. Obviously it is the design of the application.
Following are the design flaws cause the damage in software, mostly.

1. Putting more stress on classes by assigning more responsibilities to them.(Lot of
functionality which is not related to a class).
2. Forcing the Classes depend on other. If a classes is depending on other (i.e tightly
coupled),then a change in one will affect other.
3. Spreading duplicate code in the system/application.
Solution
1) Choosing correct architecture (i.e. MVC, 3-tier, Layered, MVP, MVVP … etc).
2) Following Design Principles.
3) Choosing correct Design Patterns to build the software based on it’s specifications.
Now we go through the Design Principles first and will cover the rest soon.

Intro – SOLID principles
SOLID principles are the design principles enable us to manage with most of the software design problems. Robert C. Martin compiled these principles in 1990s.These principles give us ways to moving from tightly coupled code and little encapsulation to desired results of loosely coupled and encapsulating the real needs of business properly. SOLID is an acronym of the following.

. S- Single Responsibility Principle (SRP).
. O- Open closed Principle (OSP).
. L- Liskov substitution Principle (LSP).
. I- Interface Segregation Principle (ISP).
. D- Dependency Inversion Principle (DIP).

S- Single Responsibility Principle (SRP)
SRP says “Every software module should have only one reason to change“.

SingleResponsibilityPrinciple

This means that every class, or similar structure, in your code should have only one job to do. Everything in the class should be related to that single purpose. If one of them in Swiss knife needs to be changed then the whole set needs to be disturbed. It does not mean that your classes should only contain one method or property. There may be many members as long as they relate to the single responsibility.
The Single Responsibility Principle gives us a good way of identifying classes at the design phase of an application and it makes you to think of all the ways a class can change. A good separation of responsibilities is done only when the full picture of how the application should work. Let check this with an example.

public class UserService
{
      public void Register(string email, string password)
      {
          if (!ValidateEmail(email))
             throw new ValidationException("Email is not an email!");
          var user = new User(email, password);
          SendEmail(new MailMessage("mysite@nowhere.com", email)  {Subject="HEllo foo.."});
      }
      public virtual bool ValidateEmail(string email)
      {
          return email.Contains("@");
      }
      public bool SendEmail(MailMessage message)
      {
         _smtpClient.Send(message);
      }
}

It looks fine, but it is not following SRP. SendEmail and ValidateEmail method have nothing to do
with in UserService class. Lets refract it.

public class UserService
{
     EmailService _emailService;
     DbContext _dbContext;
     public UserService(EmailService aEmailService, DbContext aDbContext)
     {
         _emailService = aEmailService;
         _dbContext = aDbContext;
     }
     public void Register(string email, string password)
     {
         if (!_emailService.ValidateEmail(email))
             throw new ValidationException("Email is not an email");
         var user = new User(email, password);
         _dbContext.Save(user);
         emailService.SendEmail(new MailMessage("myname@mydomain.com", email) {Subject="Hi. How are you!";                              });

     }
}
public class EmailService
{
     SmtpClient _smtpClient;
     public EmailService(SmtpClient aSmtpClient)
     {
         _smtpClient = aSmtpClient;
     }
     public bool virtual ValidateEmail(string email)
     {
         return email.Contains("@");
     }
     public bool SendEmail(MailMessage message)
     {
         _smtpClient.Send(message);
     }
}

Now the responsibilities are divided among these two classes. Now we can say that this design is following SRP.

O – Open/Closed Principle
Open/closed Principle says ” A software module/class is open for extension and closed for modification”.

OpenClosedPrinciple
Here “Open for extension” means, we have design our module/class in such a way that new functionality can be added when new requirements are generated. “Closed for modification” means we already developed a class and it has gone through unit testing,then we should not alter it until we find any bugs. As it says that a class should open for extension, we can use inheritance to achieve this. OK, lets dive into an example.

Suppose we have a Rectangle class with properties Height and Width.

public class Rectangle{
     public double Height {get;set;}
     public double Wight {get;set; }
}

Our app needs a functionality to calculate the total area of a collection of Rectangles. As we already learned SRP (Single responsibility principle), we don’t have to put the total area calculation code inside the rectangle. So here I created another class for area calculation.

public class AreaCalculator {
     public double TotalArea(Rectangle[] arrRectangles)
     {
          double area;
          foreach(var objRectangle in arrRectangles)
          {
             area += objRectangle.Height * objRectangle.Width;
          }
      return area;
    }
}

Hey… We did it. We made our app with out violating SRP. No issues as for now. But can we extend our app so that it could calculate the area of not only Rectangles but also the area of Circles as well? Now we have an issue with the area calculation issue, because way of circle area calculation is different. Hmm.. not a big deal. We can change the TotalArea method a bit, so that it can accept array of objects as an argument. We check the object type in the loop and perform area calculation based on the object type.

public class Rectangle{
     public double Height {get;set;}
     public double Wight {get;set; }
}
public class Circle{
     public double Radius {get;set;}
}
public class AreaCalculator
{
     public double TotalArea(object[] arrObjects)
     {
          double area = 0;
          Rectangle objRectangle;
          Circle objCircle;
          foreach(var obj in arrObjects)
          {
              if(obj is Rectangle)
              {
                  objRectangle = (Rectangle)obj;
                  area += obj.Height * obj.Width;
              }
              else
              {
                 objCircle = (Circle)obj;
                 area += objCircle.Radius * objCircle.Radius * Math.PI;
              }
          }
          return area;
     }
}

Wow. We done with the change. Here we successfully introduced Circle in our app. We can add Triangle and calculate it’s area by adding one more “if” block in the TotalArea method of AreaCalculator. But every time we introduce a new shape we have to alter the TotalArea method. So the AreaCalculator class is not closed for modification. How can we make our design to avoid this situation? Generally we can achieve this by referring to abstractions for dependencies, such as interfaces or abstract classes, rather than using concrete classes. Such interfaces can be fixed once developed so the classes that depend upon them can rely upon unchanging abstractions. Functionality can be added by creating new classes that implement the interfaces. So lets refract our code with the help of an interface.

public abstract class Shape
{
     public abstract double Area();
}

Inheriting from Shape the Rectangle and Circle classes now looks like this:

public class Rectangle: Shape
{
     public double Height {get;set;}
     public double Width {get;set;}
     public override double Area()
     {
          return Height * Width;
     }
}
public class Circle: Shape
{
     public double Radius {get;set;}
     public override double Area()
     {
         return Radius * Radus * Math.PI;
     }
}

Every shape contains its area show with it’s own way of calculation functionality and our AreaCalculator class will become simple than before.

public class AreaCalculator
{
     public double TotalArea(Shape[] arrShapes)
     {
          double area=0;
          foreach(var objShape in arrShapes)
          {
             area += objShape.Area();
          }
          return area;
     }
}

Now our code is following SRP and OCP both. When ever you introduce new shape by deriving from the “Shape” abstract class, you need not to change the “AreaCalculator” class. Awesome. Isn’t it?.
L- Liskov substitution Principle

LiskovSubtitutionPrinciple
Liskov Substitution Principle (LSP) and states that “you should be able to use any derived class in place of a parent class and have it behave in the same manner without modification”. It ensures that a derived class does not affect the behavior of the parent class, i.e. that a derived class must be substitutable for its base class.

This principle is just an extension of the Open Close Principle and it means that we must make sure that new derived classes are extending the base classes without changing their behavior. I will explain this with a real world example which violates LSP.

A father is a doctor and where as his son wants to become a cricketer. So here son can’t replace his father even though they both belong to same family hierarchy.

Now jump into an example to learn how a design can violates LSP. Suppose we have to build an app to manage data using group of sql files text. Here our part is to write functionality to load and save the text of group of sql files in the application directory. So we need a class which manages load and save text of group of sql files along with SqlFile Class.

public class SqlFile
{
     public string FilePath {get;set;}
     public string FileText {get;set;}
     public string LoadText()
     {
        /* Code to read text from sql file */
     }
     public string SaveText()
     {
        /* Code to save text into sql file */
     }
}
public class SqlFileManager
{
     public List<SqlFile> lstSqlFiles {get;set}

     public string GetTextFromFiles()
     {
          StringBuilder objStrBuilder = new StringBuilder();
          foreach(var objFile in lstSqlFiles)
          {
             objStrBuilder.Append(objFile.LoadText());
          }
          return objStrBuilder.ToString();
     }
     public void SaveTextIntoFiles()
     {
         foreach(var objFile in lstSqlFiles)
         {
            objFile.SaveText();
         }
     }
}

OK. We are done with our part. Functionality looks good for now. After some time our lead might tell us that we may have few read only files in application folder, so we have to restrict the flow when ever it tries to perform save on them.

Hmm .. OK. We can do that by creating a “ReadOnlySqlFile” class which inherits “SqlFile” class and we have to alter the SaveTextIntoFiles() method by introducing a condition to prevent calling the SaveText() method on ReadOnlySqlFile instances.

public class SqlFile
{
      public string LoadText()
      {
         /* Code to read text from sql file */
      }
      public void SaveText()
      {
         /* Code to save text into sql file */
      }
}
public class ReadOnlySqlFile: SqlFile
{
      public string FilePath {get;set;}
      public string FileText {get;set;}
      public string LoadText()
      {
         /* Code to read text from sql file */
      }
      public void SaveText()
      {
         /* Throw an exception when app flow tries to perform save. */
         throw new IOException("Can't Save");
      }
}

To avoid exception we have to modify “SqlFileManager” by adding one condition in the loop.

public class SqlFileManager
{
     public List<SqlFile> lstSqlFiles {get;set}
     public string GetTextFromFiles()
     {
           StringBuilder objStrBuilder = new StringBuilder();
           foreach(var objFile in lstSqlFiles)
           {
               objStrBuilder.Append(objFile.LoadText());
           }
           return objStrBuilder.ToString();
      }
      public void SaveTextIntoFiles()
      {
           foreach(var objFile in lstSqlFiles)
           {
              //Check whether the current file object is read only or not.If yes, skip calling it's 
              // SaveText() method to skip the exception.

              if(! objFile is ReadOnlySqlFile)
                 objFile.SaveText();
           }
      }
}

Here we altered SaveTextIntoFiles() method in SqlFileManager class to check the instance is of ReadOnlySqlFile or not to avoid exception. We can’t use this ReadOnlySqlFile class as a substitute of it’s parent with out altering SqlFileManager code. So we can say that this design is not following LSP. Let’s make this design to follow the LSP. Here we will introduce interfaces to make the SqlFileManager class independent from the rest of the blocks.

public interface IReadableSqlFile
{
     string LoadText();
}
public interface IWritableSqlFile
{
     void SaveText();
}

Now we implement IReadableSqlFile through ReadOnlySqlFile class which reads only the text from read only files.

public class ReadOnlySqlFile: IReadableSqlFile
{
      public string FilePath {get;set;}
      public string FileText {get;set;}
      public string LoadText()
      {
         /* Code to read text from sql file */
      }
}

Here we implement both IWritableSqlFile,IReadableSqlFile in SqlFile class by which we can read and write files.

public class SqlFile: IWritableSqlFile,IReadableSqlFile
{
     public string FilePath {get;set;}
     public string FileText {get;set;}
     public string LoadText()
     {
        /* Code to read text from sql file */
     }
     public void SaveText()
     {
        /* Code to save text into sql file */
     }
}

Now the design of the SqlFileManager class becomes like this.

public class SqlFileManager
{
     public string GetTextFromFiles(List<IReadableSqlFile> aLstReadableFiles)
     {
           StringBuilder objStrBuilder = new StringBuilder();
           foreach(var objFile in aLstReadableFiles)
           {
               objStrBuilder.Append(objFile.LoadText());
           }
           return objStrBuilder.ToString();
     }
     public void SaveTextIntoFiles(List<IWritableSqlFile> aLstWritableFiles)
     {
           foreach(var objFile in aLstWritableFiles)
           {
              objFile.SaveText();
           }
     }
}

Here GetTextFromFiles() method gets only the list of instances of class which implements the IReadOnlySqlFile interface that means SqlFile and ReadOnlySqlFile classes instances. And  SaveTextIntoFiles() method gets only the list instances of class which implements the IWritableSqlFiles interface i.e SqlFile instances in this case. Now we can say our design is following LSP. And we fixed the problem using Interface segregation principle by (ISP) identifying the abstraction and the responsibility separation method.

Interface Segregation Principle (ISP)
The Interface Segregation Principle states “that clients should not be forced to implement interfaces they don’t use. Instead of one fat interface many small interfaces are preferred based on groups of methods, each one serving one sub module.

InterfaceSegregationPrinciple

We can define it in another way like, Interface should be closer related to the code that uses it than code that implement it. So the methods on the interface are defined by which methods client code needs than which methods class implements. So Clients should not be forced to depend upon interfaces that they don’t use.

Like classes each interface should have a specific purpose/responsibility (refer SRP). You shouldn’t be forced to implement an interface when your object doesn’t share that purpose. The larger the interface, the more likely it includes methods that not all implementers can achieve. That’s the essence of the Interface Segregation Principle. Lets start with an example which breaks ISP. Suppose We have to build a system for a IT firm which contains roles like TeamLead and Programmer where TeamLead divide a huge Task into smaller Tasks and assign them to his/her Programmers or can directly work on them.

Based on specifications, we need to create an Interface and a TeamLead class to implement it.

public Interface ILead
{
      void CreateSubTask();
      void AssginTask();
      void WorkOnTask();
}
public class TeamLead : ILead
{
     public void AssignTask()
     {
         //Code to assign a task.
     }
     public void CreateSubTask()
     {
        //Code to create a sub task
     }
     public void WorkOnTask()
     {
        //Code to implement perform assigned task.
     }
}

OK. The design looks fine for now. Later some time another role like Manager, who assigns tasks to TeamLead and will not work on the tasks, introduced in the system. Can we directly implement ILead interface in Manager class, like following?.

public class Manager: ILead
{
     public void AssignTask()
     {
        //Code to assign a task.
     }
     public void CreateSubTask()
     {
        //Code to create a sub task.
     }
     public void WorkOnTask()
     {
        throw new Exception("Manager can't work on Task");
     }
}

As the Manager can’t work on task and at the same time no one can assign tasks to Manager, this WorkOnTask() should not be there in Manager class. But we are implementing this class from ILead interface, we have to provide a concrete Method. Here we are forcing Manager class to implement WorkOnTask() method with out any purpose. This is wrong. The design violates ISP. Lets correct the design.

As we have three roles, 1. Manager – who can only divide and assign the tasks , 2. TeamLead -who can divide,assign the tasks and can work on them as well, 3. Programmer – who can only work on tasks, we need to divide the responsibilities by segregating the ILead interface. An interface which provides a contract for WorkOnTask()..

public interface IProgrammer
{
     void WorkOnTask();
}

An interface which provide contracts to manage the tasks..

public interface ILead
{
     void AssignTask();
     void CreateSubTask();
}

Then the implementation becomes…

public class Programmer: IProgrammer
{
     public void WorkOnTask()
     {
        //code to implement to work on the Task.
     }
}
public class Manager: ILead
{
     public void AssignTask()
     {
        //Code to assign a Task
     }
     public void CreateSubTask()
     {
        //Code to create a sub taks from a task.
     }
}

TeamLead can manage tasks and can work on them if needed. Then the TeamLead class should implement both IProgrammer and ILead interfaces.

public class TeamLead: IProgrammer, ILead
{
      public void AssignTask()
      {
          //Code to assign a Task
      }
      public void CreateSubTask()
      {
         //Code to create a sub task from a task.
      }
      public void WorkOnTask()
      {
         //code to implement to work on the Task.
      }
}

Wow. Here we separated responsibilities/purposes and distributed them on multiple interface, and provided good level of abstraction too.

D- Dependency Inversion Principle
The Dependency Inversion Principle (DIP) states that high level modules/classes should not depend upon low level modules/classes. Both should depend upon abstractions. Secondly, abstractions should not depend upon details. Details should depend upon abstractions.

DependencyInversionPrinciple

High level modules/Classes implements business rules or logic in a system( application). Low level modules/Classes deal with more detailed operations, i.e they may deal with writing information to databases or passing messages to the operating system or services.

A High level module/class has dependency on low level modules/classes or some other class and knows a lot about the other classes it interacts with is said to be tightly coupled. When a class knows explicitly about the design and implementation of another class, its raise the risk that changes to one class will break the other class. So we must keep these high and low level modules/class loosely coupled as much as we can. To achieve that, we have to make both of them to depend on abstractions instead of knowing each other. Lets start an with an example.

Suppose we have to work on Error logging module which logs exceptions stack trace into a file. Simple, isn’t it?

Following are the classes which provide functionality to log stack trace into a file.

public class FileLogger
{
     public void LogMessage(string aStackTrace)
     {
         //code to log stack trace into a file.
     }
}
public static class ExceptionLogger
{
     public static void LogIntoFile(Exception aException)
     {
         FileLogger objFileLogger = new FileLogger();
         objFileLogger.LogMessage(GetUserReadableMessage(aException));
     }
     private string GetUserReadableMessage(Exception ex)
     {
         string strMessage = string. Empty;
         //code to convert Exception’s stack trace and message to user readable format.
         ....
         ....
         return strMessage;
     }
}

A client class to exports data from bunch of files to database.

public class DataExporter
{
      public void ExportDataFromFile()
      {
           try {
               //code to export data from files to database.
           }
           catch(Exception ex)
           {
              new ExceptionLogger().LogIntoFile(ex);
           }
       }
}

Looks good. We sent our application to client. But our client wants to store this stack trace in database if any IO exception comes. hmm… OK, no problem. We can implement that too. Here we have to add one more class which provides functionality to log the stack trace into database and an extra method in ExceptionLogger to interact with our new class to log the stack trace.

public class DbLogger
{
     public void LogMessage(string aMessage)
     {
       //Code to write message in database.
     }
}
public class FileLogger
{
     public void LogMessage(string aStackTrace)
     {
       //code to log stack trace into a file.
     }
}
public class ExceptionLogger
{
    public void LogIntoFile(Exception aException)
    {
         FileLogger objFileLogger = new FileLogger();
         objFileLogger.LogMessage(GetUserReadableMessage(aException));
    }
    public void LogIntoDataBase(Exception aException)
    {
         DbLogger objDbLogger = new DbLogger();
         objDbLogger.LogMessage(GetUserReadableMessage(aException));
    }
    private string GetUserReadableMessage(Exception ex)
    {
        string strMessage = string.Empty;
       //code to convert Exception’s stack trace and message  to user readable format.
       ....
       ....
       return strMessage;
    }
}
public class DataExporter
{
     public void ExportDataFromFile()
     {
         try {
           //code to export data from files to database.
         }
         catch(IOException ex)
         {
            new ExceptionLogger().LogIntoDataBase(ex);
         }
         catch(Exception ex)
         {
            new ExceptionLogger().LogIntoFile(ex);
         }
     }
}

Looks fine for now. But when ever client wants to introduce new logger, we have to alter ExceptionLogger by adding new method. If we keep doing this after some time we will see a fat ExceptionLogger class with large set of methods which provide functionality to log message into various targets. Why this issue occurs?, because ExceptionLogger is directly contacting low level classes FileLogger and and DbLogger classes to log the exception. We have to alter the design so that this ExceptionLogger class can be loosely coupled with those class. To do that we need to introduce an abstraction between them, so that ExcetpionLogger can contact the abstraction to log the exception instead of depending on the low level classes directly.

public interface ILogger
{
     public void LogMessage(string aString);
}

Now our low level classes need to implement this interface.

 
public class DbLogger: ILogger
{
     public void LogMessage(string aMessage)
     {
          //Code to write message in database.
     }
}
public class FileLogger: ILogger
{
     public void LogMessage(string aStackTrace)
     {
         //code to log stack trace into a file.
     }
}

Now, we move the low level classes intitiation from ExcetpionLogger class to DataExporter class to make ExceptionLogger loosely coupled with the low level classes FileLogger and EventLogger classes. And by doing that we are giving provision to DataExporter class to decide what kind of Logger should be called based on the exception occurs.

public class ExceptionLogger
{
     private ILogger _logger;
     public ExceptionLogger(ILogger aLogger)
     {
          this._logger = aLogger;
     }
     public void LogException(Exception aException)
     {
          string strMessage = GetUserReadableMessage(aException);
          this._logger.LogMessage(strMessage);
     }
     private string GetUserReadableMessage(Exception aException)
     {
          string strMessage = string.Empty;
          //code to convert Exception’s stack trace and message  to user readable format.
          ....
          ....
          return strMessage;
     }
}
public class DataExporter
{
     public void ExportDataFromFile()
     {
         ExceptionLogger _exceptionLogger;
         try {
             //code to export data from files to database.
         }
         catch(IOException ex)
         {
            _exceptionLogger = new ExceptionLogger(new DbLogger());
            ExceptionLogger.LogException(ex);
         }
         catch(Exception ex)
         {
            _exceptionLogger = new ExceptionLogger(new FileLogger());
            ExceptionLogger.LogException(ex);
         }
      }
}

We successfully removed dependency on low level classes. This ExceptionLogger doesn’t depend on FileLogger and EventLogger classes to log the stack trace. We don’t have to change the ExceptionLogger’s code any more for any new logging functionality. We have to create a new logging class which implements the ILogger interface and need to add another catch block to DataExporter class’s ExportDataFromFile method.

public class EventLogger: ILogger
{
      public void LogMessage(string aMessage)
      {
          //Code to write message in system's event viewer.
      }
}

And we need to add a condition in DataExporter class..

public class DataExporter
{
     public void ExportDataFromFile()
     {
         ExceptionLogger _exceptionLogger;
         try {
           //code to export data from files to database.
         }
         catch(IOException ex)
         {
           _exceptionLogger = new ExceptionLogger(new DbLogger());
           ExceptionLogger.LogException(ex);
         }
         catch(SqlException ex) 
         {
            _exceptionLogger = new ExceptionLogger(new EventLogger());
            ExceptionLogger.LogException(ex);
         }
         catch(Exception ex)
         {
            _exceptionLogger = new ExceptionLogger(new FileLogger());
            ExceptionLogger.LogException(ex);
         }
     }
}

Looks good. But we introduced the dependency here in DataExporter class’s catch blocks. Yeah.. some one has to take the responsibility to provide necessary objects to the ExceptionLogger to make the work done.

Let me explain it with an real world example. Suppose we want a wooden chair with specific measurements, and kind of wood to be used to make that chair from. Then we can’t leave this decision making on measurements and the wood to the carpenter. Here his job is  making a  chair based on our requirements with his tools. We provide the specifications to him to make a good chair.

So what is the benefit we get by above design? Yes, we definitely have a benefit with it. We had to modify both DataExporter class and ExceptionLogger class when ever we had to introduce a new logging functionality. But in the updated design we have to add only another catch block for new exception logging feature. Coupling is not inherently evil. If you don’t have some amount of coupling, your software will not do anything for you. The only thing we have to do is understand the system, requirements and environment properly and find areas where DIP should be followed.

Great .., we have gone through the all five SOLID principles successfully. And we can conclude that using these principles we can build an application with tidy, readable and easily maintainable code.

Here you may get a doubt. Yes, about the quantity of code. Because of these principles, Code might get increased in our applications. But my dear friends, you have to compare it with the quality which we get by following these principles. Hmm … but anyway 27 lines are much less than 200 lines. I am not saying that these principles should be followed 100%, You have to draw a yellow so that you can hold the control over the things like quality and delivery to maintain their balance.

This is a little effort to share the uses of SOLID principles. I hope you enjoyed this article.

Images courtesy :http://Lostechies.com
Thank you,
Damu

2 thoughts on “SOLID principles C#

Leave a comment