codingstuff.io
ExploreTutorialsProblemsCS Subjects
Get Started
ExploreTutorialsProblemsCS Subjects
Get Started
codingstuff.io

Master the art of building software through interactive tutorials, real-world problems, and guided projects.

Pune, Maharashtra, India

codingstuffmail@gmail.com

Product

  • Explore
  • Tutorials
  • Problems
  • CS Subjects

Company

  • About
  • Contact
  • Privacy Policy
  • Terms & Conditions
  • Sitemap

© 2026 codingstuff.io. All rights reserved.

Built with ❤️ for developers everywhere

/
/
All Subjects
🧩

OOP Concepts

23 chapters

1Procedural vs Object-Oriented2Classes, Objects, & Instantiation3Constructors & Destructors4Static Members & Methods5Encapsulation & Access Modifiers6Data Abstraction7Inheritance Types (Single, Multiple)8Compile-Time Polymorphism (Overloading)9Polymorphism & Interfaces10Run-Time Polymorphism (Overriding)11Virtual Functions & V-Tables12Interfaces & Abstract Classes13Generic Programming (Templates & Generics)14Exception Handling in OOP15SOLID Design Principles16Composition over Inheritance17Coupling & Cohesion18UML Diagrams Basics19Creational Patterns (Singleton, Factory)20Structural Patterns (Adapter, Decorator)21Behavioral Patterns (Observer, Strategy)22MVC Architecture Pattern23Object Serialization & Cloning
SubjectsOOP Concepts

SOLID Design Principles

Updated 2026-04-25
2 min read

SOLID Design Principles

SOLID is an acronym for five design principles introduced by Robert C. Martin ("Uncle Bob") that guide developers in writing clean, maintainable, and robust object-oriented code. These principles are considered the gold standard in professional software engineering and are a very common topic in technical interviews.

1. S — Single Responsibility Principle (SRP)

"A class should have one, and only one, reason to change."

Every class should be responsible for a single piece of functionality. If a class handles user authentication AND email sending AND logging, it has three reasons to change. A bug fix in the email logic could accidentally break the authentication logic.

// BAD: One class doing everything
class UserService {
    void registerUser() { /* ... */ }
    void sendWelcomeEmail() { /* ... */ }
    void logRegistration() { /* ... */ }
}

// GOOD: Each class has a single responsibility
class UserRegistration { void register() { /* ... */ } }
class EmailService { void sendWelcome() { /* ... */ } }
class AuditLogger { void log() { /* ... */ } }

2. O — Open/Closed Principle (OCP)

"Software entities should be open for extension, but closed for modification."

You should be able to add new functionality to a system by writing new code, not by modifying existing, tested, and working code. This is typically achieved through Inheritance and Interfaces.

// GOOD: Adding a new shape does NOT require modifying existing code
interface Shape { double area(); }

class Circle implements Shape { /* ... */ }
class Rectangle implements Shape { /* ... */ }
class Triangle implements Shape { /* ... */ } // NEW! No existing code modified.

double totalArea(List<Shape> shapes) {
    return shapes.stream().mapToDouble(Shape::area).sum();
}

3. L — Liskov Substitution Principle (LSP)

"Objects of a superclass should be replaceable with objects of its subclasses without breaking the application."

If a function accepts a Bird object, it should work perfectly fine if you pass it a Sparrow (a subclass). The subclass must not weaken the guarantees of the parent class.

The classic violation is the Square-Rectangle problem. A Square IS-A Rectangle mathematically, but if Rectangle.setWidth() and Rectangle.setHeight() are independent operations, a Square overriding setWidth() to also set the height breaks the parent's contract.

4. I — Interface Segregation Principle (ISP)

"A client should never be forced to implement an interface that it doesn't use."

Fat interfaces with dozens of methods force implementing classes to provide stub implementations for methods they don't care about. Instead, break large interfaces into smaller, more specific ones.

// BAD: A fat interface
interface Worker {
    void work();
    void eat();
    void sleep();
}
// A Robot implements Worker but CANNOT eat or sleep!

// GOOD: Segregated interfaces
interface Workable { void work(); }
interface Eatable { void eat(); }
interface Sleepable { void sleep(); }

class HumanWorker implements Workable, Eatable, Sleepable { /* ... */ }
class RobotWorker implements Workable { /* Only implements what it needs */ }

5. D — Dependency Inversion Principle (DIP)

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

Instead of a PaymentService class directly creating and using a StripeGateway object (tight coupling), both should depend on a PaymentGateway interface. This allows you to easily swap Stripe for PayPal without touching the PaymentService code.

// High-level module depends on an ABSTRACTION, not a concrete class
class PaymentService {
    private PaymentGateway gateway; // Interface, not Stripe!

    PaymentService(PaymentGateway gateway) {
        this.gateway = gateway; // Injected via constructor
    }

    void processPayment(double amount) {
        gateway.charge(amount);
    }
}


PreviousException Handling in OOPNextComposition over Inheritance

Recommended Gear

Exception Handling in OOPComposition over Inheritance