CodeRigor

FREE LESSON

Methods as state transitions

Design commands and queries around a bounded reservation state machine, with explicit errors and no partial updates.

A useful class exposes actions in the language of its job. A reservation can reserve or release units; callers should not have to coordinate assignments to held and available counters themselves. Describe the state and legal transitions before selecting method names. Here capacity stays fixed, held starts at zero, and the invariant is 0 <= held <= capacity.

Available is always capacity - held, so it is a derived query. Storing it as a third changing field would create another way for the object to contradict itself. Derivation is inexpensive in this model and keeps one source of truth. A property is convenient for each small observation; a method is appropriate for commands with parameters and failure behavior.

Reservation states
start / 2
Empty, partially held and full are all valid states. Commands move between them while queries observe.
Reservation states
Empty, partially held and full are all valid states. Commands move between them while queries observe.

A command changes state; a query reports it

The example below is deliberately small enough to trace. Both commands return None, so callers read held or available after the operation. Returning a new count could also be a valid API, but mixing that choice across similar methods makes a program harder to understand. Follow the build’s stated return contract rather than relying on incidental expression results.

Python
class Hold:
    def __init__(self, capacity):
        self.capacity = capacity
        self.held = 0

    @property
    def available(self):
        return self.capacity - self.held

    def reserve(self, units):
        if units <= 0 or units > self.available:
            raise ValueError("cannot reserve")
        self.held += units

    def release(self, units):
        if units <= 0 or units > self.held:
            raise ValueError("cannot release")
        self.held -= units

hold = Hold(5)
print(hold.reserve(5))
print(hold.held, hold.available)
hold.release(2)
print(hold.held, hold.available)

This illustrative class assumes integer arguments and exposes writable fields. The graded Reservation strengthens it with exact integer checks, read-only properties and validated nonnegative construction. The distinction matters: an example that assumes trusted inputs does not prove a reusable public API’s boundary behavior.

One stored count, one derived count
start / 3
For fixed capacity five, reserve and release preserve held plus available equal to five.
One stored count, one derived count
For fixed capacity five, reserve and release preserve held plus available equal to five.

Test a sequence, including rejected actions

For reserve, reject counts strictly greater than available. Equality is legal and produces a full reservation. For release, reject counts greater than held; equality empties the reservation. Both methods reject zero because a command is defined to request positive units, even though zero is a valid state.

A method can look correct in isolation while a sequence breaks the invariant. Reserve several small counts, query twice, release everything, then attempt an invalid release. Every observation must agree, and a rejected command must leave capacity, held and available unchanged. Also make a second instance to prove that its state does not follow the first instance’s transitions.

Queries should have no surprising side effects. Reading available must not release anything, lazily reset a counter or consume an iterator. Such behavior would make ordinary debugging observations change the program being inspected. In the build, repeated property reads must therefore remain stable.

The checks ask you to reason about stored versus derived facts and command boundaries. The bug hunts reverse a transition and reject a valid exact fill. The fill implements reserve’s guard and commit. Finish by writing the complete class and demonstrating the invariant through a full fill-and-release cycle. This is a single-threaded model; simultaneous booking requires additional coordination that this lesson does not provide.

Watch & read

Free lectures, talks and readings that cover this skill from another angle. All resources →

Watch how an instance method receives self and uses instance attributes. Apply that mechanism to reserve and release; the lesson and tests supply the state-transition and failure contracts.