In this article, you’ll learn about the SOLID Principles in Java — five key object-oriented design principles that make your code scalable, maintainable, and flexible.
These principles are the foundation of writing clean code and are widely used in real-world projects and system design interviews.
🔹 What are SOLID Principles?
SOLID is an acronym that represents five design principles introduced by Robert C. Martin (Uncle Bob). They help developers design software that is easy to understand, maintain, and extend.
SOLID stands for:
- S – Single Responsibility Principle (SRP)
- O – Open/Closed Principle (OCP)
- L – Liskov Substitution Principle (LSP)
- I – Interface Segregation Principle (ISP)
- D – Dependency Inversion Principle (DIP)
1️⃣ Single Responsibility Principle (SRP)
Definition: A class should have only one reason to change — it should do only one thing.
In simple terms: One class = One job.
🚫 Bad Example:
public class Employee {
public void calculateSalary() {
// Salary calculation logic
}
public void generateReport() {
// Report generation logic
}
}
Here, the Employee class does two things: salary calculation and report generation — violating SRP.
✅ Correct Example:
public class SalaryCalculator {
public void calculateSalary() {
// Salary calculation logic
}
}
public class ReportGenerator {
public void generateReport() {
// Report generation logic
}
}
Now each class has a single responsibility — much cleaner and easier to maintain.
2️⃣ Open/Closed Principle (OCP)
Definition: Classes should be open for extension but closed for modification.
That means you should be able to add new functionality without changing existing code.
🚫 Bad Example:
public class NotificationService {
public void send(String type) {
if (type.equals("EMAIL")) {
System.out.println("Sending Email...");
} else if (type.equals("SMS")) {
System.out.println("Sending SMS...");
}
}
}
Adding a new type (like Push Notification) would require modifying the class — violating OCP.
✅ Correct Example (Using Polymorphism):
interface Notification {
void send();
}
class EmailNotification implements Notification {
public void send() {
System.out.println("Sending Email...");
}
}
class SMSNotification implements Notification {
public void send() {
System.out.println("Sending SMS...");
}
}
class NotificationService {
public void notifyUser(Notification notification) {
notification.send();
}
}
Now, adding a new notification type just means adding a new class — no existing code needs modification.
3️⃣ Liskov Substitution Principle (LSP)
Definition: Subclasses should be replaceable with their base classes without breaking functionality.
In other words, derived classes must be able to stand in for their base classes.
🚫 Bad Example:
class Bird {
void fly() {
System.out.println("Flying...");
}
}
class Ostrich extends Bird {
void fly() {
throw new UnsupportedOperationException("Ostrich can't fly!");
}
}
This violates LSP — Ostrich is a Bird, but it can’t behave like one (flying causes an error).
✅ Correct Example:
interface Bird {
void eat();
}
interface Flyable {
void fly();
}
class Sparrow implements Bird, Flyable {
public void eat() { System.out.println("Sparrow eating"); }
public void fly() { System.out.println("Sparrow flying"); }
}
class Ostrich implements Bird {
public void eat() { System.out.println("Ostrich eating"); }
}
Now the hierarchy is correct — no class is forced to implement behavior it doesn’t support.
4️⃣ Interface Segregation Principle (ISP)
Definition: A class should not be forced to implement interfaces it doesn’t use.
In simple words: Prefer smaller, specific interfaces over one large, general-purpose interface.
🚫 Bad Example:
interface Worker {
void work();
void eat();
}
class Robot implements Worker {
public void work() { System.out.println("Robot working"); }
public void eat() {
// Robots don't eat, but must implement this
}
}
✅ Correct Example:
interface Workable {
void work();
}
interface Eatable {
void eat();
}
class Human implements Workable, Eatable {
public void work() { System.out.println("Human working"); }
public void eat() { System.out.println("Human eating"); }
}
class Robot implements Workable {
public void work() { System.out.println("Robot working"); }
}
Now each class implements only the interfaces it needs — following ISP.
5️⃣ Dependency Inversion Principle (DIP)
Definition: High-level modules should not depend on low-level modules — both should depend on abstractions (interfaces).
Also, abstractions should not depend on details; details should depend on abstractions.
🚫 Bad Example:
class MySQLDatabase {
void connect() {
System.out.println("Connected to MySQL");
}
}
class Application {
private MySQLDatabase database = new MySQLDatabase();
void start() {
database.connect();
}
}
Here, the Application class depends directly on a specific database — tightly coupled.
✅ Correct Example:
interface Database {
void connect();
}
class MySQLDatabase implements Database {
public void connect() {
System.out.println("Connected to MySQL");
}
}
class PostgreSQLDatabase implements Database {
public void connect() {
System.out.println("Connected to PostgreSQL");
}
}
class Application {
private Database database;
// Dependency is injected through constructor
public Application(Database database) {
this.database = database;
}
void start() {
database.connect();
}
}
// Test
public class Main {
public static void main(String[] args) {
Database db = new PostgreSQLDatabase();
Application app = new Application(db);
app.start();
}
}
Output:
Connected to PostgreSQL
Now the application depends on the interface, not the concrete class — making it flexible and extensible.
✅ Summary of SOLID Principles
| Principle | Goal |
|---|---|
| Single Responsibility | One class should do one thing |
| Open/Closed | Open for extension, closed for modification |
| Liskov Substitution | Subclasses must be replaceable by base classes |
| Interface Segregation | Use small, specific interfaces |
| Dependency Inversion | Depend on abstractions, not concrete classes |
Why SOLID Matters
- Improves code maintainability and readability
- Reduces coupling and increases flexibility
- Makes code easier to test and scale
- Encourages best practices in object-oriented design
🏁 Conclusion
The SOLID Principles are at the heart of clean, professional software design. When applied properly, they make your code robust, reusable, and ready for change.
Master these principles, and you’ll write code that’s not just functional — but future-proof.
Next Read: 👉 Singleton Design Pattern 👉 Builder Design Pattern 👉 Factory Design Pattern
0 Comments