Watch it extend
The base platform stays locked. Press Add Extension to plug in new discount types without touching the base class.
Code example
Discount logic with a switch vs. an extensible interface. Toggle to compare languages.
// ? BAD — must modify DiscountCalculator for every new type
public class DiscountCalculator
{
public decimal Calculate(Customer c, decimal price)
{
if (c.Type == "Gold") return price * 0.80m;
if (c.Type == "Silver") return price * 0.90m;
// Adding "Platinum"? Edit this class. Risk breaking Gold/Silver.
return price;
}
}
// ? GOOD — extend by adding new classes, base never changes
public interface IDiscount
{
decimal Apply(decimal price);
}
public class GoldDiscount : IDiscount
{
public decimal Apply(decimal p) => p * 0.80m;
}
public class SilverDiscount : IDiscount
{
public decimal Apply(decimal p) => p * 0.90m;
}
// Adding Platinum? Just add a new class — zero edits to existing code.
public class PlatinumDiscount : IDiscount
{
public decimal Apply(decimal p) => p * 0.70m;
}
public class DiscountCalculator // closed for modification
{
public decimal Calculate(IDiscount d, decimal price) => d.Apply(price);
}
// ? BAD — every new type requires editing calculate()
function calculate(customer, price) {
if (customer.type === 'gold') return price * 0.80;
if (customer.type === 'silver') return price * 0.90;
return price; // edit this every time
}
// ? GOOD — strategy pattern; base function never changes
class GoldDiscount { apply(p) { return p * 0.80; } }
class SilverDiscount { apply(p) { return p * 0.90; } }
class PlatinumDiscount { apply(p) { return p * 0.70; } }
class DiscountCalculator {
calculate(discount, price) {
return discount.apply(price); // closed — never changes
}
}
// Usage — plug in any strategy, no edits required
const calc = new DiscountCalculator();
calc.calculate(new GoldDiscount(), 100); // 80
calc.calculate(new PlatinumDiscount(), 100); // 70
Key concepts
Open for Extension
New behaviour can be added by writing new code — a new class, a new strategy, a new plugin — without touching what already works.
Closed for Modification
Existing, tested code is sealed. Edits risk regressions. Abstractions protect the stable core from change.
Abstraction
An interface or abstract class defines the contract. Concrete classes implement it. The consumer only knows the contract — never the implementation.
Strategy Pattern
Define a family of algorithms behind an interface. Swap them at runtime without changing the context class. A natural consequence of OCP.