Card 08/ 09
Practice: A Smart Home Remote With Undo
Each button turns a light on or sets the heater, and one button undoes the last press.
A remote control is a natural Command, and this exercise asks you to write the commands and decide what each stores. The kitchen light starts off and the heater starts at 16 °C. The remote's buttons turn the light on, set the heater to a given temperature, and undo the last press, as many times as there are presses to undo.
- Write
LightOnandSetHeaterwithexecuteandundo, and aRemotethat keeps the history. - What must each command store, and when must it store it?
- Press light on, heater to 21, heater to 23, then undo three times. What are the light and the heater after each step?
Show a solution
class Light:
def __init__(self, room: str) -> None:
self.room, self.on = room, False
class Heater:
def __init__(self) -> None:
self.celsius = 16
class LightOn:
def __init__(self, light: Light) -> None:
self.light = light
self.was_on = False
def execute(self) -> None:
self.was_on = self.light.on
self.light.on = True
def undo(self) -> None:
self.light.on = self.was_on
class SetHeater:
def __init__(self, heater: Heater, celsius: int) -> None:
self.heater, self.celsius = heater, celsius
self.previous = 0
def execute(self) -> None:
self.previous = self.heater.celsius
self.heater.celsius = self.celsius
def undo(self) -> None:
self.heater.celsius = self.previous
class Remote:
def __init__(self) -> None:
self.history = []
def press(self, command) -> None:
command.execute()
self.history.append(command)
def undo(self) -> None:
if self.history:
self.history.pop().undo()
kitchen, heater, remote = Light("kitchen"), Heater(), Remote()
def show(step: str) -> None:
print(f"{step:22} light {'on' if kitchen.on else 'off'}, heater {heater.celsius} C")
show("start")
remote.press(LightOn(kitchen))
show("light on")
remote.press(SetHeater(heater, 21))
show("heater to 21")
remote.press(SetHeater(heater, 23))
show("heater to 23")
remote.undo()
show("undo")
remote.undo()
show("undo")
remote.undo()
show("undo")start light off, heater 16 C
light on light on, heater 16 C
heater to 21 light on, heater 21 C
heater to 23 light on, heater 23 C
undo light on, heater 21 C
undo light on, heater 16 C
undo light off, heater 16 CSetHeater stores the temperature it replaced, and must read it in execute, not in its constructor. The heater can change between the moment a command is created and the moment it runs, for instance when a remote creates each button's command once, at setup, so a value read in the constructor would be stale. LightOn stores whether the light was already on, so undoing it on a light that was on leaves it on.
The three undos run in reverse: 23 back to 21, 21 back to 16, then the light off. The remote's undo checks for an empty history, so a fourth press does nothing instead of raising.
← → move through sections and cards