Real-World Examples of Violating the Open-Closed Principle
Let's explore some common examples where the Open-Closed Principle is violated and explore how these issues can be addressed through refactoring.

Object-oriented programming has a few core principles worth internalizing, and the Open-Closed Principle (OCP) is one of the most practical. It says software entities should be open for extension but closed for modification. In practice, developers run into OCP violations often — the result is code that's brittle, bug-prone, and painful to extend. Let's look at some concrete examples and how refactoring can fix them.
Example 1: Conditional Statements Proliferation
Issue: One common violation of the Open-Closed Principle occurs when conditional statements proliferate throughout the codebase. For instance, consider a scenario where a system needs to calculate the shipping cost based on various factors such as destination, weight, and shipping method. Without adhering to the OCP, developers might end up with a series of if-else statements scattered across the codebase to handle different shipping scenarios.
if shipping_method == 'standard':
shipping_cost = calculate_standard_shipping_cost(destination, weight)
elif shipping_method == 'express':
shipping_cost = calculate_express_shipping_cost(destination, weight)
elif shipping_method == 'priority':
shipping_cost = calculate_priority_shipping_cost(destination, weight)
# More conditions added for different scenarios
Solution: To adhere to the Open-Closed Principle, we can use polymorphism and abstraction to encapsulate the varying behavior of shipping methods. By defining an abstract ShippingMethod interface or base class and implementing concrete subclasses for each shipping method, we get a more flexible and extensible design.
class ShippingMethod:
def calculate_shipping_cost(self, destination, weight):
pass
class StandardShipping(ShippingMethod):
def calculate_shipping_cost(self, destination, weight):
# Calculate standard shipping cost
pass
class ExpressShipping(ShippingMethod):
def calculate_shipping_cost(self, destination, weight):
# Calculate express shipping cost
pass
class PriorityShipping(ShippingMethod):
def calculate_shipping_cost(self, destination, weight):
# Calculate priority shipping cost
pass
By following this approach, adding new shipping methods in the future becomes as simple as creating a new subclass of ShippingMethod, thus adhering to the Open-Closed Principle.
Example 2: Hard-Coded Dependencies
Issue: Another common violation of the Open-Closed Principle occurs when classes have hard-coded dependencies on concrete implementations rather than abstractions. This tightly couples classes together, making it challenging to extend or modify the system without modifying existing code.
public class OrderProcessor {
private DatabaseConnector dbConnector;
public OrderProcessor() {
this.dbConnector = new DatabaseConnector(); // Concrete dependency
}
public void processOrder(Order order) {
// Process order logic
dbConnector.saveOrder(order); // Hard-coded dependency
}
}
Solution: To adhere to the Open-Closed Principle, we can introduce dependency inversion by programming to interfaces instead of concrete implementations. By injecting dependencies via constructor or setter methods, we can decouple classes and make them more open for extension.
public class OrderProcessor {
private DatabaseConnector dbConnector;
public OrderProcessor(DatabaseConnector dbConnector) {
this.dbConnector = dbConnector; // Dependency injected via constructor
}
public void processOrder(Order order) {
// Process order logic
dbConnector.saveOrder(order); // Dependency is now abstract
}
}
By following this approach, we can easily replace the DatabaseConnector with a different implementation without modifying the OrderProcessor class, thus adhering to the Open-Closed Principle.
Conclusion
OCP violations make codebases fragile and rigid — the kind where every new requirement means touching existing logic and risking regressions. Abstraction, polymorphism, and dependency inversion give you a way out. Once you've refactored a few real examples this way, the pattern becomes second nature. The payoff isn't just cleaner design; it's a codebase that new contributors can extend without needing to understand every existing branch.


