SOLID Principles
1. Single Responsibility Principle (SRP)
The Single Responsibility Principle states that a class should have only one
reason to change, meaning it should only have one job or responsibility.
❌ Wrong Example: A class handling multiple responsibilities
class UserManager {
private User user;
public UserManager(User user) {
this.user = user;
}
public void saveUser() {
// Save user to database
}
public void sendEmail() {
// Send email to user
}
}
✅ Right Example: Separate classes for each responsibility
class UserRepository {
public void saveUser(User user) {
// Save user to database
}
}
class EmailService {
public void sendEmail(User user) {
// Send email to user
}
}
2. Open/Closed Principle (OCP)
The Open/Closed Principle suggests that software entities (classes, modules,
functions) should be open for extension but closed for modification.
❌ Wrong Example: Modifying existing class to add new behavior
class DiscountCalculator {
public int calculateDiscount(String customerType) {
if (customerType.equals("regular")) {
return 10;
} else if (customerType.equals("premium")) {
return 20;
}
return 0;
}
}
✅ Right Example: Extend behavior without modifying existing code
interface DiscountStrategy {
int getDiscount();
}
class RegularCustomerDiscount implements DiscountStrategy {
public int getDiscount() {
return 10;
}
}
class PremiumCustomerDiscount implements DiscountStrategy {
public int getDiscount() {
return 20;
}
}
3. Liskov Substitution Principle (LSP)
The Liskov Substitution Principle states that objects of a superclass should be
replaceable with objects of a subclass without affecting the functionality of the
program.
❌ Wrong Example: Subclass breaks expected behavior
class Bird {
public void fly() {
System.out.println("Flying");
}
}
class Penguin extends Bird {
@Override
public void fly() {
throw new UnsupportedOperationException("Penguins can't fly");
}
}
✅ Right Example: Subclasses maintain expected behavior
class Bird {}
class FlyingBird extends Bird {
public void fly() {
System.out.println("Flying");
}
}
class NonFlyingBird extends Bird {}
4. Interface Segregation Principle (ISP)
The Interface Segregation Principle states that no client should be forced to
depend on methods it does not use. It encourages creating smaller, more
specific interfaces.
❌ Wrong Example: Fat interface forces implementation of unused methods
interface Worker {
void work();
void eat();
void sleep();
}
class Robot implements Worker {
public void work() {}
public void eat() { throw new UnsupportedOperationException(); }
public void sleep() { throw new UnsupportedOperationException(); }
}
✅ Right Example: Smaller, focused interfaces
interface Workable {
void work();
}
interface Eatable {
void eat();
}
interface Sleepable {
void sleep();
}
class Robot implements Workable {
public void work() {}
}
5. Dependency Inversion Principle (DIP)
The Dependency Inversion Principle suggests that high-level modules should
not depend on low-level modules, but both should depend on abstractions.
❌ Wrong Example: High-level module depends on low-level module
class PDFExporter {
public void export(String data) {
// Export to PDF
}
}
class ReportGenerator {
public void generateReport(String data) {
PDFExporter exporter = new PDFExporter();
exporter.export(data);
}
}
✅ Right Example: Depend on abstractions
interface Exporter {
void export(String data);
}
class PDFExporter implements Exporter {
public void export(String data) {
// Export to PDF
}
}
class ReportGenerator {
private Exporter exporter;
public ReportGenerator(Exporter exporter) {
this.exporter = exporter;
}
public void generateReport(String data) {
exporter.export(data);
}