JAVA / CHAPTER 09

Designing classes

Intermediate

Protect invariants through constructors and cohesive public methods.

By the end of this chapter

  • Describe a class invariant
  • Validate updates before mutation
  • Separate queries from commands

The idea

A class invariant is a rule that should remain true whenever a public operation finishes. For a bounded ticket counter, the count might always remain between zero and capacity. Constructors must establish the rule, and every modifying method must preserve it. Private fields prevent callers from bypassing these checks. A useful public method expresses an action meaningful to the problem rather than exposing every internal field.

Plan failure behavior before changing state. A reservation method can return false when a request exceeds the remaining capacity, leaving the object unchanged. Query methods report state without modifying it. This separation makes sequences of operations easier to test and helps callers understand which expressions have side effects.

Java example
class Seats {
  private int remaining;
  Seats(int capacity) {
    if (capacity < 0) throw new IllegalArgumentException();
    remaining = capacity;
  }
  boolean reserve(int count) {
    if (count < 0 || count > remaining) return false;
    remaining -= count; return true;
  }
  int remaining() { return remaining; }
}
public class Main {
  public static void main(String[] args) {
    Seats s = new Seats(4);
    System.out.println(s.reserve(5));
    System.out.println(s.remaining());
  }
}
Result
false
4

The guard rejects an oversized request before subtracting anything. The failed command leaves the observable state unchanged.

Try it yourself

Specify a bounded volume control whose increase method cannot exceed its maximum.

Use your own editor for this exercise. Runnable challenges on CodeQuest are available for JavaScript, Python, and SQL.

CHECK YOUR UNDERSTANDING

Chapter checkpoint

Which API best protects a nonnegative balance?