Intermediate Project 6 — Vending Machine¶
Difficulty: Intermediate · Estimated effort: 10–14 hours · Prerequisites: Module 13 — Exception Handling, Module 12 — Templates, Module 14 — The STL with OOP · Combines: a state machine, refusals on the failure path, a stock model, a menu loaded from a file
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. The states are described as behaviour; naming them and deciding what may happen in each is the exercise.
1. Scenario¶
A vending machine takes coins, dispenses a product when enough credit has been inserted, and returns change. It must refuse what it cannot do — a product that is sold out, an amount above what it can give change for, a coin it does not accept — and it must never take money without either dispensing a product or returning the money.
The machine's control program is also its test suite: a script of coin inserts and button presses must produce exactly one documented outcome each.
2. Problem statement¶
Model the machine as a state machine with explicit states and transitions, a stock of products, a coin store used for change, and a refusal for every impossible request. Change-making must respect the coins actually in the machine, not the coins the ideal machine would have.
3. User requirements¶
- [ ] Insert coins and see the current credit.
- [ ] Select a product; it dispenses if credit suffices and it is in stock.
- [ ] Receive change in coins the machine actually holds.
- [ ] Request a refund of the current credit at any time before dispensing.
- [ ] See an out-of-service message when the machine cannot give change for any purchase.
- [ ] Load a menu and initial stock from a file; save the closing state.
4. Functional requirements¶
- [ ] Explicit states, at minimum: idle, has credit, dispensing, out of service.
- [ ] Transitions that are only legal from particular states, enforced rather than assumed.
- [ ] A
Productwith a price, and a stock level that cannot go negative. - [ ] A coin store counted per denomination, mutated only by the machine's own operations.
- [ ] Change computed greedily with the available coins, and a documented fallback when greedy fails.
- [ ] Refusals as distinct exception types: sold out, insufficient credit, cannot make change, invalid coin.
- [ ] Every operation is a transaction: either it happens completely or nothing changed.
5. Non-functional requirements¶
- [ ] The credit and the coins inserted but not yet committed are accounted for separately, or the design says why they are the same value.
- [ ] Total money in the machine is conserved: insert + load = dispensed value + change + held.
- [ ] Illegal transitions are impossible to perform, not merely detected.
- [ ] A refused purchase leaves credit, stock and the coin store all unchanged.
- [ ] Builds warning-free with
g++ -std=c++17 -Wall -Wextra -Werror.
6. Suggested features¶
- A restocking mode with an authorization code, and a note on whether that belongs in the same class.
- A sales log with a daily summary.
- A
CoinCountertemplate used for both coins and notes, if it earns its generality. - A price-rise operation applied to a whole category at once, with the refusal amount recomputed.
7. OOP concepts involved¶
| Concept | Required | Where it appears |
|---|---|---|
| Multiple classes | yes | Machine, Product, CoinStore, state representation, Menu, error types |
| Relationships | yes | Machine ◆ menu and coin store; sale ──> product (a record of a completed act) |
| Encapsulation | yes | Stock, credit and coin counts change only through the machine's own operations; conservation holds |
| Inheritance or composition | yes | States as objects is one honest answer; an enum plus guard functions is another — argue for yours |
| Polymorphism | yes | A Dispenser or state interface with at least two concrete behaviours, or an equally real use of dispatch |
| The STL | yes | std::map keyed by denomination, std::vector for the sales log, algorithms for change |
| Exception handling | yes | Five refusal types, each rolled back and tested |
8. Suggested design considerations¶
Try "states as objects" once. A MachineState interface with Idle, HasCredit and
OutOfService implementations removes the switch and makes "which operation is legal now" a
property of the type. It is more code; decide whether it is clearer, and say so either way.
Money conservation is your invariant. Write it down as a formula — loaded + inserted ==
dispensed_value + change_given + still_held — and check it in the tests after every
operation, including refusals. It catches nearly every bug in this project.
Change-making is a real algorithm. Greedy fails for some coin systems. Either prove your coin set is safe for greedy (many are, and the proof is short), or implement the fallback and document it. Do not simply assume.
Decide what "refund" means mid-dispense. If the machine is jammed between committing credit and releasing the product, what happens? A one-paragraph answer in the report is enough; silence is not.
9. Minimum viable version¶
Products with stock, a coin store, insert/select/refund working, a price check, and one refusal type per failure. State handling may be a simple enum at this stage — but documented.
10. Intermediate extension¶
Add change-making with a documented fallback, the out-of-service state, and the conservation test that runs after every operation in a scripted session. Then induce the sold-out and cannot-make-change refusals deliberately and confirm the conservation formula still holds.
11. Advanced extension¶
Convert the state handling to state objects, or justify in writing why you kept the enum — with
the line count of the switch as evidence. Then add the sales log and the restocking mode and
count the files each change touched.
12. Testing checklist¶
- [ ] Exact payment dispenses and leaves credit at zero
- [ ] Overpayment returns the correct change from the coins available
- [ ] Selecting a sold-out product is refused, and credit is preserved or refunded as documented
- [ ] Insufficient credit is refused with the shortfall in the message
- [ ] Change that the machine cannot make is refused before the product dispenses
- [ ] Conservation holds after every operation in the scripted session, refusals included
- [ ] A refund returns exactly the credit to the coin store and the customer
- [ ] Stock never goes below zero under any scripted sequence
- [ ] Reloading a saved state reproduces stock, coin counts and prices exactly
- [ ] An invalid coin is rejected without changing anything
13. Documentation checklist¶
- [ ] A state table: state, legal operations, resulting state
- [ ] The money conservation invariant, as a formula
- [ ] The change-making rule and its justification or fallback
- [ ] The menu and stock file format
- [ ] The scripted session, recorded as evidence, with the observed output
14. Self-evaluation rubric¶
| Criterion | Excellent looks like | Weight |
|---|---|---|
| State machine | Explicit states and transitions; illegal operations impossible or cleanly refused | 25% |
| Invariants | Money conserved and stock non-negative, checked after every operation | 25% |
| Transactional behaviour | Every refusal rolls back completely and is tested | 20% |
| Change algorithm | Correct for the coin set, with the reasoning or the fallback documented | 10% |
| STL use | Containers and algorithms chosen with reasons | 10% |
| Documentation | State table, invariant and evidence written down | 10% |
Related: Intermediate projects · Project roadmap · All projects · Module 13 — Exception Handling