Intermediate Project 5 — Car Park Ticket System¶
Difficulty: Intermediate · Estimated effort: 12–18 hours · Prerequisites: Module 9 — Composition and Association, Module 13 — Exception Handling, Module 15 — SOLID and Design Patterns · Combines: occupancy as an invariant, a pricing policy that varies, time as an injected value, persistence
This is one of the designated integration projects. It must use, explicitly and in writing: multiple classes · relationships between classes · encapsulation · inheritance or composition · polymorphism · the STL · exception handling.
No complete solution is provided. In particular the charging rules are stated as behaviour, and turning them into a design is the exercise.
1. Scenario¶
A car park has several floors, each with numbered spaces and a mix of sizes: standard, large and twin (two small cars in one space). A driver arrives, is issued a ticket with an entry time, parks if a suitable space is free, and pays on exit according to a rate card that changes by day, by duration band and by season ticket.
The previous version charged with if chains over the floor number, which stopped being
maintainable the second time the rate card changed.
2. Problem statement¶
Build the car park as a system: spaces with sizes, tickets with an issued time and a space, entry and exit operations that keep occupancy exact, and a charging policy that is data-driven and replaceable. The clock is injected, so a full day of parking can be simulated in milliseconds.
3. User requirements¶
- [ ] Issue a ticket to a vehicle when a suitable space exists, and refuse with a reason when none does.
- [ ] Record the exit, compute the charge and free the space.
- [ ] See the occupancy of each floor and of the whole car park.
- [ ] See the charges for a set of simulated days, as a report.
- [ ] Have a season-ticket holder charged differently without editing the parking operations.
- [ ] Save and load the current occupancy.
4. Functional requirements¶
- [ ]
Spacewith an identifier, a floor and a size. - [ ]
Ticketissued at a given time to a given vehicle for a given space, with no exit yet. - [ ]
CarParkowning spaces, withenter()andexit()operations. - [ ] A
RateCardabstraction with at least two implementations: hourly and season. - [ ] Durations computed from injected
TimePointvalues, never from a clock read inside the domain. - [ ] Money in integer minor units, with a documented rounding rule for part-hours.
- [ ] Refusals as typed exceptions: no space, wrong size, unknown ticket, ticket already closed.
5. Non-functional requirements¶
- [ ] Occupancy matches the open tickets exactly, at every point in a simulated day — verified after every operation in a scripted sequence.
- [ ] A refused entry leaves occupancy unchanged and issues no ticket.
- [ ] Charging rules contain no reference to floor numbers or vehicle registration patterns.
- [ ] Loading a file that refers to an unknown space is rejected, not silently repaired.
- [ ] Builds warning-free with
g++ -std=c++17 -Wall -Wextra -Werror.
6. Suggested features¶
- A daily takings report with a histogram of stay lengths.
- Reservations, and how they interact with arrival order.
- A lost-ticket charge, and a policy for the maximum chargeable stay.
- An interface plus a second implementation for the allocation rule (nearest space, cheapest size), so allocation is a policy too.
7. OOP concepts involved¶
| Concept | Required | Where it appears |
|---|---|---|
| Multiple classes | yes | Space, Ticket, Vehicle, CarPark, rate cards, clock, error types, reports |
| Relationships | yes | Car park ◆ spaces; ticket ──> space and vehicle; policy injected, not owned |
| Encapsulation | yes | A space's occupancy is derived from open tickets and cannot be set directly |
| Inheritance or composition | yes | Composed rate card behind an interface; inheritance only if it genuinely removes work |
| Polymorphism | yes | Charging and allocation dispatch through interfaces; no branches on rate type |
| The STL | yes | std::map from space id to state, std::vector of tickets, algorithms for reports |
| Exception handling | yes | Four distinct refusal types, each tested, none of them a return code |
8. Suggested design considerations¶
One representation of occupancy. A space is occupied because an open ticket refers to it, or because a flag says so — never both. Two answers to the same question is the invariant bug this domain invites; choose one, and show the other cannot be reached.
Inject the clock. enter(ticket, time) and exit(ticket, time) take time as a parameter.
This is what makes the whole system testable: a year of parking fits in a test that runs in
milliseconds.
Rate cards are data plus a rule. A duration-band table loaded from a file, plus a small amount of code for "which band applies", beats a class per pricing scenario. Say in the report where the line between data and code falls for you.
Refuse with a type, not a message. NoSuitableSpace, UnknownTicket, AlreadyClosed and
WrongSize are four different situations, and a caller may want to treat them differently.
Return codes force the caller to switch on an integer and forget one case.
9. Minimum viable version¶
Spaces, tickets and a car park that can enter and exit, occupancy queries, and one hourly rate
card with a documented part-hour rule. Driven by a main with a scripted day.
10. Intermediate extension¶
Add the season rate card and a simulated week: 200 arrivals and departures with deterministic pseudo-random times, printing the takings and the occupancy profile. Verify that occupancy after the last exit is zero, and that the takings equal the sum of the individual charges.
11. Advanced extension¶
Make allocation a policy as well as pricing, add persistence of current occupancy, and then add a new vehicle size across both policies. Count the files each change touched, and state in one paragraph whether the policies earned their interfaces.
12. Testing checklist¶
- [ ] Entering with no free suitable space is refused and changes nothing
- [ ] Entering a large vehicle into a standard space is refused with a size error
- [ ] After every operation in the scripted day, open tickets equal occupied spaces
- [ ] Exiting frees exactly the space the ticket names, not the first free one
- [ ] Exiting an already-closed ticket is refused, and the charge is not applied twice
- [ ] A zero-length stay is charged as documented (free or minimum charge — pick one)
- [ ] One second over a band boundary falls in the higher band, per the documented rule
- [ ] A season-ticket holder's charge differs from an hourly stay of the same duration
- [ ] Unknown-ticket exit names the ticket in the message
- [ ] Loading occupancy that references an unknown space is rejected
- [ ] The takings report equals the sum of the charges in the log
13. Documentation checklist¶
- [ ] The rate card written as a specification, before the code
- [ ] The occupancy invariant, and the single place that maintains it
- [ ] The money and rounding rules
- [ ] The class diagram with the policies shown as injected dependencies
- [ ] The scripted day recorded as evidence: start time, operations, expected occupancy
14. Self-evaluation rubric¶
| Criterion | Excellent looks like | Weight |
|---|---|---|
| Invariant | Occupancy provably equals open tickets after every operation | 25% |
| Policy design | Pricing and allocation are injected, data-driven and swappable | 25% |
| Testability | Time injected; a week simulated deterministically | 15% |
| Failure design | Four typed refusals, each tested, each leaving state untouched | 15% |
| Money handling | Integer minor units; one documented rounding rule | 10% |
| Documentation | Rate card specification, invariant and evidence written down | 10% |
Related: Intermediate projects · Project roadmap · All projects · Module 15 — SOLID and Design Patterns