Skip to content

SOLID Principles

5 design principles that make code maintainable, scalable, and clean.

  • S — Single Responsibility
  • O — Open/Closed
  • L — Liskov Substitution
  • I — Interface Segregation
  • D — Dependency Inversion

S — Single Responsibility Principle

"A class should have only ONE reason to change" — One class = One job

cpp
// WRONG — one class doing too many jobs
class Employee {
public:
    void getDetails() { /* ... */ }       // job 1 — employee data
    void saveToDatabase() { /* ... */ }    // job 2 — database logic  WRONG
    void generateReport() { /* ... */ }    // job 3 — report generation  WRONG
};

// CORRECT — each class has ONE job
class Employee { void getDetails() { /* ... */ } };
class EmployeeDB { void save(Employee& e) { /* ... */ } };
class EmployeeReport { void generate(Employee& e) { /* ... */ } };

O — Open/Closed Principle

"Open for extension, closed for modification" — Add new features by adding new code, not changing existing code

cpp
// WRONG — adding new type requires modifying existing class
class Discount {
    double calculate(string type, double price) {
        if (type == "student") return price * 0.8;
        if (type == "senior") return price * 0.7;
        // adding employee requires MODIFYING this class
    }
};

// CORRECT — extend by adding new classes
class Discount { public: virtual double calculate(double price) = 0; };
class StudentDiscount : public Discount { double calculate(double p) override { return p * 0.8; } };
class SeniorDiscount : public Discount { double calculate(double p) override { return p * 0.7; } };
class EmployeeDiscount : public Discount { double calculate(double p) override { return p * 0.6; } };
// New type? Just add a new class — no existing code touched!

L — Liskov Substitution Principle

"Derived class must be substitutable for its base class" — Wherever base class is used, derived class should work correctly

cpp
// WRONG — breaks LSP
class Penguin : public Bird {
    void fly() override { throw "Penguins can't fly!"; } // BREAKS!
};

// CORRECT — proper hierarchy
class Bird { virtual void eat() { /* ... */ } }; // all birds eat
class FlyingBird : public Bird { virtual void fly() { /* ... */ } };
class Penguin : public Bird { void eat() override { /* ... */ } }; // no fly()
class Eagle : public FlyingBird { void fly() override { /* ... */ } };

I — Interface Segregation Principle

"A class should not be forced to implement interfaces it doesn't use" — Many small interfaces > one large interface

cpp
// WRONG — Robot forced to implement eat() and sleep()
class IWorker { virtual void work()=0; virtual void eat()=0; virtual void sleep()=0; };
class Robot : public IWorker {
    void work() override { /* ... */ }
    void eat() override { /* robots don't eat — forced! */ }   // WRONG
    void sleep() override { /* robots don't sleep — forced! */ } // WRONG
};

// CORRECT — split into small focused interfaces
class IWorkable { virtual void work() = 0; };
class IEatable { virtual void eat() = 0; };
class ISleepable { virtual void sleep() = 0; };

class Robot : public IWorkable { void work() override { /* ... */ } }; // only what it needs
class Human : public IWorkable, public IEatable, public ISleepable { /* ... */ };

D — Dependency Inversion Principle

"Depend on abstractions, not on concrete classes" — High level modules should not depend on low level modules

cpp
// WRONG — tightly coupled to MySQL
class UserService {
    MySQLDatabase db; // directly depends on MySQL
public:
    void saveUser(string name) { db.save(name); }
    // switching to MongoDB requires changing UserService!
};

// CORRECT — depend on abstraction
class IDatabase { public: virtual void save(string data) = 0; };
class MySQLDatabase : public IDatabase { void save(string d) override { /* ... */ } };
class MongoDB : public IDatabase { void save(string d) override { /* ... */ } };

class UserService {
    IDatabase* db; // depends on interface
public:
    UserService(IDatabase* d) : db(d) {}
    void saveUser(string name) { db->save(name); } // works with any DB!
};

MySQLDatabase mysql;
UserService s1(&mysql); s1.saveUser("Alice"); // MySQL

MongoDB mongo;
UserService s2(&mongo); s2.saveUser("Bob"); // MongoDB

SOLID Quick Reference

PrincipleRuleKeyword
S — Single ResponsibilityOne class, one jobSeparation
O — Open/ClosedExtend don't modifyExtension
L — Liskov SubstitutionDerived substitutes baseSubstitution
I — Interface SegregationSmall focused interfacesSegregation
D — Dependency InversionDepend on abstractionsInjection

Real World Analogy

  • S — A chef only cooks, doesn't manage accounts
  • O — Add new menu items without changing kitchen rules
  • L — Any chef can follow the same recipe book
  • I — Chef doesn't need accounting skills
  • D — Restaurant depends on "chef" role, not specific person

One-liner: "SOLID principles guide writing clean OOP code — single responsibility keeps classes focused, open/closed allows extension without modification, Liskov ensures substitutability, interface segregation avoids fat interfaces, and dependency inversion promotes loose coupling via abstractions."