SOLID Principles — A Practical guide in C#

Vineet Sharma
3 min readJan 22, 2024

SOLID Principles — best practices using C#

The SOLID principles are a set of five design principles for writing maintainable and scalable software. They were introduced by Robert C. Martin and are widely used in object-oriented programming. The SOLID acronym stands for:

  1. Single Responsibility Principle (SRP):
  • A class should have only one reason to change, meaning that a class should only have one responsibility.
  • This principle encourages the division of responsibilities in a program so that if a change occurs, it only affects one part of the code.

2. Open/Closed Principle (OCP):

  • Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.
  • This principle promotes the use of abstraction and polymorphism to allow adding new functionality without altering existing code.

3. Liskov Substitution Principle (LSP):

  • Objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program.
  • This principle ensures that a derived class can be substituted for its base class without changing the correctness of the program.

4. Interface Segregation Principle (ISP):

  • A client should not be forced to depend on interfaces it does not use.
  • This principle encourages the creation of small, specific interfaces instead of large, general-purpose ones, to prevent classes from being forced to implement methods they don’t need.

5. Dependency Inversion Principle (DIP):

  • High-level modules should not depend on low-level modules. Both should depend on abstractions.
  • Abstractions should not depend on details. Details should depend on abstractions.
  • This principle encourages the use of dependency injection and inversion of control to achieve a flexible and maintainable architecture.

Now, let’s provide a practical guide in C# for each of these SOLID principles:

Single Responsibility Principle (SRP):

// Bad Example
class Employee
{
public void CalculateSalary() { /* ... */ }
public void GenerateReport() { /* ... */ }
}

// Good Example
class Employee
{
public void CalculateSalary() { /* ... */ }
}

class ReportGenerator
{
public void GenerateReport() { /* ... */ }
}

Open/Closed Principle (OCP):

:// Bad Example
class Shape
{
public virtual double Area() { /* ... */ }
}

class Circle : Shape
{
public override double Area() { /* ... */ }
}

// Good Example
interface IShape
{
double Area();
}

class Circle : IShape
{
public double Area() { /* ... */ }
}

class Square : IShape
{
public double Area() { /* ... */ }
}

Liskov Substitution Principle (LSP):

// Bad Example
class Bird
{
public virtual void Fly() { /* ... */ }
}

class Penguin : Bird
{
public override void Fly() { /* Not applicable */ }
}

// Good Example
interface IFlyable
{
void Fly();
}

class Bird : IFlyable
{
public void Fly() { /* ... */ }
}

class Penguin : IFlyable
{
public void Fly() { /* Not applicable */ }
}

Interface Segregation Principle (ISP):

// Bad Example
interface IWorker
{
void Work();
void TakeBreak();
}

class Manager : IWorker
{
public void Work() { /* ... */ }
public void TakeBreak() { /* ... */ }
}

// Good Example
interface IWorker
{
void Work();
}

interface IBreakable
{
void TakeBreak();
}

class Manager : IWorker, IBreakable
{
public void Work() { /* ... */ }
public void TakeBreak() { /* ... */ }
}

Dependency Inversion Principle (DIP):

// Bad Example
class DataAccessLayer
{
public string GetData() { /* ... */ }
}

class BusinessLogicLayer
{
private readonly DataAccessLayer _dataAccessLayer;

public BusinessLogicLayer()
{
_dataAccessLayer = new DataAccessLayer();
}

public string ProcessData()
{
var data = _dataAccessLayer.GetData();
// Process data
return data;
}
}

// Good Example
interface IDataAccessLayer
{
string GetData();
}

class DataAccessLayer : IDataAccessLayer
{
public string GetData() { /* ... */ }
}

class BusinessLogicLayer
{
private readonly IDataAccessLayer _dataAccessLayer;

public BusinessLogicLayer(IDataAccessLayer dataAccessLayer)
{
_dataAccessLayer = dataAccessLayer;
}

public string ProcessData()
{
var data = _dataAccessLayer.GetData();
// Process data
return data;
}
}

These examples illustrate how to apply each SOLID principle in C# to create more maintainable, flexible, and scalable code. Keep in mind that these are simplified examples, and real-world scenarios may require more complex implementations.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

No responses yet

Write a response