Context:SOLID · the O · add features by writing new classes, not editing old ones · powered by abstraction + polymorphismProblem it solves: a growing if/instanceof ladder that you edit every time a new type is added — replace it with polymorphic dispatch.
Quick Revision
🎯 Trigger: you must edit existing code to add a new case ➔ violates OCP; push the behaviour behind an abstract method.
⚡ Key Constraint: “closed for modification” means the existing, tested class shouldn’t change; “open for extension” means new behaviour arrives as new subclasses.
🔧 Minimal Working Example
// SMELL: Player.consumeAllItems edits an if-ladder for every new item typepublic void consumeAllItems() { for (ConsumableItem item : inventory) { if (item instanceof Apple) healthPoint += 50; else if (item instanceof HealthPotion) healthPoint += 1000; else if (item instanceof CarolinaReaper) healthPoint -= 500; // add Burger? edit here again }}// FIX: let each item know how it affects the playerpublic abstract class ConsumableItem { public abstract void consumedBy(Player player); }public class HealthPotion extends ConsumableItem { @Override public void consumedBy(Player p) { p.setHealthPoint(p.getHealthPoint() + 1000); }}// Player no longer changes:public void consumeAllItems() { for (ConsumableItem item : inventory) item.consumedBy(this); // polymorphic dispatch}
Expected output: adding Burger/Potato = new class only; Player stays untouched.
Abstraction is the hinge ➔ an abstract method (or interface) defines the extension point.
New type = new class ➔ implement the method; no edit to the consuming loop.
Kills the instanceof ladder ➔ dispatch replaces conditional type checks.
🏛️ Structure
classDiagram
class ConsumableItem {
<<abstract>>
+consumedBy(Player p)* void
}
class Apple { +consumedBy(Player p) void }
class HealthPotion { +consumedBy(Player p) void }
class CarolinaReaper { +consumedBy(Player p) void }
class Player { +consumeAllItems() void }
ConsumableItem <|-- Apple
ConsumableItem <|-- HealthPotion
ConsumableItem <|-- CarolinaReaper
Player --> "*" ConsumableItem : inventory
✍️ Practice
Practice 1: A Renderer has if (shape instanceof Circle) ... else if (shape instanceof Square) ... to compute area. Refactor to obey OCP.