OOP · SOLID Principles
Open Closed Principle (OCP)
Design every class with a single, clearly defined responsibility.
test, debug, and reuse across different modules. As applications grow, developers can introduce new features without continuously modifying existing classes, resulting in cleaner architecture, better scalability, and improved long-term maintainability.
Best Practices
- Design every class with a single, clearly defined responsibility.
- Keep business logic separate from database and communication logic.
- Create small, focused service classes instead of large utility classes.
- Name classes according to the responsibility they perform.
- Review classes regularly to identify responsibilities that should be separated.
Common Mistakes
- Placing multiple unrelated methods inside one class.
- Combining business logic with database operations.
- Sending emails directly from entity classes.
- Creating "God Classes" that manage every feature.
- Assuming one class should handle everything related to an object.
Real-World Problem
Imagine you're developing an Online Shopping System. Initially, the application supports only Regular Customers, who receive a 5% discount. The DiscountCalculator class calculates this discount correctly, and everything works as expected.
After a few months, the business introduces Premium Customers with a 10% discount. Later, VIP Customers receive a 20% discount, followed by Festival Offers, Corporate Discounts, and Student Discounts. Every time a new discount type is introduced, developers open the same DiscountCalculator class and modify its existing logic.
Although the application continues to work, each modification increases the possibility of introducing bugs into code that was already tested and deployed.
Problem The DiscountCalculator class depends on multiple if-else or switch statements to determine which discount should be applied. As new customer categories are introduced, developers repeatedly modify the existing class. Instead of extending the application's functionality, they continuously change already working code.
Over time, the class becomes larger, more difficult to understand, and harder to maintain. Every modification requires regression testing because changes to one discount rule may unintentionally affect another.

Program 1 – OCP Violation (Bad Design)
Program 2 – OCP Applied (Good Design)


Pain Points
Every new customer category forces developers to modify the same class, increasing maintenance effort and the risk of introducing defects. The growing number of conditional statements reduces readability and makes the application more difficult to test. Existing functionality must be retested whenever a new discount is added, even though previously implemented features have not changed. As business requirements continue to evolve, the class becomes increasingly complex and difficult to manage.
Solution
The Open Closed Principle states that software entities should be open for extension but closed for modification.
Instead of modifying the existing DiscountCalculator whenever a new discount is introduced, each discount rule should be implemented in its own class. The calculator simply works with a common abstraction, while new discount strategies can be added by creating new classes rather than changing existing ones. This allows the application to grow without repeatedly modifying stable code.
Benefits
The Open Closed Principle enables developers to introduce new features without modifying existing, tested code. This reduces the likelihood of introducing bugs, simplifies maintenance, and allows applications to evolve more easily as business requirements change. It also improves scalability because new functionality can be implemented by creating new classes instead of altering existing ones. As a result, software becomes more flexible, reusable, and easier to maintain over time.
Best Practices
- Program to abstractions rather than concrete implementations.
- Prefer polymorphism over multiple conditional statements.
- Add new functionality through new classes instead of modifying existing ones.
- Keep existing business logic stable after testing.
- Design classes that can be extended without changing their source code.
Open for extension, closed for modification — add behavior via new types, not edits to old code.
// BAD: every new shape forces editing this method
// double area(Shape s) { if (s is Circle)... else if (s is Square)... }
interface Shape { double area(); }
class Circle implements Shape {
double r;
Circle(double r) { this.r = r; }
public double area() { return Math.PI * r * r; }
}
// Adding a Triangle means writing a NEW class — no existing code changes
class Triangle implements Shape {
double b, h;
Triangle(double b, double h) { this.b = b; this.h = h; }
public double area() { return 0.5 * b * h; }
}