Skip to main content

Open/Closed Principle (OCP)

What is the Open/Closed Principle?​

Shubh: Hey, do you know about the Open/Closed Principle?

You: I've heard of it, but I'm not entirely sure how it applies to coding. Can you explain it?

Shubh: Of course! The Open/Closed Principle (OCP) is one of the SOLID principles of object-oriented design. It states that software entities like classes, modules, and functions should be open for extension but closed for modification. This means you should be able to add new functionality without changing existing code.

Understanding the Problem: A Bad Approach​

To better understand the Open/Closed Principle, let's look at an example where this principle is not followed.

Shubh: Here’s a Payment class that violates the OCP because it requires modification every time a new payment method is added:

Payment.java
public class Payment {
public void pay(PaymentType paymentType){
if(paymentType == PaymentType.PAYPAL){
System.out.println("Paying through paypal");
}else if(paymentType == PaymentType.PAYTM){
System.out.println("Paying through paytm");
}else if(paymentType == PaymentType.GOOGLEPAY){
System.out.println("Paying through google pay");
}
}
}
PaymentType.java
public enum PaymentType {
PAYPAL, PAYTM, GOOGLEPAY, STRIPE;
}
Main.java
public class Main {
public static void main(String[] args) {
Payment paymentObj = new Payment();
paymentObj.pay(PaymentType.GOOGLEPAY);
}
}

You: I see. The Payment class has to be modified every time we add a new payment method. This violates the Open/Closed Principle.

Shubh: Exactly! This makes the code harder to maintain and more prone to errors. We should avoid modifying the existing Payment class to add new payment methods. Instead, we should design it in a way that allows us to extend its functionality without changing the existing code.

A Better Approach: Applying the Open/Closed Principle​

To adhere to the Open/Closed Principle, we can refactor the code using polymorphism, allowing us to add new payment methods without modifying existing code.

You: How can we refactor this to follow the OCP?

Shubh: Let’s do that by creating an interface for payment methods and then implementing it for each payment type:

PaymentMethod.java
interface PaymentMethod {
void pay();
}
PaypalPayment.java
class PaypalPayment implements PaymentMethod {
@Override
public void pay() {
System.out.println("Paying through PayPal");
}
}
PaytmPayment.java
class PaytmPayment implements PaymentMethod {
@Override
public void pay() {
System.out.println("Paying through Paytm");
}
}
GooglePayPayment.java
class GooglePayPayment implements PaymentMethod {
@Override
public void pay() {
System.out.println("Paying through Google Pay");
}
}
Payment.java
// no need to edit if new payment methods are needed
public class Payment {
private final PaymentMethod paymentMethod;

public Payment(PaymentMethod paymentMethod) {
this.paymentMethod = paymentMethod;
}

public void pay() {
paymentMethod.pay();
}
}
Main.java
public class Main {
public static void main(String[] args) {
Payment paymentObj = new Payment(new GooglePayPayment());
paymentObj.pay();
}
}

Shubh: In this refactored code, we have an interface PaymentMethod and different implementations for each payment type. The Payment class now takes a PaymentMethod object and calls its pay method.

You: I see. Now, if we want to add a new payment method, we just create a new class implementing the PaymentMethod interface, and we don't need to modify the Payment class.

Shubh: Exactly! This adheres to the Open/Closed Principle. Our Payment class is now closed for modification but open for extension.

You: This is much cleaner and easier to maintain. Thanks for explaining the Open/Closed Principle with such a clear example!