1. Single Responsibility Principle (SRP)
Definition: A class should have only one reason to change (one responsibility).
Bad Example:
Copyclass Report {
public:
void generateReport() { /* logic to generate */ }
void saveToFile() { /* logic to save to file */ }
void printReport() { /* logic to print */ }
};
- This class handles too many responsibilities (generate, save, print).
Good Example (SRP):
Copyclass Report {
public:
void generateReport() { /* logic */ }
};
class FileManager {
public:
void saveToFile(const Report& report) { /* logic */ }
};
class Printer {
public:
void printReport(const Report& report) { /* logic */ }
};
- Each class has only one reason to change.
2. Open/Closed Principle (OCP)
Definition: A class should be open for extension, but closed for modification.
Bad Example:
Copyclass Shape {
public:
string type;
};
class AreaCalculator {
public:
double calculate(Shape* shape) {
if (shape->type == "circle") {
return 3.14 * 5 * 5;
} else if (shape->type == "rectangle") {
return 10 * 5;
}
return 0;
}
};
- Every time we add a new shape, we must modify
AreaCalculator.
Good Example (OCP):
Copyclass Shape {
public:
virtual double area() const = 0;
virtual ~Shape() = default;
};
class Circle : public Shape {
double r;
public:
Circle(double r) : r(r) {}
double area() const override { return 3.14 * r * r; }
};
class Rectangle : public Shape {
double w, h;
public:
Rectangle(double w, double h) : w(w), h(h) {}
double area() const override { return w * h; }
};
- Now adding a new shape doesn’t require modifying existing code — just extend by creating a new class.
3. Liskov Substitution Principle (LSP)
Definition: Subclasses should be usable in place of base classes without breaking functionality.
Bad Example:
Copyclass Bird {
public:
virtual void fly() { cout << "Bird flying"; }
};
class Ostrich : public Bird {
public:
void fly() override { throw runtime_error("Ostrich can't fly"); }
};
Ostrichviolates LSP because a client expectingBirdcannot safely callfly().
✅ Good Example (LSP):
Copyclass Bird { };
class FlyingBird : public Bird {
public:
virtual void fly() = 0;
};
class Sparrow : public FlyingBird {
public:
void fly() override { cout << "Sparrow flying"; }
};
class Ostrich : public Bird {
// Ostrich is a Bird but not a FlyingBird
};
- Correct hierarchy: non-flying birds don’t override
fly.
4. Interface Segregation Principle (ISP)
Definition: Clients should not be forced to implement methods they don’t use.
Bad Example:
Copyclass IMachine {
public:
virtual void print() = 0;
virtual void scan() = 0;
virtual void fax() = 0;
};
class BasicPrinter : public IMachine {
public:
void print() override { cout << "Printing"; }
void scan() override { /* Not supported */ }
void fax() override { /* Not supported */ }
};
BasicPrinteris forced to implement methods it doesn’t need.
✅ Good Example (ISP):
Copyclass IPrinter {
public:
virtual void print() = 0;
};
class IScanner {
public:
virtual void scan() = 0;
};
class BasicPrinter : public IPrinter {
public:
void print() override { cout << "Printing"; }
};
- Separate small interfaces → clients implement only what they need.
5. Dependency Inversion Principle (DIP)
Definition: High-level modules should depend on abstractions, not concrete classes.
Bad Example:
Copyclass LightBulb {
public:
void turnOn() { cout << "Bulb on"; }
void turnOff() { cout << "Bulb off"; }
};
class Switch {
LightBulb bulb; // depends on concrete class
public:
void operate(bool on) {
if (on) bulb.turnOn(); else bulb.turnOff();
}
};
Switchis tightly coupled toLightBulb.
✅ Good Example (DIP):
Copyclass ISwitchable {
public:
virtual void turnOn() = 0;
virtual void turnOff() = 0;
virtual ~ISwitchable() = default;
};
class LightBulb : public ISwitchable {
public:
void turnOn() override { cout << "Bulb on"; }
void turnOff() override { cout << "Bulb off"; }
};
class Switch {
ISwitchable* device;
public:
Switch(ISwitchable* d) : device(d) {}
void operate(bool on) {
if (on) device->turnOn(); else device->turnOff();
}
};
Switchdepends only on abstraction, not implementation.