Design Pattern

Open Closed Principle (OCP)

Clean Java-only production-ready implementation.


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

Every new payment type requires modifying the processPayment switch in PaymentProcessor.

Define a stable PaymentHandler interface. Each payment type is a new class implementing it. PaymentRouter resolves by type — no switch statement.

// ─── EXAMPLE 1 ──────────────────────────────────────────────────────────────
// WHAT WE ARE IMPLEMENTING:
// A payment processing system allowing new payment gateways to be added
// without modifying existing checkout code.
//
// WHERE THE PRINCIPLE FITS IN:
// The system defines a PaymentGateway interface that is open for extension
// via StripePayment or PayPalPayment, but closed for modification in the
// client PaymentService.
// ────────────────────────────────────────────────────────────────────────────
// --- Stable interface (closed for modification) ---
interface PaymentHandler {
    void process(double amount);
}

// --- Extensions (open for extension) ---
class CardPaymentHandler implements PaymentHandler {
    public void process(double amount) {
        System.out.println("  [Card] Processing " + amount + " via card gateway. Auth: " + (amount > 100 ? "3DS required" : "Normal"));
    }
}

class UPIPaymentHandler implements PaymentHandler {
    public void process(double amount) {
        System.out.println("  [UPI] Processing " + amount + " via UPI. " + (amount > 5000 ? "OTP validation" : "Quick pay"));
    }
}

class WalletPaymentHandler implements PaymentHandler {
    public void process(double amount) {
        System.out.println("  [Wallet] Processing " + amount + " via wallet. Balance check: " + (amount >= 500 ? "OK" : "Low balance fallback"));
    }
}

// --- Resolver (no switch) ---
class PaymentRouter {
    private final java.util.Map<String, PaymentHandler> handlers = new java.util.HashMap<>();

    public PaymentRouter() {
        // Register — one line per handler
        handlers.put("CARD", new CardPaymentHandler());
        handlers.put("UPI", new UPIPaymentHandler());
        handlers.put("WALLET", new WalletPaymentHandler());
    }

    public PaymentHandler getHandler(String type) {
        PaymentHandler handler = handlers.get(type.toUpperCase());
        if (handler == null) throw new IllegalArgumentException("Unknown payment type: " + type);
        return handler;
    }

    // To add NetBanking: no existing class modified.
    // Add NetBankingPaymentHandler implements PaymentHandler
    // Register: handlers.put("NETBANKING", new NetBankingPaymentHandler())
}

// --- Client ---
public class Main {
    public static void main(String[] args) {
        PaymentRouter router = new PaymentRouter();

        for (String type : new String[]{"CARD", "UPI", "WALLET"}) {
            System.out.println("Payment via " + type + ":");
            router.getHandler(type).process(250.0);
        }
    }
}