Behavioral modeling
Finding changing parts of the design
An extensibility point (a seam) is a place where a rule can change without rewriting the core flow. You name the variation first — “how we choose a spot will change” — then choose a pattern that fits.
Example: you finished park and exit. The interviewer adds airport pricing, EV spots, and a nearest-spot rule. If you already left seams for pricing and allocation, those follow-ups are new classes. If you did not, you edit ParkingLot again.
Workshop
Name the variation in English first
“The rule for picking a free spot will change.” That statement is the design decision. Strategy is a spelling. Say it in the first ten minutes: “I will keep pricing behind an interface because you will change the fee.”
01
Ask what changes
Pricing, allocation, vehicle types, split rules, win rules.
02
Name the seam
A sentence, not a pattern name.
03
Hide it
An interface the rest already talks to.
04
Leave the rest
Do not strategy-pattern the ticket id.
Will change versus furniture
| Will change | Seam | Later spelling |
|---|---|---|
| Fee formula | PricingStrategy | Strategy |
| Which free spot | AllocationStrategy | Strategy |
| Car vs bike vs EV | Vehicle / Spot type | Factory + type |
| Equal vs percent split | SplitPolicy | Strategy |
Will not change yet — keep it simple
Only then pick a tool
Strategy is the usual tool for a swapping algorithm. Factory is the usual tool for “which subclass.” A new vehicle type should not reopen the lot; it should land as a type plus a factory mapping. You do not abstract clocks, ticket ids, and floor numbers on day one. Those are not the follow-up.
The mistake is abstracting everything, or abstracting nothing. says do not invent a plugin bus. Protected Variations says do not bury the one thing that will move inside a switch on ParkingLot. The interview is almost always testing whether you can point at the moving part before you name a pattern.
Pockets still apply. Strategy is a pocket from responsibility assignment. Factory is a pocket. The controller stays thin. The entity still refuses CLOSED → ACTIVE. Extensibility does not move invariants into a plugin.
The lot stays simple
ParkingLot asks allocation.choose and pricing.fee. It does not switch on AIRPORT. A factory may pick the implementation once. Tests pass a fake that always returns spot 1. That is the seam working.
interface SpotAllocationStrategy {
Optional<Spot> choose(List<Spot> free, Vehicle vehicle);
}
interface PricingStrategy {
Money fee(Ticket ticket, Instant now);
}
class ParkingLot {
private final SpotAllocationStrategy allocation;
private final PricingStrategy pricing;
ParkingLot(SpotAllocationStrategy allocation, PricingStrategy pricing) {
this.allocation = allocation;
this.pricing = pricing;
}
}
class SpotFactory {
Spot create(SpotType type, SpotId id) {
return switch (type) {
case COMPACT -> new CompactSpot(id);
case EV -> new EvSpot(id);
};
}
}
// Airport pricing is a new PricingStrategy.
// Nearest spot is a new AllocationStrategy.
// ParkingLot does not open. Ticket’s machine does not change.Good sentence
“Pricing will change, so ParkingLot will not own the formula. I will keep a PricingStrategy.” Pattern name comes second.
Cargo cult
“I will use Strategy” with no seam. Then TicketIdFactory, FloorStrategy, and AbstractGate hide the one follow-up that mattered.
Other problems, same habit
- Splitwise: how an expense splits — equal, percent, exact. That is the seam. The group object stays boring.
- Tic-tac-toe / chess: how a player chooses a move, or what counts as a win. Do not strategy-pattern the square index.
- Ticket machine: not a seam. CREATED → ACTIVE → PAID → CLOSED is an invariant. Do not make StatusStrategy so a retry can invent CLOSED → ACTIVE.
What you do not hide
Inject a Clock so tests are deterministic — that is a dependency, not an extensibility point. See deterministic design. Do not wrap it in ClockStrategy. Ticket ids can be a function. Floor numbers are furniture. Open-Closed is for the moving part, not for every noun.
Common mistakes
Saying “I will use Strategy” before naming the seam
Strategy is a spelling. The design sentence is “pricing will change, so ParkingLot will not own the formula.” Pattern names without that design statement are empty pattern name-dropping. Read Strategy after the sentence exists.
Interfaces for things that will not move
A TicketIdFactory, FloorStrategy, and AbstractGate on a short parking-lot interview hide the one seam that mattered. Abstract the follow-up, not the furniture.
Making the state machine a strategy
Illegal transitions are not a variation point. CLOSED → ACTIVE must stay impossible when airport pricing arrives. Extensibility is for fees and stalls, not for resurrecting tickets.
First-ten-minutes script
“Use cases are park and exit. I will keep pricing and allocation behind interfaces because those are the usual follow-ups. Ticket is a small machine: CREATED, ACTIVE, PAID, CLOSED — that is not a strategy.” Then draw the collaboration. The pattern pages wait.
Clock is a dependency, not a seam
Pass a Clock so fee(ticket, now) is testable. That is injection. It is not PricingStrategy. Do not invent ClockStrategy, InstantFactory, and AbstractNow. The follow-up is airport pricing, not a new way to read the time. Keep the moving algorithms behind interfaces; keep time as a value you pass in.
Read next
Takeaway
Name the variation before choosing a pattern. Pricing, allocation, vehicle types — those are seams. Ticket ids and floor numbers are furniture. Strategy and Factory are spellings. The machine CREATED → ACTIVE → PAID → CLOSED is an invariant, not an extensibility point.