Topic 3.4
Extensibility
In one line
Design so a new variation — payment method, vehicle type, pricing rule, storage, channel — does not rewrite the system.
Think of it like this
A phone with a charging port. When a new charger comes out, you don't open up the phone; you just plug it in. Good design leaves 'ports' where new things can plug in.
Key ideas
- 01
The five classic extension axes: new payment method, new vehicle type, new notification channel, new pricing rule, new storage implementation.
- 02
Each axis maps to a seam: PaymentGateway interface, VehicleType enum + factory, Channel strategy, PricePolicy strategy, PersistencePort interface.
- 03
Rule of thumb: variation identified → abstract it. Unknown variation → do not pre-abstract (YAGNI).
- 04
Demonstrating extension in an interview: 'if they add a sixth payment type, I add one class + one registry entry'.
- 05
Extensibility without tests is just hope — every seam should be unit-testable with a fake implementation.
Java / Spring map
- →
Spring: new implementation = new @Component, registration = map/qualifier entry. No editing of core flow.
Code & diagrams
Spring collects every implementation automatically. A new payment method is one new class and zero edits.
public interface PaymentMethod {
String code(); // "UPI", "CARD", "CASH"
PaymentResult pay(Money amount, PaymentDetails d);
}
@Component class UpiPayment implements PaymentMethod { public String code() { return "UPI"; } /* ... */ }
@Component class CardPayment implements PaymentMethod { public String code() { return "CARD"; } /* ... */ }
@Service
public class PaymentRouter {
private final Map<String, PaymentMethod> byCode;
// Spring injects ALL PaymentMethod beans into this list.
public PaymentRouter(List<PaymentMethod> methods) {
this.byCode = methods.stream()
.collect(Collectors.toUnmodifiableMap(PaymentMethod::code, m -> m));
}
public PaymentResult pay(String code, Money amount, PaymentDetails d) {
PaymentMethod m = byCode.get(code);
if (m == null) throw new UnsupportedPaymentException(code);
return m.pay(amount, d);
}
}
// Tomorrow: add @Component class WalletPayment implements PaymentMethod { ... }
// PaymentRouter, controllers, and tests stay untouched. ← Open/Closed in practiceExplain without notes
For 'new vehicle type appears', walk the exact files that change in your parking lot design.
Practice
Add 'EV charging pricing surcharge' as a decorator/strategy to your pricing model.
Trade-offs
- ↔
Premature abstraction adds indirection everywhere; extend on the second concrete need.
Completion checklist
I can name the seam for each of the five extension axes and point at the class that opens.