ooad1-12 Exercise 3: Reviewing the Capstone Design ======================================================== ex3/ex3.py (needs smells.py and the workshops package from the chapter folder; run from there): """Exercise 3: review the capstone with chapter 11's checklist and back each finding with evidence. Run from the chapter folder: py ex3/ex3.py""" import pathlib import re import sys from datetime import date HERE = pathlib.Path(__file__).resolve().parent.parent sys.path.insert(0, str(HERE)) import smells # noqa: E402 from workshops.domain import Session # noqa: E402 print("1. the smell detector on the domain and application files:") for rel in ("workshops/domain/session.py", "workshops/domain/booking.py", "workshops/application/desk.py"): for line in smells.analyse(str(HERE / rel)) or ["(nothing flagged)"]: print(f" {rel}: {line}") print() desk_text = (HERE / "workshops/application/desk.py").read_text(encoding="utf8") writes = re.findall(r"^\s*(booking\.(?:receipt|paid_pence)) = ", desk_text, re.M) print(f"2. fields of a Booking that the DESK sets from outside: {len(writes)} assignments ({sorted(set(writes))})") print() s = Session("S1", "Pottery", date(2026, 12, 1), 2, 2500) b = s.open_booking("ann", date(2026, 11, 1)) # Requested: a payment is in flight s.cancel_session() print(f"3. the session is cancelled while a booking is still Requested: the booking is {b.state}, and it still holds a seat: {b.holds_a_seat()}") print() ok = Session("S2", "Coding", date(2026, 12, 1), 1, 100) try: ok.open_booking("ann", date(2026, 12, 1)) except Exception as e: print(f"4. booking on the day of the session: {e}") Run with: py ex3/ex3.py Output: 1. the smell detector on the domain and application files: workshops/domain/session.py: (nothing flagged) workshops/domain/booking.py: ANEMIC? 1 of 4 classes hold only data (Transition); Booking reads or writes their fields 6 times from outside workshops/application/desk.py: (nothing flagged) 2. fields of a Booking that the DESK sets from outside: 4 assignments (['booking.paid_pence', 'booking.receipt']) 3. the session is cancelled while a booking is still Requested: the booking is Requested, and it still holds a seat: True 4. booking on the day of the session: this session has already started FOUR FINDINGS, WITH THEIR EVIDENCE 1. REPORTED BY THE DETECTOR, AND WRONG: 'anemic?' on Transition (output item 1). Transition is one row of the state table, a frozen record, data by design. The detector counts classes without methods and cannot know that. A false positive to dismiss, with a reason. 2. MISSED BY THE DETECTOR, AND REAL: the desk sets two fields of a Booking from outside, four times (output item 2). The detector looks for reads and writes of data-only classes, but Booking has methods, so it was not suspected. A mark_paid(receipt, pence, today) on the booking would put the rule with the data (Information Expert, chapter 6), and remove the desk's knowledge of how a booking records payment. 3. A LATENT DEFECT: cancelling a session leaves a Requested booking as it was, still holding a seat (output item 3). With synchronous payment no booking is Requested when the organiser acts, so it cannot happen today. It is design decision D8's consequence, now demonstrated rather than asserted: if payment becomes asynchronous, cancel_session needs a rule for Requested. 4. A QUESTION FOR THE MANAGER: booking on the day of the session is refused as 'already started' (output item 4). Nothing in the requirements says so, it is my assumption, and it affects people who want a same-day seat. Add it to the requirements as Q7 and ask. Other findings a reviewer could add: events are plain strings in Booking.send, so a typo is only caught at run time (and the every-pair test would not notice a misspelt event in a new row); and bookings are kept in creation order and that order is the waiting-list order, which is an implicit coupling worth a comment or a test (there is one). WHY THIS WORKS AS AN ANSWER --------------------------- Each finding is backed by running something, and they come from three sources: a tool that was wrong (1), a tool's blind spot that reading the code covered (2), the code itself when pushed (3), and the requirements, where only a person can answer (4). That mix is typical of a real review: the tools are a prompt, not the verdict.