Card 04/ 08
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.
| Template | Booking form example |
|---|---|
| ConcreteMediator | BookingDialog |
| Colleagues that report | TripType and DepartureDate |
| Colleagues that are updated | ReturnDate, SeatInfo and BookButton |
| notify | changed(field) |
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))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: TrueChoosing 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.
← → move through sections and cards