Card 04/ 08

ExampleDifficulty: Intermediate2 min

Mediator Example: A Booking Form Whose Fields Depend on Each Other

Choosing a return trip shows the return date; choosing a date checks the seats.

A form whose fields affect each other is a natural Mediator, and this example maps the template onto it. Choosing a return trip shows the return date field. Choosing a departure date shows how many seats are left, and the book button is enabled only when there is a seat. The fields tell the dialog they changed, and the dialog applies the rules.

The template's parts in the booking form example
TemplateBooking form example
ConcreteMediatorBookingDialog
Colleagues that reportTripType and DepartureDate
Colleagues that are updatedReturnDate, SeatInfo and BookButton
notifychanged(field)
py
from datetime import date


class Field:
    def __init__(self, dialog: "BookingDialog") -> None:
        self.dialog = dialog


class TripType(Field):
    value = "one-way"

    def choose(self, value: str) -> None:
        self.value = value
        self.dialog.changed(self)


class DepartureDate(Field):
    value: date | None = None

    def choose(self, value: date) -> None:
        self.value = value
        self.dialog.changed(self)


class ReturnDate(Field):
    visible = False


class SeatInfo(Field):
    text = ""


class BookButton(Field):
    enabled = False


class BookingDialog:
    SEATS = {date(2026, 11, 2): 14, date(2026, 11, 3): 0}

    def __init__(self) -> None:
        self.trip = TripType(self)
        self.departure = DepartureDate(self)
        self.return_date = ReturnDate(self)
        self.seats = SeatInfo(self)
        self.book = BookButton(self)

    def changed(self, field: Field) -> None:
        if field is self.trip:
            self.return_date.visible = self.trip.value == "return"
        if field is self.departure and self.departure.value is not None:
            free = self.SEATS.get(self.departure.value, 0)
            self.seats.text = f"{free} seats left"
            self.book.enabled = free > 0
        self.show()

    def show(self) -> None:
        print(f"  return date shown: {self.return_date.visible}, "
              f"seats: {self.seats.text or '-'}, book enabled: {self.book.enabled}")


dialog = BookingDialog()
print("choose return trip:")
dialog.trip.choose("return")
print("choose 3 November:")
dialog.departure.choose(date(2026, 11, 3))
print("choose 2 November:")
dialog.departure.choose(date(2026, 11, 2))
OutputRan with python:3.12-slim (Python 3.12.13)
choose return trip:
  return date shown: True, seats: -, book enabled: False
choose 3 November:
  return date shown: True, seats: 0 seats left, book enabled: False
choose 2 November:
  return date shown: True, seats: 14 seats left, book enabled: True
The fields call `changed`; only the dialog knows the rules.

Choosing a return trip shows the return date. Choosing 3 November finds no seats and leaves the book button disabled; choosing 2 November finds 14 and enables it. TripType does not know that a return date field exists, and DepartureDate does not know about the button.

The rules are four lines of changed. A new rule, such as "a return trip must return after it departs", goes in the same method. The seat counts are fixed in a dictionary here so the listing runs offline.