SOLID Principles Explained With Examples

SOLID Principles Explained With Examples

Modern software development is not only about writing code that works — it is about writing code that is maintainable, scalable, reusable, and easy to understand. As applications grow larger, poorly structured code quickly becomes difficult to manage. This is where the SOLID principles become extremely important.

SOLID is a collection of five object-oriented design principles introduced by Robert C. Martin, widely known as “Uncle Bob.” These principles help developers create software systems that are easier to maintain, extend, and test.

The acronym SOLID stands for:

  1. S — Single Responsibility Principle (SRP)
  2. O — Open/Closed Principle (OCP)
  3. L — Liskov Substitution Principle (LSP)
  4. I — Interface Segregation Principle (ISP)
  5. D — Dependency Inversion Principle (DIP)

In this blog, we will explain each principle in detail with simple examples using object-oriented programming concepts.

Why SOLID Principles Matter

Before diving into each principle, it is important to understand why SOLID matters.

Without proper design principles, software often suffers from:

  • Tight coupling
  • Repeated code
  • Difficult debugging
  • Hard-to-test modules
  • Poor scalability
  • Fragile architecture

SOLID principles help solve these issues by encouraging:

  • Clean architecture
  • Flexible design
  • Reusable components
  • Easier testing
  • Better collaboration among developers

These principles are heavily used in frameworks like Spring Framework, ASP.NET Core, and Laravel.

1. Single Responsibility Principle (SRP)

Definition

A class should have only one reason to change.

This means a class should handle only one responsibility or functionality.

Bad Example

class Report {
    public void generateReport() {
        System.out.println("Generating report...");
    }

    public void saveToFile() {
        System.out.println("Saving report to file...");
    }

    public void sendEmail() {
        System.out.println("Sending email...");
    }
}

Problem

The Report class is handling:

  • Report generation
  • File storage
  • Email sending

If email logic changes, the class changes.
If file storage changes, the same class changes again.

This violates SRP.

Good Example

class ReportGenerator {
    public void generateReport() {
        System.out.println("Generating report...");
    }
}

class ReportSaver {
    public void saveToFile() {
        System.out.println("Saving report...");
    }
}

class EmailSender {
    public void sendEmail() {
        System.out.println("Sending email...");
    }
}

Benefits

  • Easier maintenance
  • Better readability
  • Independent testing
  • Reduced complexity

2. Open/Closed Principle (OCP)

Definition

Software entities should be:

  • Open for extension
  • Closed for modification

This means you should add new functionality without changing existing code.

Bad Example

class PaymentProcessor {
    public void processPayment(String type) {
        if(type.equals("CreditCard")) {
            System.out.println("Processing credit card payment");
        }
        else if(type.equals("PayPal")) {
            System.out.println("Processing PayPal payment");
        }
    }
}

Problem

Whenever a new payment method is added, you must modify existing code.

This increases risk and breaks stability.

Good Example

interface PaymentMethod {
    void pay();
}

class CreditCardPayment implements PaymentMethod {
    public void pay() {
        System.out.println("Credit card payment");
    }
}

class PayPalPayment implements PaymentMethod {
    public void pay() {
        System.out.println("PayPal payment");
    }
}

class PaymentProcessor {
    public void processPayment(PaymentMethod method) {
        method.pay();
    }
}

Now you can add new payment methods without changing PaymentProcessor.

Example:

class UpiPayment implements PaymentMethod {
    public void pay() {
        System.out.println("UPI Payment");
    }
}

Benefits

  • Safer code changes
  • Better scalability
  • Easier feature additions

3. Liskov Substitution Principle (LSP)

Definition

Objects of a subclass should be replaceable with objects of the parent class without breaking the application.

In simple words:

A child class should behave like its parent class.

Bad Example

class Bird {
    public void fly() {
        System.out.println("Bird can fly");
    }
}

class Ostrich extends Bird {
    public void fly() {
        throw new UnsupportedOperationException();
    }
}

Problem

An ostrich cannot fly, but it inherits fly().

This breaks LSP because replacing Bird with Ostrich causes unexpected behavior.

Good Example

class Bird {
}

class FlyingBird extends Bird {
    public void fly() {
        System.out.println("Flying...");
    }
}

class Sparrow extends FlyingBird {
}

class Ostrich extends Bird {
}

Now only flying birds have flying behavior.

Benefits

  • Better inheritance structure
  • Fewer runtime errors
  • More predictable behavior

4. Interface Segregation Principle (ISP)

Definition

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

Instead of large interfaces, create smaller and focused interfaces.

Bad Example

interface Worker {
    void work();
    void eat();
}

class Robot implements Worker {
    public void work() {
        System.out.println("Robot working");
    }

    public void eat() {
        // Robots don't eat
    }
}

Problem

Robot is forced to implement eat() even though it does not need it.

Good Example

interface Workable {
    void work();
}

interface Eatable {
    void eat();
}

class Human implements Workable, Eatable {
    public void work() {
        System.out.println("Human working");
    }

    public void eat() {
        System.out.println("Human eating");
    }
}

class Robot implements Workable {
    public void work() {
        System.out.println("Robot working");
    }
}

Benefits

  • Cleaner interfaces
  • Less unnecessary code
  • Improved flexibility

5. Dependency Inversion Principle (DIP)

Definition

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

In simple words:

Depend on interfaces, not concrete classes.

Bad Example

class Keyboard {
    public void type() {
        System.out.println("Typing...");
    }
}

class Computer {
    private Keyboard keyboard = new Keyboard();

    public void start() {
        keyboard.type();
    }
}

Problem

Computer is tightly coupled to Keyboard.

Changing the keyboard implementation requires modifying Computer.

Good Example

interface InputDevice {
    void input();
}

class Keyboard implements InputDevice {
    public void input() {
        System.out.println("Typing...");
    }
}

class Mouse implements InputDevice {
    public void input() {
        System.out.println("Clicking...");
    }
}

class Computer {
    private InputDevice device;

    public Computer(InputDevice device) {
        this.device = device;
    }

    public void start() {
        device.input();
    }
}

Now Computer works with any input device.

Benefits

  • Loose coupling
  • Better testability
  • Easier dependency injection
  • More flexible architecture

Real-World Example of SOLID Principles

Consider an e-commerce application.

Without SOLID:

  • One giant class handles orders, payments, emails, and reports.
  • Adding new payment methods becomes difficult.
  • Testing individual modules becomes almost impossible.

With SOLID:

  • Separate services handle separate responsibilities.
  • Payment methods become extensible.
  • Interfaces isolate features.
  • Dependencies become loosely coupled.

This results in:

  • Faster development
  • Easier debugging
  • Cleaner architecture
  • Better scalability

SOLID Principles in Popular Frameworks

Many modern frameworks internally follow SOLID principles.

Java

  • Spring Boot uses dependency injection heavily.
  • Interfaces and abstractions are core concepts.

C

  • ASP.NET promotes dependency injection and interface-based architecture.

PHP

  • Laravel uses service containers and inversion of control.

Swift

  • SwiftUI encourages modular and reusable design patterns.

Common Mistakes Developers Make

Even experienced developers sometimes misuse SOLID principles.

Overengineering

Some developers create too many abstractions for small applications.

SOLID should simplify code, not make it unnecessarily complicated.

Misusing Inheritance

Inheritance should represent true “is-a” relationships.

Example:

  • Car is a Vehicle ✅
  • Car is an Engine ❌

Ignoring Interfaces

Hardcoding dependencies makes testing difficult.

Interfaces improve flexibility and mock testing.

Tips to Apply SOLID Principles

Start Small

You do not need to redesign your entire application immediately.

Apply SOLID gradually.

Use Composition Over Inheritance

Prefer combining objects rather than deeply nested inheritance trees.

Write Unit Tests

SOLID works best with testing practices.

Frameworks like:

  • JUnit
  • XCTest
  • Mockito

help validate modular code design.

Advantages of SOLID Principles

| Principle | Main Benefit |
| | — |
| SRP | Easier maintenance |
| OCP | Easy extension |
| LSP | Reliable inheritance |
| ISP | Smaller interfaces |
| DIP | Loose coupling |

Overall advantages:

  • Better architecture
  • Cleaner code
  • Improved readability
  • Easier testing
  • Faster debugging
  • Scalable systems

When Should You Use SOLID?

SOLID principles are especially useful for:

  • Enterprise applications
  • Backend systems
  • APIs
  • SaaS products
  • Mobile apps
  • Large collaborative projects

For very small scripts or prototypes, applying every principle strictly may not be necessary.

Conclusion

SOLID principles are foundational concepts in object-oriented software development. They help developers create applications that are flexible, maintainable, scalable, and easier to understand.

To summarize:

  • SRP keeps classes focused
  • OCP allows safe extension
  • LSP ensures proper inheritance
  • ISP avoids bloated interfaces
  • DIP reduces tight coupling

Mastering SOLID principles can significantly improve code quality and software architecture. Whether you are building APIs, mobile applications, SaaS platforms, or enterprise systems, these principles help you write professional and future-proof code.

Instead of only writing code that works today, SOLID encourages writing code that remains manageable tomorrow.