Behavioral modeling
Object collaboration
Collaboration is the conversation that finishes one use case: who asks, who decides, who records, and who must stay out. The class diagram is the cast list; collaboration is the scene.
Example: you have Lot, Spot, Ticket, AllocationStrategy, and TicketRepository. The interviewer asks, “Walk me through park.” Naming the types is not enough — you must say who calls whom, in what order, and who is not allowed to speak during park.
Cast list
A is Lot, Spot, Ticket, Strategy, Repository. Useful. Incomplete. The interviewer still cannot see time or permission.
The scene
Controller asks the lot to allocate. Lot asks the strategy to choose. Chosen spot occupies itself. Factory or lot issues a ticket. Repository saves. That is park.
The script for park
Each should go to a close collaborator. The controller may know the lot and the repository. The lot may know its floors and the allocation strategy. The ticket must not know SQL. The strategy must not occupy spots — it only chooses. The repository must not calculate fees.
- 01 Controller
Lot.allocate(vehicle)
Use-case door starts the scene.
- 02 Lot
Strategy.choose(freeSpots, vehicle)
Lot knows floors; strategy only picks.
- 03 Strategy
returns a Spot — does not occupy
Choosing is not taking.
- 04 Lot
Spot.occupy()
Occupancy is the entity’s invariant.
- 05 Lot / Factory
issue Ticket (CREATED → ACTIVE)
Birth happens with the stall.
- 06 Controller
Repository.save(ticket)
Memory is not the lot’s job.
ASCII — who speaks
park(Vehicle) Controller → Lot.allocate(vehicle) Lot → Strategy.choose(freeSpots, vehicle) Strategy → (returns a Spot, does not occupy) Lot → Spot.occupy() Lot → Factory / itself issue Ticket Controller → Repository.save(ticket) Not in this conversation: PricingStrategy, Emailer, GST.
Keep-out is a design decision
Also name the people who are not in the room. That is as important as the arrows. Pricing does not speak during park. SMS does not speak during park. The floor does not print a receipt. Leaving someone out keeps park from becoming “everything that happens in a parking lot.”
If two objects both think they occupy the spot, you do not have a collaboration problem — you have a responsibility problem. Fix the home of occupy first, then redraw who calls it. Collaboration cannot repair a verb that lives in two pockets.
The same scene in Java
Notice who is missing: no PricingStrategy field, no mailer. Ticket is created after occupy succeeds. Repository is the last speaker. That match between script and code is the point of this page. The clock version lives on sequence diagrams.
class ParkingService {
private final ParkingLot lot;
private final TicketRepository tickets;
Ticket park(Vehicle vehicle) {
Spot spot = lot.allocate(vehicle); // lot talks to strategy + spot
Ticket ticket = Ticket.issue(spot, vehicle);
ticket.activate(); // CREATED → ACTIVE
tickets.save(ticket); // repository records
return ticket;
}
}
class ParkingLot {
private final SpotAllocationStrategy allocation;
Spot allocate(Vehicle vehicle) {
Spot spot = allocation.choose(freeSpots(), vehicle)
.orElseThrow(LotFull::new);
spot.occupy(vehicle); // entity, not strategy
return spot;
}
}
// Ticket.save() must not exist. Persistence is the repository’s line.When the conversation goes off-script
When an object reaches through three getters to do someone else’s job, the conversation has gone off-script. That is the Law of Demeter: talk to close friends, not to friends of friends. lot.getFloors().get(0).getSpots().get(3).occupy() means the controller walked into the building and sat in a stall. Ask the lot.
Ticket that talks to the database is the same smell with a different costume. Ticket collaborates with its own status and times. Persistence is the repository’s line. If Ticket.save() exists, the conversation has the wrong speaker.
Who asks
Controller starts park. Lot starts choose and occupy.
Who decides
Strategy picks a stall. Spot accepts or refuses occupy.
Who records
Repository saves the ACTIVE ticket. Nobody else writes rows.
Exit is a different scene
Do not reuse the park cast for exit and pretend it is the same conversation. Pricing now speaks. Occupy does not. Ticket moves PAID → CLOSED. A second scene, a second keep-out list (allocation stays home).
exit(TicketId) Controller → Repository.get(id) Controller → PricingStrategy.fee(ticket, now) Controller → Ticket.pay(fee) // ACTIVE → PAID Controller → Lot.release(spotId) Controller → Ticket.close() // PAID → CLOSED Controller → Repository.save(ticket) Not in this conversation: AllocationStrategy. Illegal: any speaker assigning CLOSED → ACTIVE.
Common mistakes
A class list instead of a conversation
“I have Lot, Ticket, Strategy” is casting. Collaboration is the scene: who calls occupy, who saves, who never hears about park.
Ticket that talks to the database
Ticket collaborates with its own status and times. Persistence is the repository’s line. If Ticket.save() exists, the conversation has the wrong speaker.
One conversation for the whole product
Park and exit share types. They do not share a script. Draw two scenes. The sequence page will put a clock on each.
Disable is a third scene
Admin disable(SpotId) is not park with a different actor. The lot asks the spot whether it is free. If a car is there, refuse. No ticket is issued. Pricing never enters. Repository may record that the stall is out of service, but Ticket is not in the room. Three use cases, three conversations, one cast list.
disable(SpotId) Controller → Lot.disable(id) Lot → Spot.ensureEmpty() Lot → Spot.markOutOfService() Controller → (optional) audit log Not in this conversation: Ticket, PricingStrategy, AllocationStrategy.
Close collaborators
Controller knows Lot and Repository. Lot knows Strategy and Spot. Ticket knows its status. That is the whole acquaintance graph for park.
Strangers
Ticket does not know Floor. Strategy does not know TicketRepository. Spot does not know Receipt. If those imports appear, the scene hired extras.
Read next
Takeaway
Collaboration is park as a conversation: who asks, who decides, who records, who stays out. A cast list is not a design. Two objects occupying the same stall is a responsibility bug, not a missing arrow. Draw the keep-out list on purpose.