Q: Explain the SOLID Principles with Java examples.
Answer:
SOLID is a set of five design principles for writing maintainable, scalable object-oriented code.
S — Single Responsibility Principle
A class should have only one reason to change. It should do one thing and do it well.
// ❌ Violates SRP: handles both user logic AND email sending
public class UserService {
public void createUser(User user) { /* save to DB */ }
public void sendWelcomeEmail(User user) { /* send email */ }
}
// ✅ Follows SRP: each class has one responsibility
public class UserService {
public void createUser(User user) { /* save to DB */ }
}
public class EmailService {
public void sendWelcomeEmail(User user) { /* send email */ }
}
O — Open/Closed Principle
Classes should be open for extension, closed for modification. Add new behavior without changing existing code.
// ❌ Must modify this class every time a new shape is added
public double calculateArea(Shape shape) {
if (shape instanceof Circle) return Math.PI * ((Circle) shape).radius * ((Circle) shape).radius;
if (shape instanceof Rectangle) return ((Rectangle) shape).w * ((Rectangle) shape).h;
}
// ✅ Add new shapes by extending, not modifying
public abstract class Shape {
public abstract double area();
}
public class Circle extends Shape {
@Override public double area() { return Math.PI * radius * radius; }
}
// Adding a new shape doesn't touch existing code
L — Liskov Substitution Principle
Subtypes must be substitutable for their base types without breaking the program.
// ❌ Violates LSP: Square changes Rectangle's expected behavior
public class Rectangle {
public void setWidth(int w) { this.width = w; }
public void setHeight(int h) { this.height = h; }
}
public class Square extends Rectangle {
@Override public void setWidth(int w) { this.width = w; this.height = w; } // Surprise!
}
// Code expecting Rectangle behavior breaks:
Rectangle r = new Square();
r.setWidth(5);
r.setHeight(10);
assert r.area() == 50; // FAILS! Square made it 100
I — Interface Segregation Principle
Clients should not be forced to depend on methods they don't use. Prefer small, focused interfaces.
// ❌ Fat interface: forces all implementations to handle everything
public interface Worker {
void work();
void eat();
void sleep();
}
// ✅ Segregated: each interface is focused
public interface Workable { void work(); }
public interface Feedable { void eat(); }
public class Robot implements Workable {
@Override public void work() { /* ... */ }
// Robot doesn't need eat() or sleep()
}
D — Dependency Inversion Principle
High-level modules should not depend on low-level modules. Both should depend on abstractions.
// ❌ High-level depends directly on low-level
public class OrderService {
private MySQLOrderRepository repo = new MySQLOrderRepository(); // Tight coupling
}
// ✅ Both depend on abstraction
public interface OrderRepository { void save(Order order); }
public class OrderService {
private final OrderRepository repo; // Depends on interface
public OrderService(OrderRepository repo) { this.repo = repo; } // DI
}
[!TIP] In interviews, don't just list the principles — give examples of violations and their consequences. This shows you've actually used them in practice rather than memorizing definitions.