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.
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:
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:
// 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:
// 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:
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:
// 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.
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.
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.
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.
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.