You are currently viewing Open Closed Principle In Java

Open Closed Principle In Java

Introduction

The Open/Closed Principle (OCP) is the second letter in the SOLID principles of object-oriented design. It states:

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

In other words, you should be able to add new functionality to a class or module without changing its existing code. This is especially important for systems that evolve over time or are maintained by large teams.

Let’s look at how to apply the Open/Closed Principle in real Java projects.


1. Recognizing the Violation

Suppose you are building a simple invoicing system:

class InvoicePrinter {
    public void printInvoice(Invoice invoice, String format) {
        if ("PDF".equals(format)) {
            // PDF logic
        } else if ("HTML".equals(format)) {
            // HTML logic
        }
    }
}

This class is not closed for modification. Every time you need a new format (e.g., CSV), you must edit this class. That violates OCP.


2. Applying OCP with Polymorphism

A better design is to rely on interfaces or abstract classes, allowing you to add new behaviors through extension.

✅ Step 1: Define an abstraction

interface InvoiceFormatter {
    void format(Invoice invoice);
}

✅ Step 2: Create concrete implementations

class PdfInvoiceFormatter implements InvoiceFormatter {
    public void format(Invoice invoice) {
        System.out.println("Formatting as PDF");
    }
}

class HtmlInvoiceFormatter implements InvoiceFormatter {
    public void format(Invoice invoice) {
        System.out.println("Formatting as HTML");
    }
}

✅ Step 3: Refactor the printer

class InvoicePrinter {
    public void print(Invoice invoice, InvoiceFormatter formatter) {
        formatter.format(invoice);
    }
}

Now you can add new formats without modifying InvoicePrinter.


3. Benefits of OCP

  • Extensible systems: Easily add features with minimal risk of breaking things.
  • Isolated testing: Each formatter can be tested independently.
  • Pluggable architecture: Useful in plugin systems, payment gateways, UI components.

4. Real-World Use Case: Payment Methods

The Open/Closed Principle is especially valuable in domains where new features or integrations are frequently added. One common example is payment processing in e-commerce systems.

Let’s say you initially support only credit card payments:

class CheckoutService {
    public void pay(Payment payment) {
        System.out.println("Processing credit card...");
    }
}

Soon, your business needs to support PayPal, Stripe, and others. If you modify CheckoutService each time, the class becomes fragile and tightly coupled to all payment logic.

✅ Step 1: Define an abstraction

interface PaymentProcessor {
    void process(Payment payment);
}

✅ Step 2: Implement different processors

class CreditCardProcessor implements PaymentProcessor {
    public void process(Payment payment) {
        System.out.println("Processing credit card...");
    }
}

class PaypalProcessor implements PaymentProcessor {
    public void process(Payment payment) {
        System.out.println("Processing PayPal...");
    }
}

class StripeProcessor implements PaymentProcessor {
    public void process(Payment payment) {
        System.out.println("Processing Stripe...");
    }
}

✅ Step 3: Refactor CheckoutService to use the abstraction

class CheckoutService {
    private final PaymentProcessor processor;

    public CheckoutService(PaymentProcessor processor) {
        this.processor = processor;
    }

    public void pay(Payment payment) {
        processor.process(payment);
    }
}

Now the system is open for extension — new payment types only require new PaymentProcessor implementations — and closed for modificationCheckoutService remains unchanged.

5. Summary

TermMeaning
Open for extensionYou can add new behaviors
Closed for modificationExisting code does not need to change

Conclusion

The Open/Closed Principle helps you extend your code without breaking existing functionality. By depending on abstractions, you keep your design flexible, testable, and ready for change.

You can find the complete code of this article here in GitHub.

📚 Related: Liskov Substitution Principle in Java

Noel Kamphoa

Experienced software engineer with expertise in Telecom, Payroll, and Banking. Now Senior Software Engineer at Societe Generale Paris.

This Post Has One Comment

Comments are closed.