Open–Closed principle#
tl;dr - The OCP states that software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
The OCP states that software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. This means that the behavior of a module or class should be easily extended without modifying its source code. Let’s see an example in Java to understand the OCP and how to apply it.
public class PaymentProcessor {
public void processPayment(Payment payment) {
if (payment.getType() == PaymentType.CREDIT_CARD) {
// Code for processing credit card payment
} else if (payment.getType() == PaymentType.BANK_TRANSFER) {
// Code for processing bank transfer payment
}
}
}
In this example, the PaymentProcessor violates the OCP because it is not closed for modification. If we introduce a new payment type, such as PayPal, we would need to modify the PaymentProcessor class and add another conditional statement.
public interface PaymentProcessor {
void processPayment(Payment payment);
}
We create an interface or abstract PaymentProcessor class that declares the processPayment() method. Each specific payment processor will extend this class and provide its own implementation.
public class CreditCardPaymentProcessor implements PaymentProcessor {
@Override
public void processPayment(Payment payment) {
// Code for processing credit card payment
}
}
public class BankTransferPaymentProcessor implements PaymentProcessor {
@Override
public void processPayment(Payment payment) {
// Code for processing bank transfer payment
}
}
We create separate concrete implementations for credit card and bank transfer payment processors. Each subclass overrides the processPayment() method with its specific logic.
public class PaymentService {
private PaymentProcessor paymentProcessor;
public PaymentService(PaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
public void processPayment(Payment payment) {
paymentProcessor.processPayment(payment);
}
}
We introduce a PaymentService class that encapsulates the payment processing logic. It takes a PaymentProcessor object as a constructor parameter and uses polymorphism to delegate the payment processing to the specific processor.
Now, if we want to introduce a new payment type, such as PayPal, we can simply create a new PayPalPaymentProcessor class that extends PaymentProcessor and provides its own implementation of the processPayment() method. The existing code remains untouched.
By refactoring the code in this manner, we adhere to the OCP. The PaymentProcessor class is now closed for modification but open for extension. New payment types can be added by creating new subclasses without modifying the existing codebase. This approach enhances code maintainability, extensibility, and reduces the impact of changes on existing functionality.