Appearance
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"); // MongoDBSOLID Quick Reference
| Principle | Rule | Keyword |
|---|---|---|
| S — Single Responsibility | One class, one job | Separation |
| O — Open/Closed | Extend don't modify | Extension |
| L — Liskov Substitution | Derived substitutes base | Substitution |
| I — Interface Segregation | Small focused interfaces | Segregation |
| D — Dependency Inversion | Depend on abstractions | Injection |
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."