SOLID principles are the design principles which help us in managing with most of the software design problems and to create a good software architecture. The SOLID principles are basically 5 principles.

SOLID stands for:

S stands for Single Responsibility principle(SRP)

O stands for Open Closed principle(OCP)

L stands for Liskov substitution principle(LSP)

I stands for Interface Segregation principle(ISP)

D stands for Dependency Injection principle(DIP)

1.Single Responsibility principle:

As per Single responsibility principle a class should take only one responsibility and there should be only one reason to change that class.

Consider below example where a class has two methods one of calculating bill and other generating report.

public class GenerateReport
{
   public void calculate()
   {
     //calculates amount which need to be printed
   }
   public void PrintReport()
   {
     //Report printing method
   }
}

Here above GenerateReport class is taking two responsibilities, one is to calculate and other is to print which is vioalating Single responsibility principle

So according to Single Responsibility principle we have to write a separate method for calculating as given below

public class Calculate
{
   public void calculate()
   {

   }
}

2. Open closed principle(OCP)

According to Open closed principle a software class should be open for extension and closed for modifications

Lets assume in above example of calculate, there is a tax to be included depending upon request hence the class be as follows:

public class Calculate
{
   public void calculate(bool includeTax)
   {
     if(includeTax)
     {
       //calculate with tax
     }else
     {
       //calculate without tax
     }
   }
}

The above example violates open closed principle where it is modifying the existing class methods. Hence it should be defined as below

public class ICalculate
{
	public virtual void Calculate()
	{
	
	}
}
public class CalculateWithoutTax:ICalculate
{
	public override void CalculateWithoutTaxMethod()
	{
	}
}

public class CalculateWithTax:ICalculate
{
	public override void CalculateWithTaxMethod()
	{
	}
}

3. Liskov substitution Principle:

This principle states that we should be able to use any derived class instead of parent class and it should behave in the same manner without modification i.e., it must ensure that derived class does not affect the behaviour of parent class.

Consider below example where a class has two virtual methods which need to be overridden

public abstract class student
    {
        public virtual string GetStudentAttandance(int iStudentId)
        {
            return "base attendance";
        }
        public virtual string GetStudentGrades(int iStudentId)
        {
            return "base grades";
        }
    }

    public class PrimaryStudents : student
    {
        public virtual string GetStudentAttandance(int iStudentId)
        {
            return "primary attendance";
        }
        public virtual string GetStudentGrades(int iStudentId)
        {
            //There may be no grades for primary class students
            throw new NotImplementedException();
        }
    }

    public class SecondaryStudents : student
    {
        public virtual string GetStudentAttandance(int iStudentId)
        {
            return "secondary attendance";
        }
        public virtual string GetStudentGrades(int iStudentId)
        {
            //Grades are present for secondary class students
            return "secondary grade attendance";
        }
    }

Now when we are creating list of students as below then primary students will return exception as they don’t have grade which results in violating of Liskov substitution principle

List<student> lstStudents=new List<students>();
lstStudents.Add(new SecondaryStudents());
lstStudents.Add(new PrimaryStudents());

foreach(student e in lstStudents)
{
   e.GetStudentGrades(101);
}

Since this class is violating this principle it should be breakdown into two interfaces

public interface IAttendance{
   string GetStudentAttandance(int iStudentId);
}


public interface IGrades{
   string GetStudentGrades(int iStudentId);
}

4. Interface segregation principle(ISP)

This Interface segregation principle is simple and states that no client should be forced to implement interfaces which they don’t use i.e., if there is a possibility of any not using interface then such abstract methods should be kept in different interface.

Consider below example:

public interface IStudent
{
   bool AddStudent();
   bool ShowStudent(int iStudId);
}

It is not necessary that every client will implement both methods

Hence it should be broken down as

public interface IAddStudent
{
   bool AddStudent();
}

public interface IShowStudent
{
   bool ShowStudent(int iStudId);
}

Dependency inversion principle

This principle states that code should be less dependent and it should be loosely coupled. There are basically 3 types of Dependency injections namely Constructor Injection, Property Injection and Method injection.