Knowledge Guide
HomeOO & Low-Level DesignSOLID Principles

Real World Analogies and Code Example

In the previous lesson, we learned the definition of the Open/Closed Principle (OCP). Now, in this lesson, we’ll explore a real-world coding example to understand how this principle is applied.

Initial Code Example Without OCP

Let’s start with a code example where all methods and logic are placed within a single class. This approach violates the Open/Closed Principle because adding new functionality will require us to modify the existing class.

java
public class Invoice {
    private double amount;

    public Invoice(double amount) {
        this.amount = amount;
    }

    public double getAmount() {
        return amount;
    }

    // This method handles generating basic invoices
    public void generateInvoice() {
        System.out.println("Generating basic invoice for amount: " + amount);
    }

    // This method handles applying discounts on the invoice
    public void applyDiscount() {
        System.out.println("Applying discount on invoice: " + amount);
    }
}

In this example, the Invoice class is responsible for generating invoices and applying discounts. If we need to add a new type of invoice later (like an international invoice), we would have to modify this class.

Extending the Code: Why It Doesn’t Follow OCP

Now, suppose we want to add the functionality to generate international invoices. Without applying the Open/Closed Principle, we’ll have to modify the existing Invoice class like this:

java
public class Invoice {
    private double amount;

    public Invoice(double amount) {
        this.amount = amount;
    }

    public double getAmount() {
        return amount;
    }

    // Adding new logic for international invoices
    public void generateInvoice(String type) {
        if (type.equals("basic")) {
            System.out.println("Generating basic invoice for amount: " + amount);
        } else if (type.equals("international")) {
            System.out.println("Generating international invoice for amount: " + amount);
        }
    }

    public void applyDiscount() {
        System.out.println("Applying discount on invoice: " + amount);
    }
}

Here, we’ve introduced an if-else block to handle different invoice types. This is a clear violation of the Open/Closed Principle. Each time we add a new invoice type, we will need to modify the generateInvoice() method, making the code more difficult to maintain and prone to errors.

Refactoring the Code to Follow OCP

Let’s refactor the code so that it follows the Open/Closed Principle. We’ll separate the logic into different classes so that new types of invoices can be added without modifying the existing code.

First, we’ll create an interface for generating invoices:

java
// Interface that defines the contract for generating invoices
public interface InvoiceGenerator {
    void generateInvoice(double amount);
}

Next, we’ll create separate classes for each type of invoice:

java
// Class for generating basic invoices
public class BasicInvoice implements InvoiceGenerator {
    @Override
    public void generateInvoice(double amount) {
        System.out.println("Generating basic invoice for amount: " + amount);
    }
}

// Class for generating international invoices
public class InternationalInvoice implements InvoiceGenerator {
    @Override
    public void generateInvoice(double amount) {
        System.out.println("Generating international invoice for amount: " + amount);
    }
}

Finally, we modify the Invoice class to delegate invoice generation to an object of type InvoiceGenerator:

java
public class Invoice {
    private double amount;
    private InvoiceGenerator invoiceGenerator;

    // Constructor takes an InvoiceGenerator to delegate invoice generation
    public Invoice(double amount, InvoiceGenerator invoiceGenerator) {
        this.amount = amount;
        this.invoiceGenerator = invoiceGenerator;
    }

    // Delegate invoice generation to the InvoiceGenerator
    public void generateInvoice() {
        invoiceGenerator.generateInvoice(amount);
    }

    public void applyDiscount() {
        System.out.println("Applying discount on invoice: " + amount);
    }
}

How This Follows OCP

Now, the Invoice class follows the Open/Closed Principle because it no longer needs to be modified when we want to introduce new types of invoices. Instead, we simply create a new class that implements the InvoiceGenerator interface.

For example, if we wanted to add a detailed invoice, we could do so without touching the existing code:

java
// New class for generating detailed invoices
public class DetailedInvoice implements InvoiceGenerator {
    @Override
    public void generateInvoice(double amount) {
        System.out.println("Generating detailed invoice with breakdown for amount: " + amount);
    }
}

The existing Invoice class remains unchanged, while we can easily extend the system by adding new invoice types through new classes.

Wrap Up

In this lesson, we saw how the Open/Closed Principle applies to real-world code. Initially, the Invoice class was tightly coupled to the invoice generation logic, forcing us to modify it whenever new functionality was added. By refactoring the code to follow OCP, we made it possible to extend the system by adding new types of invoices without modifying the core logic. This step-by-step approach ensures that our code is flexible, maintainable, and future-proof.

🤖 Don't fully get this? Learn it with Claude

Stuck on Real World Analogies and Code Example? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Real World Analogies and Code Example** (OO & Low-Level Design) and want to truly understand it. Explain Real World Analogies and Code Example from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Real World Analogies and Code Example** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Real World Analogies and Code Example** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Real World Analogies and Code Example** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes