Card 08/ 09
Practice: A Temperature Sensor With Displays That Come and Go
Displays subscribe and unsubscribe while readings arrive.
A temperature sensor is a natural Observer, and this exercise asks you to find the subscriber that outlives its use. A sensor notifies attached displays of each reading. A maximum display is attached for the whole run. A room shows the current reading on its own display while it is open.
class Room:
def open(self) -> None:
self._sensor.attach(self._display)
self.is_open = True
def close(self) -> None:
self.is_open = FalseThe sensor records 19.5, then the room opens and it records 21.0, then the room closes and it records 20.0.
- Which displays print each reading?
- How many displays are attached at the end?
- Fix
close.
Show a solution
from typing import Protocol
class Display(Protocol):
def reading(self, celsius: float) -> None: ...
class Sensor:
def __init__(self) -> None:
self._displays: list[Display] = []
def attach(self, display: Display) -> None:
self._displays.append(display)
def detach(self, display: Display) -> None:
self._displays.remove(display)
def record(self, celsius: float) -> None:
for display in self._displays:
display.reading(celsius)
@property
def display_count(self) -> int:
return len(self._displays)
class CurrentDisplay:
def reading(self, celsius: float) -> None:
print(f" now: {celsius} C")
class MaxDisplay:
def __init__(self) -> None:
self.highest = float("-inf")
def reading(self, celsius: float) -> None:
self.highest = max(self.highest, celsius)
print(f" max: {self.highest} C")
class Room:
def __init__(self, sensor: Sensor) -> None:
self._sensor = sensor
self._display = CurrentDisplay()
self.is_open = False
def open(self) -> None:
self._sensor.attach(self._display)
self.is_open = True
def close(self) -> None:
self.is_open = False
sensor = Sensor()
peak = MaxDisplay()
sensor.attach(peak)
print("19.5 with one display")
sensor.record(19.5)
room = Room(sensor)
room.open()
print("21.0 with the room open")
sensor.record(21.0)
room.close()
print("20.0 with the room closed")
sensor.record(20.0)
print("displays attached:", sensor.display_count)19.5 with one display
max: 19.5 C
21.0 with the room open
max: 21.0 C
now: 21.0 C
20.0 with the room closed
max: 21.0 C
now: 20.0 C
displays attached: 2The maximum display prints all three readings. The room's display prints 21.0, as expected, and also 20.0 after the room closed, and two displays are still attached at the end. close changes a flag that the sensor never reads, and the display stays on the list.
The fix is the matching detach, so the room undoes in close exactly what it did in open.
def close(self) -> None:
self._sensor.detach(self._display)With it, the 20.0 reading reaches only the maximum display, and one display is attached at the end. The general check is to look for every attach and find the detach that answers it.
← → move through sections and cards