Open Closed Principle
Open Closed Principle
Section titled “Open Closed Principle”The Open Closed Principle (also known as OCP or OCP principle) is one of the five SOLID principles of object-oriented design. The Open Closed Principle (sometimes written as open-closed principle or open/closed principle) states that software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. This means you should be able to add new functionality without changing existing code.
In simple terms, whenever we design a class, we need to carefully encapsulate the implementation details so that it has good maintainability. We want it to be open to extension but closed to modification. Understanding the Open Closed Principle is essential for building maintainable, scalable software systems.
Open Closed Principle Example
Section titled “Open Closed Principle Example”Let’s look at a practical Open Closed Principle example to understand how it works in real-world scenarios.
Understanding the Principle
Section titled “Understanding the Principle”The behavior of a module can be extended without modifying its source code. This is typically achieved through mechanisms such as:
- Inheritance - Creating subclasses
- Interfaces - Implementing contracts
- Composition - Combining objects
Example: Notification System
Section titled “Example: Notification System”Consider a notification system where you need to send alerts through different channels. Instead of modifying the core notification logic every time you add a new channel, you can extend it through inheritance.
Violating OCP (Bad Approach)
Section titled “Violating OCP (Bad Approach)”Following OCP (Good Approach)
Section titled “Following OCP (Good Approach)”Adding New Functionality
Section titled “Adding New Functionality”If you need to add a new notification channel (like Teams or SMS), you can create a new subclass without touching the existing code:
classDiagram
class Notification {
<<abstract>>
+send(message: str)
}
class EmailNotification {
-recipient: str
+send(message: str)
}
class SlackNotification {
-channel: str
+send(message: str)
}
class TeamsNotification {
-team: str
+send(message: str)
}
Notification <|-- EmailNotification
Notification <|-- SlackNotification
Notification <|-- TeamsNotification
Benefits of Following OCP
Section titled “Benefits of Following OCP”Handling Scenarios Where Modification Might Be Necessary
Section titled “Handling Scenarios Where Modification Might Be Necessary”While OCP encourages extension over modification, there are scenarios where modification is unavoidable or where alternative approaches are needed:
1. Third-Party Classes (Using Adapter Pattern)
Section titled “1. Third-Party Classes (Using Adapter Pattern)”When working with external libraries or sealed classes that cannot be extended, use composition and adapters instead.
Problem: You’re using a third-party notification library that you cannot modify:
Solution: Create an adapter/wrapper class that implements your interface:
This way, you extend functionality (add new notification types) without modifying the third-party code, following OCP through composition.
classDiagram
class Notification {
<<interface>>
+send(message: str)
}
class ThirdPartyEmailService {
+send_email(to, subject, body)
}
class ThirdPartyEmailAdapter {
-email_service: ThirdPartyEmailService
-recipient: str
+send(message: str)
}
Notification <|.. ThirdPartyEmailAdapter
ThirdPartyEmailAdapter *-- ThirdPartyEmailService : uses
2. Cross-Cutting Concerns
Section titled “2. Cross-Cutting Concerns”When you need to add functionality that doesn’t belong to the same domain (like logging, caching, or security), consider using decorators or aspect-oriented programming instead of modifying the base class.
Example: Adding audit logging without modifying the base class:
3. Fundamental Behavior Changes
Section titled “3. Fundamental Behavior Changes”If the core behavior needs to change (not just extend), modification might be necessary. However, consider if this indicates a design issue that should be refactored. Sometimes, it’s better to create a new abstraction rather than modifying the existing one.