Skip to content
BytePatterns

Encapsulation and Invariants

Low-Level Design: lesson 2 of 10

Hide the state so the object can never be caught in a bad one.

Lesson 2 of 10 · 5 min

Encapsulation and Invariants

Step 1 of 11

An invariant is a promise that holds before and after every public call.

The Idea

An invariant is a promise about an object that holds before and after every public call. Encapsulation is how you keep it: fields stay private, and the only way in is a method that validates first. A rejected call changes nothing at all.

Real-World Example

A pressure cooker's locking lid. While there is pressure inside, the lid physically will not turn. You are not trusted to check the gauge yourself — the only route to an open pot runs through the release valve.

The Code

class Thermostat:
    def __init__(self, target): self._target = 5; self.set(target)
    def set(self, c):
        if not 5 <= c <= 30:                  # the invariant, checked at the door
            raise ValueError("outside safe range")
        self._target = c
    @property
    def target(self): return self._target     # read-only from outside

t = Thermostat(21)
t.set(24)
print(t.target)                               # 24
try: t.set(80)
except ValueError as e: print("refused:", e)  # refused: outside safe range
print(t.target)                               # 24 -- still a legal state

Your turn

What does this print?

class Counter:
  def __init__(self): self._n = 0
  def add(self, k):
      if k <= 0: return False
      self._n += k
      return True

c = Counter()
print(c.add(3), c.add(-1), c._n)

Mini quiz

1 / 3

An invariant is: