Mini Project 2 — Saving Account¶
Difficulty: Beginner · Estimated effort: 3 hours · Prerequisites: Module 4 — Encapsulation, Module 3 — Constructors and Destructors · Combines: encapsulation, invariant enforcement, constructors, static bookkeeping
No complete solution is provided. Build it from the requirements, then review it against the checklists below.
1. Scenario¶
A savings scheme for a small club needs a program that tracks each member's balance. The rules are simple but absolute: a balance can never go negative, a withdrawal larger than the balance is refused rather than partly applied, and every account belongs to a named member with an account number the club can read over the phone.
The previous semester's version stored balances in a global array and let a withdrawal push one negative. The club noticed three weeks later.
2. Problem statement¶
Build an Account type whose balance is private, whose operations either succeed completely or
change nothing, and whose account numbers are unique by construction rather than by convention.
3. User requirements¶
- [ ] Open an account for a named holder; the club never supplies the number.
- [ ] Deposit and withdraw, with a refusal that says why.
- [ ] Transfer between two accounts without ever borrowing from an account that is not there.
- [ ] Print a statement line: number, holder, balance.
- [ ] See how many accounts currently exist.
4. Functional requirements¶
- [ ] Private balance, holder name and account number.
- [ ]
deposit(amount)rejects a non-positive amount. - [ ]
withdraw(amount)rejects a non-positive amount and an amount above the balance, and leaves the balance untouched in both cases. - [ ]
transferTo(other, amount)leaves both accounts untouched if any part of it fails. - [ ] Account numbers are sequential and unique, generated by the class.
- [ ]
operator<<prints the account.
5. Non-functional requirements¶
- [ ] Money is stored in integer minor units (pence/cents), never
double. - [ ] No public data members and no setters that bypass the rules.
- [ ] Builds warning-free with
g++ -std=c++17 -Wall -Wextra -Werror. - [ ] A failed operation changes nothing that can be observed afterwards.
- [ ] No global balance state; the class is the only thing that touches a balance.
6. Suggested features¶
- A daily withdrawal limit, stored per account, enforced on the same code path.
- An optional overdraft for accounts opened with a limit — and a decision about whether it belongs in this class or in a derived one.
- A frozen flag: transfers out are refused, deposits still allowed.
- A short transaction log of the last n operations, printed with the statement.
7. OOP concepts involved¶
| Concept | Where it appears |
|---|---|
| Encapsulation | The balance is only ever changed through a validating operation |
| Invariant | balance >= 0 holds after every operation, or the type is wrong |
| Constructors | An account cannot be created with a negative opening balance |
| Static members | The next account number, and the live account count |
| Strong guarantee | A failed transfer leaves both accounts unchanged |
| Copy control | Decide deliberately what copying an account means, and write it down |
8. Suggested design considerations¶
Ask what a transfer guarantees. The natural implementation subtracts from one account and adds to the other. If the addition cannot fail, that is fine — but say so. If it can, the order of operations matters and the guarantee has to be designed, not hoped for.
Compile the invariant, do not document it. A private balance plus two validating member functions is stronger than a comment that says "balance must not go below zero".
Static counters are still state. The next account number is shared by every account, so think about what happens when accounts are copied and destroyed, and about whether the count should be per-object or per-program. Both answers can be right; an unexamined one cannot.
Resist the setter. setBalance will be tempting within an hour of starting. Almost every
bug this project is designed to prevent begins with one.
9. Minimum viable version¶
Account with private members, deposit, withdraw, transferTo, balance(), holder(),
number(), and an operator<<. A main that performs a handful of operations, prints the
balance after each, and attempts two operations that must be refused.
10. Intermediate extension¶
Add the transaction log and print it with the statement. Then verify the guarantee under failure: attempt a transfer that must be refused and print both balances afterwards, showing that neither moved.
11. Advanced extension¶
Add a second kind of account — one with an overdraft limit — using inheritance, and make the
withdrawal rule virtual. Then answer in writing: did inheritance remove the if, or just move
it? If it merely moved it, would composition have been the better tool?
12. Testing checklist¶
- [ ] Opening with a negative balance is refused
- [ ] Deposit of
0and of a negative amount are refused - [ ] Withdrawal of exactly the balance succeeds and leaves
0 - [ ] Withdrawal of one pence more is refused and leaves the balance unchanged
- [ ] A refused transfer leaves both balances identical to before
- [ ] A successful transfer changes the two balances by exactly the amount
- [ ] Account numbers are unique and do not repeat after objects are destroyed
- [ ] The printed statement shows minor units with the correct decimal point only at display
13. Documentation checklist¶
- [ ] The invariant written at the top of the header
- [ ] Every public member function documented with what it refuses and what it guarantees
- [ ] The meaning of copying an account written down, with the reason
- [ ] A worked example of a refused transfer in the
READMEor a block comment - [ ] A note on where money is converted to and from display form
14. Self-evaluation rubric¶
| Criterion | Excellent looks like | Weight |
|---|---|---|
| Invariant | Impossible to reach a negative balance from outside, demonstrated by trying | 30% |
| Failure behaviour | Every refusal is complete, tested, and leaves state untouched | 25% |
| Interface | Small, honest, and impossible to misuse accidentally | 20% |
| Money handling | Integer minor units everywhere; conversion only at display | 10% |
| Static members | Unique numbers, with the lifetime question answered | 5% |
| Documentation | Invariant and refusal rules written for a reader who did not write the code | 10% |
Related: Mini projects · Project roadmap · All projects · Module 4 — Encapsulation