Creational PatternsBuilder PatternMedium⏱️ ~2 min

Builder Pattern: Parking Lot Ticket Application

Let's apply Builder to design a ParkingTicket in a parking lot system. Tickets have many attributes: some required (ticket ID, vehicle plate, entry time), some optional (assigned spot, parking attendant, special permit, discount code).
ParkingTicket
- ticketId: String
- vehiclePlate: String
- entryTime: DateTime
- assignedSpot: ParkingSpot
- attendantId: String
- specialPermit: Permit
- discountCode: String
- ParkingTicket(builder)
+ getTicketId(): String
+ calculateFee(exitTime): Money
+ getAssignedSpot(): ParkingSpot
ParkingTicket.Builder
- ticketId: String
- vehiclePlate: String
- entryTime: DateTime
- assignedSpot: ParkingSpot
- attendantId: String
- specialPermit: Permit
- discountCode: String
+ Builder(id, plate, time)
+ assignSpot(spot): Builder
+ setAttendant(id): Builder
+ applyPermit(permit): Builder
+ applyDiscount(code): Builder
+ build(): ParkingTicket
Design Walkthrough:

Required Parameters in Constructor:
The builder constructor enforces ticketId, vehiclePlate, and entryTime because every ticket must have these. This prevents the system from creating invalid tickets.

Optional Parameters via Fluent Methods:
Not all vehicles get assigned spots immediately (valet parking might assign later). Not all entries are handled by attendants (automated entry gates). Special permits and discounts are rare cases. Each optional attribute has a setter method returning this.

Validation in build():
The build() method performs cross-field validation. For example, if a discountCode is applied, verify it is valid for the vehicle type. If a specialPermit is set, ensure the assigned spot allows permit parking. Only after validation passes does build() call the private ParkingTicket constructor.
Usage Example:

ticket = ParkingTicket.Builder("T12345", "ABC-1234", currentTime)
  .assignSpot(spotA5)
  .setAttendant("ATD-007")
  .applyPermit(disabledPermit)
  .build()

// Minimal ticket for automated entry
autoTicket = ParkingTicket.Builder("T12346", "XYZ-9876", currentTime)
  .build()
Why Builder Fits Here:

First, Tickets have clear required vs optional distinction. Entry cannot happen without ID, plate, and time, but spot assignment can be deferred.

Second, Validation depends on combinations. Discount eligibility might depend on permit type or vehicle category, which is complex to validate in a constructor.

Third, Immutability is critical. Once issued, a ticket's core attributes should not change (though you might add an exit time later through a separate method, but builder-set fields stay fixed).

Fourth, Readability matters in domain models. applyPermit(permit) is far clearer than setPermit(permit) or a constructor with eight parameters.
Interview Tip: When asked to design a ticket system, explicitly mention: "I will use Builder because tickets have required fields (ID, plate, entry time) and optional fields (spot, attendant, permit). This avoids telescoping constructors and ensures immutability." Then draw the class diagram showing the nested builder.
💡 Key Takeaways
Required ticket attributes (ID, plate, time) go in builder constructor
Optional attributes (spot, attendant, permit) set via fluent methods
Validation happens in build() checking cross-field business rules
Immutable ticket prevents modification after issuance
Nested builder accesses private ticket constructor for encapsulation
📌 Examples
1Valet parking assigns spot after ticket creation
2Automated entry creates tickets without attendant ID
3Disabled permit requires validation of spot accessibility
← Back to Builder Pattern Overview
Builder Pattern: Parking Lot Ticket Application | Builder Pattern - System Overflow