One of the GoF design patterns is the facade. It lets us create a simple interface that hides the underlying complexity. For example, we can have a facade which lets the client book meetings on various calendars like Google, Outlook, Calendly, etc. The client specifies details about the meeting such as the title, description, etc. along with which calendar to use. The facade then executes appropriate logic to book the meeting, without the client having to deal with the low-level details.
This post talks about how we can create a facade in Python. We’ll first take a look at singledispatch to see how we can call different functions depending on the type of the argument. We’ll then build upon this to create a function which dispatches based on the value instead of the type. We’ll use the example given above to create a function which dispatches to the right function based on what calendar the client would like to use.
The official documentation defines single dispatch to be a function where the implementation is chosen based on the type of a single argument. This means we can have one function which handles integers, another which handles strings, and so on. Such functions are created using the
singledispatch decorator from the
functools package. Here’s a trivial example which prints the type of the argument handled by the function.
We start by decorating the
echo function with
singledispatch. This is the function we will pass our arguments to. We then create
echo_str which are different implementation that will handle the various types of arguments. These are registered using the
When we run the example, we get the following output. As expected, the function to execute is chosen based on the type of the argument. Calling the function with a type which is not handled results in a noop as we’ve set the body of
echo to ellipses.
5 is an int
When looking at the source code of
singledispatch, we find that it maintains a dictionary which maps the type of the argument to its corresponding function. In the following sections, we’ll look at how we can dispatch based on the value of the argument.
Let’s say we’re writing a library that lets the users book meetings on a calendar of their choosing. We expose a
book_meeting function. The argument to this function is an instance of the
Meeting data class which contains information about the meeting, and the calendar on which it should be booked.
We’ll start by adding an enum which represents the calendars that we support.
Next we’ll add the data class which represents the meeting as a
import dataclasses as dc
Finally, we’ll start creating the facade by adding functions which will dispatch based on the value of
calendar contained within the instance of
We’ll create a registry which maps the enum to its corresponding function. The function takes as input a
Meeting object and returns a boolean indicating whether the meeting was successfully booked or not.
from typing import Callable, TypeVar
Next we’ll add the
book_meeting function. This is where we dispatch to the appropriate function depending on the meeting object that is received as the argument.
def book_meeting(meeting: Meeting) -> bool:
To be able to register functions which contains the logic for a particular calendar, we’ll create a decorator called
def register(calendar: Calendar):
register accepts as argument the calendar for which we’re registering a function. It returns another higher-order function which puts the actual function in the registry. Since the actual logic of the execution is in the decorated function, we simply return the original function
Finally, we register functions for different calendars.
We’ll put all of this code in action by trying to book a meeting on Google calendar.
if __name__ == "__main__":
This prints “Booked Google meeting”, like we’d expect. We can now continue to add more functions which contain logic for specific calendars. Our library can evolve without any change to the exposed interface. It’s also possible to organise functions into their own modules, import the
register decorator, and decorate them to add them to the registry. This has two main benefits. One, we keep the code well structured. Two, the code for different versions of the same calendar can stay separated; we avoid having to write
if checks to see the calendar version since that can be made part of the enum itself, like
That’s it. That’s how you can create a facade in Python.