ExerciseDifficulty: Advanced2 min

Practice: A Facade for a Home Cinema

Projector, amplifier, streaming player and lights, with watch_movie() and end_movie().

A home cinema is a natural Facade, and this exercise asks you to write one and then say what it leaves open. Four classes are involved. Projector and Amplifier each have on() and off(), and the amplifier has set_volume(level). StreamingPlayer has play(title) and stop(). Lights has dim(percent).

  1. Write HomeCinema with watch_movie(title), which dims the lights to 10%, turns on the projector and amplifier, sets the volume to 5 and plays the film, and end_movie(), which reverses it.
  2. Halfway through a film, the viewer wants the volume at 8. The facade has no volume method. How do they change it?
Show a solution
py
class Projector:
    def on(self) -> None:
        print("projector on")

    def off(self) -> None:
        print("projector off")


class Amplifier:
    def on(self) -> None:
        print("amplifier on")

    def set_volume(self, level: int) -> None:
        print(f"amplifier volume {level}")

    def off(self) -> None:
        print("amplifier off")


class StreamingPlayer:
    def play(self, title: str) -> None:
        print(f"playing {title}")

    def stop(self) -> None:
        print("player stopped")


class Lights:
    def dim(self, percent: int) -> None:
        print(f"lights at {percent}%")


class HomeCinema:
    def __init__(self, projector: Projector, amplifier: Amplifier, player: StreamingPlayer, lights: Lights) -> None:
        self._projector = projector
        self._amplifier = amplifier
        self._player = player
        self._lights = lights

    def watch_movie(self, title: str) -> None:
        self._lights.dim(10)
        self._projector.on()
        self._amplifier.on()
        self._amplifier.set_volume(5)
        self._player.play(title)

    def end_movie(self) -> None:
        self._player.stop()
        self._amplifier.off()
        self._projector.off()
        self._lights.dim(100)


amplifier = Amplifier()
cinema = HomeCinema(Projector(), amplifier, StreamingPlayer(), Lights())
cinema.watch_movie("Brief Encounter")
amplifier.set_volume(8)
cinema.end_movie()
OutputRan with python:3.12-slim (Python 3.12.13)
lights at 10%
projector on
amplifier on
amplifier volume 5
playing Brief Encounter
amplifier volume 8
player stopped
amplifier off
projector off
lights at 100%
The facade runs both sequences; the volume change goes straight to the amplifier.
The participants in the home cinema

The facade holds the two sequences, and end_movie runs them in reverse, so the lights come up last. The volume change does not need a new facade method: the client keeps its reference to the amplifier and calls set_volume(8) directly, as the output shows between the two sequences. The extra arrow from the client to the amplifier in the diagram is that call. A facade that tried to offer every amplifier setting would be the subsystem again, with a longer name.