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.