What are the SOLID principles? Explain each in simple words.
SOLID is five design guidelines. Single responsibility (one reason to change), Open/closed (add features without editing working code), Liskov substitution (a child must work wherever its parent works), Interface segregation (small, focused interfaces) and Dependency inversion (depend on abstractions, not concrete classes).
Explain it simply
Think of organising a college fest.
- S, Single responsibility: the food team handles food, the sound team handles sound. If the menu changes, only the food team is disturbed.
- O, Open/closed: you can add a new event, say a gaming tournament, without rewriting the schedule for the existing events.
- L, Liskov substitution: if the plan says "any volunteer can man the help desk", every volunteer should actually be able to do it. A volunteer who refuses breaks the plan.
- I, Interface segregation: do not give every volunteer a 40-point checklist. The registration desk gets its own short list, the stage crew gets theirs.
- D, Dependency inversion: the fest committee asks for "a caterer", not "Sharma Caterers specifically". Any caterer that meets the requirements can be plugged in.
Example
// D: depend on an abstraction
interface Notifier { void send(String to, String message); }
class EmailNotifier implements Notifier {
public void send(String to, String message) { System.out.println("Email to " + to); }
}
class SmsNotifier implements Notifier {
public void send(String to, String message) { System.out.println("SMS to " + to); }
}
// S: this class only manages registrations, not how messages are delivered
class RegistrationService {
private final Notifier notifier;
RegistrationService(Notifier notifier) { this.notifier = notifier; }
void register(String student) {
// ...save the registration...
notifier.send(student, "You are registered for the fest");
}
}
public class Fest {
public static void main(String[] args) {
new RegistrationService(new SmsNotifier()).register("9876500000"); // prints: SMS to 9876500000
// O: adding WhatsAppNotifier later needs a new class, not a change here
}
}How it works
- Single Responsibility Principle: a class should have one reason to change. A class that validates input, saves to the database and sends emails changes whenever any of those changes. Split it.
- Open/Closed Principle: classes should be open for extension, closed for modification. New behaviour comes from new classes (a new
Notifier), not from adding anotherif-elsebranch to tested code. - Liskov Substitution Principle: any subclass must be usable through its parent type without surprises. If
Birdhasfly()andPenguin extends Birdthrows an exception infly(), the hierarchy is wrong. - Interface Segregation Principle: clients should not be forced to depend on methods they do not use. Prefer
PrintableandScannableover one hugeMachineinterface that a basic printer cannot fully implement. - Dependency Inversion Principle: high-level code (business rules) and low-level code (database, SMS provider) should both depend on abstractions.
RegistrationServicedepends onNotifier, not onSmsNotifier.
Real-world use
Spring Boot projects follow DIP everywhere: a service receives a repository interface through its constructor. Payment systems follow OCP by adding a new gateway class instead of editing a giant switch. SRP is why controllers, services and repositories are separate layers. The principles also make unit testing easy, because you can pass a fake Notifier instead of really sending SMS.
Common mistakes
- Reciting the five names without an example. Interviewers almost always ask "give me an example of each".
- Confusing dependency inversion with dependency injection. Injection (passing dependencies in through the constructor) is one technique for achieving inversion.
- Over-applying SOLID to a tiny program, creating an interface for every class. The principles are guides for code that will grow and change.
What to say in the interview
"SOLID is five object-oriented design principles. Single responsibility: a class should have one reason to change. Open/closed: I should be able to add behaviour by adding new code rather than editing working code. Liskov substitution: a subclass must work anywhere its parent is expected. Interface segregation: keep interfaces small so classes don't implement methods they don't need. Dependency inversion: high-level code depends on abstractions, not concrete classes. For example, a registration service that takes a Notifier interface follows SRP and DIP, and adding WhatsApp notifications later is just a new class, which is open/closed."
Follow-up questions
- Give an example that violates the Liskov substitution principle.
- What is the difference between dependency inversion and dependency injection?
- How does the Strategy pattern help with the open/closed principle?
- Can following SOLID make code too complex?
Go deeper
Read these once the basics above feel easy.
The classic LSP example is Square extends Rectangle. Mathematically a square is a rectangle, but if Rectangle has setWidth and setHeight, a Square must keep both equal. Code that sets width to 5 and height to 4 and expects an area of 20 gets 16 instead. The subclass is valid Java but breaks the parent's promise, so it cannot be substituted safely. The fix is to model them differently, for example as separate immutable shapes that share a Shape interface with an area() method.