Command Palette

Search for a command to run...

PHASE 3Intermediate ~7 min· topic 4 of 7

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.

0/7 · 0%

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

  1. 01

    The five classic extension axes: new payment method, new vehicle type, new notification channel, new pricing rule, new storage implementation.

  2. 02

    Each axis maps to a seam: PaymentGateway interface, VehicleType enum + factory, Channel strategy, PricePolicy strategy, PersistencePort interface.

  3. 03

    Rule of thumb: variation identified → abstract it. Unknown variation → do not pre-abstract (YAGNI).

  4. 04

    Demonstrating extension in an interview: 'if they add a sixth payment type, I add one class + one registry entry'.

  5. 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

PluginRegistry.javajava

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 practice

Explain without notes

01

For 'new vehicle type appears', walk the exact files that change in your parking lot design.

Practice

01

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.

Back to phase