S – Single Responsibility Principle (SRP)
A class should have only one reason to change, meaning it should only have one responsibility.
// Bad Example: Single class has multiple responsibilities
class User {
void saveUserToDatabase() { /* code to save user */ }
void sendEmail() { /* code to send email */ }
}
// Good Example: Separate responsibilities
class User {
private String name;
private String email;
// getters and setters
}
class UserRepository {
void save(User user) { /* code to save user */ }
}
class EmailService {
void sendEmail(User user) { /* code to send email */ }
}
O – Open/Closed Principle (OCP)
Software entities (classes, modules, functions) should be open for extension but closed for modification.
// Bad: Modifying class when adding new behavior
class Rectangle {
double length, width;
}
class AreaCalculator {
double calculateRectangleArea(Rectangle r) { return r.length * r.width; }
}
// Good: Open for extension
interface Shape {
double area();
}
class Rectangle implements Shape {
double length, width;
public double area() { return length * width; }
}
class Circle implements Shape {
double radius;
public double area() { return Math.PI * radius * radius; }
}
class AreaCalculator {
double calculateArea(Shape shape) { return shape.area(); }
}
L – Liskov Substitution Principle (LSP)
Objects of a superclass should be replaceable with objects of a subclass without affecting correctness.
// Bad: Subclass breaks parent contract
class Bird {
void fly() {}
}
class Ostrich extends Bird {
@Override
void fly() { throw new UnsupportedOperationException(); }
}
// Good: Separate flying behavior
interface Flyable {
void fly();
}
class Sparrow extends Bird implements Flyable {
public void fly() {}
}
class Ostrich extends Bird { } // does not implement Flyable
I – Interface Segregation Principle (ISP)
Clients should not be forced to depend on interfaces they do not use. Split fat interfaces into smaller, more specific ones.
// Bad: Fat interface
interface Worker {
void work();
void eat();
}
class Robot implements Worker {
public void work() { /* working */ }
public void eat() { throw new UnsupportedOperationException(); }
}
// Good: Segregated interfaces
interface Workable { void work(); }
interface Eatable { void eat(); }
class Human implements Workable, Eatable {
public void work() { }
public void eat() { }
}
class Robot implements Workable {
public void work() { }
}
D – Dependency Inversion Principle (DIP)
High-level modules should not depend on low-level modules. Both should depend on abstractions.
// Bad: High-level module depends on low-level module
class MySQLDatabase {
void connect() { }
}
class UserService {
private MySQLDatabase db = new MySQLDatabase();
void saveUser() { db.connect(); }
}
// Good: Depend on abstraction
interface Database {
void connect();
}
class MySQLDatabase implements Database {
public void connect() { }
}
class UserService {
private Database db;
UserService(Database db) { this.db = db; }
void saveUser() { db.connect(); }
}
✅ Summary of SOLID:
- SRP → One class, one responsibility
- OCP → Extend, don’t modify
- LSP → Subtypes replace supertypes
- ISP → Many small interfaces, not one fat interface
- DIP → Depend on abstractions, not concrete classes