SOLID Principles of C# : Examples

17 februari 2021 om 10:00 by ParTech Media - Post a comment

In a real world, for whatever activity users start to implement, there will be a plan and design that will be created. And most importantly, certain standards will be followed while implementing it. Say, for example, to construct a house, a plan and design will be created. Based on that, the construction will be carried out by following some standards (like the mixture of sand and cement for creating concrete, location of electrical switches, etc..,).

Similarly, in the programming world, there are a set of basic design principles that one has to follow to achieve a code that can be easily understood, easily enhanced in the future, maintainable, and has a lot many factors. TThis set of basic principles in C# are called SOLID principles which we saw in detail in our last post. In this post, we are going to take you one step ahead by explaining the SOLID principles with the help of examples.

Table of contents

  1. SOLID Principles - A Quick Recap
  2. Example of Single Responsibility Principle
  3. Example of Open closed principle
  4. Example of Liskov Substitution Principle
  5. Example of Interface segregation principle
  6. Example of Dependency Inversion Principle
  7. Conclusion

SOLID Principles - A Quick Recap

In object-oriented programming, SOLID (acronym) is a basic design principle used to create more understandable and flexible software. SOLID stands for

  • ​ Single Responsibility Principle,
  • ​ Open Closed Principle,
  • ​ Liskov Substitution Principle,
  • ​ Interface Segregation Principle, and
  • ​ Dependency Inversion Principle

Let's see the example of each of them in the upcoming sections.

Example of Single Responsibility Principle

Imagine a Swiss knife - it has several utilities attached to it. But, when you want to add/remove a feature, then you have to arrange it completely from the base, which makes it hard. Similarly, in the programming world, a class should be written to take care of ‘one thing’.

Say, a class is written to handle the operations of spreadsheet handling, where it can hold operations to create, update, open, and delete spreadsheets. In this way, if any operations need to be updated related to the operation of the spreadsheet, then only this file has to be modified.

//Class to handle operations related to spreadsheet

public class SpreadsheetOperations

{

​    	//Method to create spreadsheet

​    	public void CreateSpreadsheet()

​    	{

​    	}

 

​    	//Method to open spreadsheet

​    	public void OpenSpreadsheet()

​    	{    

​    	}

}


Example of Open closed principle

Example - Consider a code that returns the sound of the animal, and it already has the code to send back the sound of an animal.

public class Animal

{

​    	public void Sound()

​    	{

​          	if (animalName == “dog”)

​          	{

​                	Console.WriteLine(“barks”);

​          	}

​    	}

}

But, doing this way would make it hard if more animals are introduced, as there would be a need to modify the existing code to include the new functionality.

public interface Animal

{

​    	public void Sound();

}

 

public class Dog: Animal

{

​    	public void Sound()

​    	{

​          	Console.WriteLine(“bark”);

​    	}

}

Implementing the functionality in the above way would make it easier to extend without having to change the existing code.

Example of Liskov Substitution Principle

Let us consider the above example again. Imagine we are creating multiple classes inheriting the Animal class which also has an additional property of lifespan. And we are creating a class Dinosaur from Animal and we are not sure what the lifespan of it is.

public class Dinosaur: Animal

{

​    	public void Lifespan()

​    	{

​          	return new Exception();

​    	}

} 

Now, when we access the dinosaur call method using the base class object, it would throw an error.

List<Animal> animals = new List<Animal>();

animals.Add(dog);

animals.Add(cat);

animals.Add(dinosaur);

foreach (var animal in animals)

{

animal.Lifespan();

}

The above code would error out while trying to access the method of the dinosaur.

Example of Interface segregation principle

Example - Consider an interface Car, and it has methods like GetColor, GetLength, GetConvertibleColor(). Here, Color and length will be there for all the cars. Whereas the convertible color will not be there for all the cars. So, non-convertible cars are forced to implement the method in their class.

Instead, the interface Car should hold the GetColor and GetLength method declaration and a separate interface should be there for Style

public interface ICar 

{

​    	public void GetColor();

​    	public void GetLength();

}

 

public interface IStyle

{

​    	public void GetConvertibleColor();

}

 

public class Audi : ICar, IStyle 

{

​    	//Holds the implementation of all the methods that are present in both the interface.

}

Example of Dependency Inversion Principle

Imagine a class wants to log some information that is being processed in the methods of the class. And the application has two classes for logging, one for DB logging and another for file logging. So, the interface that is being inherited by DbLogger and FileLogger can be used by the class that is used for logging the information. And based on the need, users can do dependency injection and inject the required logger. In this way, the implementing class does not know what logger is being used.

public interface ILogger

{

public void LogInformation(string message);

}

public class DbLogger : ILogger

{

public void LogInformation(string message)

{

//Logic to implement DB logging.

}

}

public class DbLogger : ILogger

{

​    	public void LogInformation(string message)

​    	{

​          	//Logic to implement DB logging.

​    	}

}

public class OperationClass

{

​    	public OperationClass(ILogger logger)

{

}

} 

void DependencyInjection()

{

  var customer = new OperationClass(new DbLogger());

}

Conclusion

Implementing SOLID principles in the code makes it easier to understand, maintain and extend the code without making more changes at the core of the application. So start using it in your code to make your life as a developer simpler and better.