If you’ve ever worked on a project that turned into a “monster” that’s hard to maintain, you probably felt the lack of good code design practices. That’s exactly where SOLID comes in.
SOLID is a set of five object-oriented programming principles that help you create code that is more organized, easier to maintain, test, and evolve.
In this article, we’ll understand each principle in simple terms, with practical examples, without unnecessary complexity.
What Is SOLID?
SOLID is an acronym created by Robert C. Martin (Uncle Bob) and stands for:
S – Single Responsibility Principle
O – Open/Closed Principle
L – Liskov Substitution Principle
I – Interface Segregation Principle
D – Dependency Inversion Principle
Let’s go through each one.
1️⃣ S — Single Responsibility Principle
A class should have only one reason to change.
❌ Bad example
A class that does too much:
class Report {
void generateReport() {}
void saveToDatabase() {}
void sendEmail() {}
}
This class:
- Generates reports
- Saves data
- Sends emails
If any of these rules change, the entire class may break.
✅ Correct example
class ReportGenerator {
void generate() {}
}
class ReportRepository {
void save() {}
}
class EmailService {
void send() {}
}
📌 Benefit: More organized code that’s easier to test and maintain.
2️⃣ O — Open/Closed Principle
Software entities should be open for extension, but closed for modification.
❌ Bad example
class DiscountCalculator {
double calculate(String type) {
if (type.equals("CHRISTMAS")) return 0.2;
if (type.equals("BLACK_FRIDAY")) return 0.3;
return 0;
}
}
Every time a new discount appears, you must modify this class.
✅ Correct example
interface Discount {
double calculate();
}
class ChristmasDiscount implements Discount {
public double calculate() {
return 0.2;
}
}
class BlackFridayDiscount implements Discount {
public double calculate() {
return 0.3;
}
}
📌 Benefit: You add new behaviors without changing existing code.
3️⃣ L — Liskov Substitution Principle
A subclass should be replaceable by its superclass without breaking the system.
❌ Classic problematic example
class Bird {
void fly() {}
}
class Penguin extends Bird {
// Penguins don’t fly!
}
This is a conceptual error: not every bird can fly.
✅ Correct example
class Bird {}
class FlyingBird extends Bird {
void fly() {}
}
class Penguin extends Bird {}
📌 Benefit: Correct inheritance hierarchies and fewer unexpected bugs.
4️⃣ I — Interface Segregation Principle
No class should be forced to implement methods it does not use.
❌ Bad example
interface Employee {
void work();
void eat();
}
What if we have a robot?
class Robot implements Employee {
public void work() {}
public void eat() {} // Makes no sense
}
✅ Correct example
interface Worker {
void work();
}
interface Eater {
void eat();
}
class Human implements Worker, Eater {}
class Robot implements Worker {}
📌 Benefit: Smaller, more specific, and reusable interfaces.
5️⃣ D — Dependency Inversion Principle
Depend on abstractions, not on concrete implementations.
❌ Bad example
class OrderService {
private MySQLDatabase db = new MySQLDatabase();
}
Here, the code is tightly coupled to MySQL.
✅ Correct example
interface Database {
void save();
}
class MySQLDatabase implements Database {
public void save() {}
}
class OrderService {
private Database db;
OrderService(Database db) {
this.db = db;
}
}
📌 Benefit: Flexible code that’s easy to test (with mocks) and easy to change implementations.
🚀 Conclusion
Applying SOLID is not about:
❌ Writing complex code
❌ Using patterns everywhere
It’s about:
✅ Organization
✅ Maintainability
✅ Scalability
✅ Fewer bugs in the future
You don’t need to apply all principles all the time, but understanding them helps you make better design decisions.
Top comments (0)