Open/Closed Principle (OCP)
The Open/Closed Principle (OCP) is the second of the five SOLID principles. It tells us how to add new features without breaking code that already works.
Definition
Software should be open for extension, but closed for modification.
- Open for extension: you can add new behavior.
- Closed for modification: you do this without editing code that is already written and tested.
Analogy: think of a phone charger socket. When you buy a new device, you plug it in. You do not open the wall and rewire the house. The socket is closed for changes, but open for new devices.
Why It Matters
Every time you edit working code, you risk breaking it. If adding a feature means changing an old class, then:
- Old features can break by accident.
- You must re-test everything that class does.
- Many developers edit the same file, so merge conflicts grow.
OCP lets you add features by writing new code, not by changing old code.
A Violation
A discount calculator that uses an if-else chain:
public class DiscountCalculator {public double calculate(String customerType, double amount) {if (customerType.equals("REGULAR")) {return amount * 0.05;} else if (customerType.equals("PREMIUM")) {return amount * 0.10;} else if (customerType.equals("VIP")) {return amount * 0.20;}return 0;}}
A new customer type, such as STUDENT, forces us to edit this method. Each new type adds another branch, and each edit risks breaking the existing ones. This class is open for modification, which is the opposite of OCP.
The Fix
Put the changing part behind an interface. Each rule becomes its own class.
public interface DiscountPolicy {double calculate(double amount);}public class RegularDiscount implements DiscountPolicy {public double calculate(double amount) { return amount * 0.05; }}public class PremiumDiscount implements DiscountPolicy {public double calculate(double amount) { return amount * 0.10; }}public class VipDiscount implements DiscountPolicy {public double calculate(double amount) { return amount * 0.20; }}// This class never changes againpublic class DiscountCalculator {public double calculate(DiscountPolicy policy, double amount) {return policy.calculate(amount);}}
Now we add the student discount by writing a new class, and we do not touch any existing code:
public class StudentDiscount implements DiscountPolicy {public double calculate(double amount) { return amount * 0.15; }}
DiscountCalculator is closed (it never changes), and the system is open (we can add as many policies as we like).
How It Works
OCP depends on abstraction and polymorphism:
- The code that uses the behavior depends on an interface, not on a concrete class.
- New behavior is a new class that implements the interface.
- The Java runtime calls the right implementation, so the caller does not need to know which one it is.
Where You Already See It
| Place | How it follows OCP |
|---|---|
| Strategy pattern | Swap algorithms (discounts, sorting, payment) behind an interface |
Comparator | Collections.sort(list, comparator) is closed, and you add new sort orders by writing new comparators |
| JDBC | Your code uses Connection. A new database only needs a new driver |
Spring beans and @Service | Add a new implementation of an interface and inject it |
| Servlet filters and middleware | Add a new filter without changing the others |
Spotting a Violation
- A long
if-elseorswitchon a type or a string, which grows with every new feature. - You keep editing the same class to add each new variation.
- Adding one feature makes you touch many files with the same
switch. - You use
instanceofchecks to decide what to do.
Common Mistakes
1. Abstracting too early. Do not build an interface for every class "just in case". Write the simple code first. When the same kind of change happens a second time, then introduce the abstraction. A rule of thumb is: fool me once, shame on you. Fool me twice, refactor.
2. Thinking "closed" means never touching the file. No design is closed against every change. You choose which kinds of change to protect against, such as "new discount types". Other changes can still need edits.
3. Replacing every if with a class. A simple, stable if is fine. OCP matters where new variations keep arriving.
OCP and Other Principles
| Principle | Link to OCP |
|---|---|
| Single Responsibility | Small focused classes are easier to extend without editing |
| Liskov Substitution | New classes must behave correctly in place of the interface, or extension breaks callers |
| Dependency Inversion | Depend on interfaces, which is exactly what makes OCP possible |
Summary
- OCP: open for extension, closed for modification.
- Add features by writing new code, not by editing working code.
- The tool is abstraction: an interface plus new implementations.
- Warning signs: growing
if-elseorswitchchains andinstanceofchecks. - Use it where variations keep coming. Do not abstract too early.
- You already see it in the Strategy pattern,
Comparator, and Spring injection.
Interview Answer: What Is the Open/Closed Principle?
The Open/Closed Principle says that code should be open for extension but closed for modification. We should be able to add new behavior without editing existing, tested code.
For example, a discount calculator with an if-else chain on customer type must be edited for every new type, which risks breaking the old ones. I would define a DiscountPolicy interface and make each discount its own class. The calculator depends only on the interface, so a new StudentDiscount is just a new class, and nothing existing changes.
It relies on abstraction and polymorphism. A signal that I need it is a growing switch or instanceof chain. I do not abstract too early, though. I wait until the same kind of change happens repeatedly, because unneeded interfaces only add complexity.