Skip to content

OCP - The Open/Closed Principle

Add new behaviour by adding new code, not by editing code that already works. Usually one interface and one class per variant.

Builds on Interfaces and Method overriding and polymorphism

Stage 2core2 min read

The code

package SOLID.OCP.BadCode;

public class PaymentProcessor {

    public void processPayment(String paymentMethod, double amount) {
        // The string decides which branch of business logic runs.
        if (paymentMethod.equals("CreditCard")) {
            System.out.println("Payment via credit card: " + amount);
        } else if (paymentMethod.equals("DebitCard")) {
            System.out.println("Payment via debit card: " + amount);
        } else if (paymentMethod.equals("Paypal")) {
            System.out.println("Payment via paypal: " + amount);
        } else {
            throw new IllegalArgumentException(
                "Unsupported payment method" + paymentMethod
            );
        }
    }
}

Main calls the processor with a string, for example processor.processPayment("CreditCard", 100). Java checks that the first argument is a string. It cannot check that the spelling matches a supported payment method.

What is wrong

Software entities should be open for extension, but closed for modification.

In plain words: when a kind of change keeps coming, add new code. Do not keep editing code that already works. New payment methods are this kind of change.

This code has no UPI branch. When product asks for UPI, you must add another else if inside PaymentProcessor. Net Banking, a wallet, and Pay Later need the same edit. So the class is not closed against the change that happens most often.

The problem is not that every if is bad. The problem is a growing list of real behaviors in one method, especially when the same list later appears in fee, refund, and display code.

What it costs you

  • Every extension risks old behavior. Adding a method such as UPI edits the method that also handles cards, so you must re-test every branch (a regression test checks that old behavior still works).
  • Strings hide mistakes. "Upi", "UPI", and "upi" all compile but behave differently at runtime.
  • The decision spreads. Refunds, fees, and labels can each gain a copy of the payment-method chain.
  • Small defects survive. The error text has no space before the method name, producing Unsupported payment methodUPI.
  • Parallel work collides. Two developers adding methods edit the same lines and cause merge conflicts, because Git cannot combine two edits to the same lines.

Quick test

Ask, "To support the next payment method, do I add a new implementation or edit this processor?" Here the answer is "edit the processor," so the part that keeps changing has no place to plug in new code.

A short switch over a fixed enum can still be reasonable. Refactor when the variants genuinely grow or contain enough logic to deserve separate tests; do not create a plugin system for an imagined future.

See GoodCode for one class per payment method, and OCP-Notes.md for the full explanation.

In this topic

Before code2 files

BadCode/Main.java15 linesGitHub
package SOLID.OCP.BadCode;/** * Bad: Adding a new payment method requires changing PaymentProcessor (if/else). */public class Main {    @SuppressWarnings("unused")    public static void main(String[] args) {        PaymentProcessor processor = new PaymentProcessor();        processor.processPayment("CreditCard", 100);        processor.processPayment("Paypal", 200);    }}
BadCode/PaymentProcessor.java22 linesGitHub
package SOLID.OCP.BadCode;public class PaymentProcessor {    public void processPayment(String paymentMethod, double amount) {        if (paymentMethod.equals("CreditCard")) {            // business logic            System.out.println("Payment via credit card: " + amount);        } else if (paymentMethod.equals("DebitCard")) {            // business logic            System.out.println("Payment via debit card: " + amount);        } else if (paymentMethod.equals("Paypal")) {            // business logic            System.out.println("Payment via paypal: " + amount);        } else {            throw new IllegalArgumentException(                "Unsupported payment method" + paymentMethod            );        }    }}

Done with this topic?

How well do you know it?

PreviousSRP - The Single Responsibility Principle
Practise 6 questions