Checkpoint: Bounded Counter

The Course
•
3 min read

Implement a bounded counter whose invariant is always 0 <= current <= maximum. A private field can hide representation, but only validation and update logic can preserve this rule. Decide what construction and failed updates do before coding.

The maximum must be a nonnegative integer. Construction with an invalid maximum throws RangeError. increment returns false at the limit and otherwise increments and returns true. Two instances must remain independent. The getter returns a number, not a mutable reference to internal state.

Budget twenty minutes: write the contract, implement the class, then test zero capacity, normal increments and independence. This prepares you to review library invariants later; it is not a reason to put every function into a class.

Predict, run, and explain

Write the output and trace the important state changes before checking it. Then run the program. Each editor is independent. Async examples end with main() so this editor waits for their work.

Predict, then run JavaScript
const max = 0;

console.log(Number.isInteger(max) && max >= 0);
console.log(0 < max);
Check your prediction
true
false

Acceptance cases before you code

Write a small test plan before implementing bounded counter rules. These cases extend the brief; they are not a second worked solution. Predict the expected behavior independently, then test through the public function. A good ordinary example and a good boundary example should catch different possible mistakes.

Case to tryWhat your result should demonstrate
Maximum zeroNever increment and keep current at zero
Reach exact maximumNext increment reports false without changing state
Separate instancesDo not share private counts

After the core task passes, deliberately break one rule and identify the case that notices it. Restore the implementation and explain the failure in terms of the contract, not the number of assertions. If a case requires a concept you have not recalled, revisit the relevant lesson before opening the solution.

Your turn

Exercise

Checkpoint: Bounded Counter: practise

Write BoundedCounter(maximum) with private state, getter current and increment() as specified. A maximum of 1 should produce true, false and 1. A separate maximum-zero instance must stay at 0.

Your workspace JavaScript
// Test the invariant at construction and before each state change.
Hint 1: the idea

An unsuccessful operation must leave state unchanged.

Hint 2: a first step

Validate maximum before storing it, then compare current with maximum.

Show one solution
class BoundedCounter {
  #current = 0;
  #maximum;

  constructor(maximum) {
    if (!Number.isInteger(maximum) || maximum < 0) {
      throw new RangeError("Invalid maximum");
    }

    this.#maximum = maximum;
  }

  increment() {
    if (this.#current >= this.#maximum) {
      return false;
    }

    this.#current += 1;
    return true;
  }

  get current() {
    return this.#current;
  }
}

const counter = new BoundedCounter(1);

console.log(counter.increment());
console.log(counter.increment());
console.log(counter.current);
console.log(new BoundedCounter(0).current);
true
false
1
0

Explain why the implementation meets the stated contract. Which part is enforced by the language or checker, and which part needs behavioral evidence?

Change it and test it

Assert that -1 and 1.5 are rejected. Borrow increment onto an unrelated object and observe the private-field failure. Explain which rule privacy enforces and which rule your conditions enforce. Redo this API tomorrow with a closure instead of a class.

Was this article helpful?

Your response is saved on this device.