Open / Closed Principle (OCP)
Software entities should be open for extension, but closed for modification.
Add new behavior by extending — not by editing working code.
Problem
Every new discount tier requires touching (and potentially breaking) the existing switch statement:
// ❌ Closed for extension, open to breakage
class DiscountCalculator {
get(price: number, tier: string) {
switch (tier) {
case 'silver': return price * 0.95;
case 'gold': return price * 0.9;
case 'platinum': return price * 0.85;
default: return price;
}
}
}
Solution
Define a strategy interface; add new tiers by adding new classes:
interface DiscountStrategy {
apply(price: number): number;
}
class SilverDiscount implements DiscountStrategy {
apply(price: number) { return price * 0.95; }
}
class GoldDiscount implements DiscountStrategy {
apply(price: number) { return price * 0.9; }
}
// Adding platinum requires ZERO changes to existing code:
class PlatinumDiscount implements DiscountStrategy {
apply(price: number) { return price * 0.85; }
}
class Checkout {
constructor(private strategy: DiscountStrategy) {}
total(price: number) { return this.strategy.apply(price); }
}
const checkout = new Checkout(new GoldDiscount());
console.log(checkout.total(100)); // 90
Key Insight
Modification is dangerous, extension is safe. In production systems, touching working code is the #1 source of regressions.