.NET  

What Is SOLID Principles in Object Oriented Programming With Real Examples

Introduction

In modern software development, writing clean, maintainable, and scalable code is not optional—it is essential. As applications grow, code that is not well-structured becomes difficult to manage, debug, and extend. This leads to technical debt, slower development, and more bugs.

To solve this problem, developers follow a set of design principles known as SOLID principles in Object-Oriented Programming (OOP).

SOLID is a collection of five important design rules that help developers write better, reusable, and flexible code. These principles are widely used in real-world applications, especially in enterprise-level software, ASP.NET Core applications, and scalable systems.

In this detailed guide, you will learn each SOLID principle in simple words, with real-world examples and C# code examples that are easy to understand.

What Does SOLID Stand For?

SOLID is an acronym for:

  • S → Single Responsibility Principle (SRP)

  • O → Open/Closed Principle (OCP)

  • L → Liskov Substitution Principle (LSP)

  • I → Interface Segregation Principle (ISP)

  • D → Dependency Inversion Principle (DIP)

Let’s understand each principle step-by-step with detailed explanations.

Single Responsibility Principle (SRP)

What is SRP?

The Single Responsibility Principle states that:

A class should have only one reason to change.

Explanation

In simple words, a class should do only one job.

If a class is handling multiple responsibilities, it becomes difficult to maintain and update. Any change in one responsibility can break another.

Bad Example

public class User
{
    public void SaveUser() { }
    public void SendEmail() { }
}

Why This is Bad

  • The class is handling database logic and email logic

  • If email logic changes, you must modify this class

  • This increases risk of bugs

Good Example

public class User
{
    public void SaveUser() { }
}

public class EmailService
{
    public void SendEmail() { }
}

Why This is Better

  • Each class has one responsibility

  • Changes are isolated

  • Code becomes easier to test and maintain

Real-World Example

Think of a restaurant:

  • Chef cooks food

  • Waiter serves food

  • Cashier handles billing

Each person has one responsibility.

Open/Closed Principle (OCP)

What is OCP?

The Open/Closed Principle states that:

Software should be open for extension but closed for modification.

Explanation

You should be able to add new features without changing existing code.

This reduces the risk of breaking already working functionality.

Bad Example

public class Discount
{
    public double GetDiscount(string type)
    {
        if (type == "Regular") return 10;
        if (type == "Premium") return 20;
        return 0;
    }
}

Problem

  • Every new discount type requires modifying this class

  • This violates OCP

Good Example

public interface IDiscount
{
    double GetDiscount();
}

public class RegularDiscount : IDiscount
{
    public double GetDiscount() => 10;
}

public class PremiumDiscount : IDiscount
{
    public double GetDiscount() => 20;
}

Why This is Better

  • You can add new classes without modifying existing code

  • Code is more flexible and scalable

Real-World Example

Think of a mobile app:

  • New features are added without changing existing features

  • Updates do not break old functionality

Liskov Substitution Principle (LSP)

What is LSP?

LSP states that:

Derived (child) classes should be able to replace base (parent) classes without breaking the application.

Explanation

If a class is a child of another class, it should behave like the parent.

Bad Example

public class Bird
{
    public virtual void Fly() { }
}

public class Ostrich : Bird
{
    public override void Fly()
    {
        throw new Exception("Ostrich can't fly");
    }
}

Problem

  • Ostrich cannot fly, but Bird expects flying behavior

  • This breaks the system logic

Good Example

public class Bird { }

public class FlyingBird : Bird
{
    public void Fly() { }
}

public class Ostrich : Bird { }

Why This is Better

  • Behavior is properly separated

  • No unexpected errors

Real-World Example

  • A car and a bicycle are both vehicles

  • But they are used differently

Interface Segregation Principle (ISP)

What is ISP?

ISP states that:

Clients should not be forced to implement methods they do not use.

Explanation

Instead of one large interface, create smaller and specific interfaces.

Bad Example

public interface IWorker
{
    void Work();
    void Eat();
}

public class Robot : IWorker
{
    public void Work() { }
    public void Eat() { }
}

Problem

  • Robot does not need Eat()

  • This leads to unnecessary implementation

Good Example

public interface IWork
{
    void Work();
}

public interface IEat
{
    void Eat();
}

public class Human : IWork, IEat
{
    public void Work() { }
    public void Eat() { }
}

public class Robot : IWork
{
    public void Work() { }
}

Why This is Better

  • Classes implement only what they need

  • Code is cleaner and flexible

Real-World Example

  • A printer that only prints should not be forced to scan or fax

Dependency Inversion Principle (DIP)

What is DIP?

DIP states that:

High-level modules should not depend on low-level modules. Both should depend on abstractions.

Explanation

Depend on interfaces, not concrete classes.

Bad Example

public class MySQLDatabase
{
    public void Save() { }
}

public class UserService
{
    private MySQLDatabase db = new MySQLDatabase();
}

Problem

  • Tight coupling

  • Hard to change database later

Good Example

public interface IDatabase
{
    void Save();
}

public class MySQLDatabase : IDatabase
{
    public void Save() { }
}

public class UserService
{
    private readonly IDatabase _db;

    public UserService(IDatabase db)
    {
        _db = db;
    }
}

Why This is Better

  • Loose coupling

  • Easy to switch database (SQL Server, MongoDB, etc.)

  • Improves testability

Real-World Example

  • A charger works with different devices using a common interface

Benefits of SOLID Principles

  • Improves code readability

  • Makes code easier to maintain

  • Helps build scalable applications

  • Reduces bugs and technical debt

  • Encourages reusable components

Common Mistakes Beginners Make

  • Trying to apply all SOLID principles at once

  • Over-complicating small projects

  • Not understanding the problem before applying principles

When Should You Use SOLID Principles?

  • Large-scale applications

  • ASP.NET Core enterprise projects

  • Applications that require long-term maintenance

Summary

SOLID principles in Object-Oriented Programming help developers write clean, maintainable, and scalable code by following five key rules: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion. These principles reduce complexity, improve flexibility, and make applications easier to extend and maintain. By applying SOLID concepts in real-world projects such as ASP.NET Core applications and enterprise systems, developers can build robust, reusable, and high-quality software solutions.