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.
Moja ya GoF muundo mifumo ni facade. Inatuwezesha kuunda interface rahisi ambayo huficha utata wa msingi. Kwa mfano, tunaweza kuwa na facade ambayo inaruhusu mteja kitabu mikutano juu ya kalenda mbalimbali kama Google, Outlook, Calendly, nk. Mteja anafafanua maelezo kuhusu mkutano kama vile jina, maelezo, nk. pamoja na kalenda ya kutumia. facade kisha kutekeleza mantiki sahihi kwa kitabu mkutano, bila mteja kuwa na kushughulika na maelezo ya chini ya ngazi. Chapisho hili anazungumza juu ya jinsi tunaweza kujenga facade katika Python. Sisi kwanza kuchukua kuangalia katika singledispatch kuona jinsi tunaweza wito kazi tofauti kulingana na aina ya hoja. Sisi kisha kujenga juu ya hii kuunda kazi ambayo dispatches kulingana na thamani badala ya aina. Sisi itabidi kutumia mfano aliyopewa hapo juu kuunda kazi ambayo dispatches kwa kazi ya haki kulingana na nini kalenda mteja angependa kutumia. Hati rasmi inafafanua kutuma moja kuwa kazi ambapo utekelezaji ni kuchaguliwa kulingana na aina ya hoja moja. Hii ina maana tunaweza kuwa na moja kazi ambayo hushughulikia integers, nyingine ambayo hushughulikia safu, na kadhalika. Kazi kama hizo ni kuundwa kwa kutumia singledispatch decorator kutoka functools mfuko. Hapa ni mfano trivial ambayo prints aina ya hoja kushughulikiwa na kazi. Sisi kuanza kwa kupamba echo kazi na singledispatch. Hii ni kazi tutakayopitisha hoja zetu kwa. Kisha sisi kujenga echo_str ambayo ni utekelezaji tofauti ambayo kushughulikia aina mbalimbali za hoja. Hii ni kusajiliwa kwa kutumia wakati sisi kukimbia mfano, sisi kupata pato zifuatazo. Kama inatarajiwa, kazi ya kutekeleza ni kuchaguliwa kulingana na aina ya hoja. Wito wa kazi na aina ambayo si kushughulikiwa matokeo katika noop kama sisi <unk>ve kuweka mwili wa echo kwa ellipses. "Kama kuangalia katika chanzo code ya ""Singledispatch,"" tunapata kwamba inadumisha kamusi ambayo ramani aina ya hoja kwa kazi yake husika." Katika sehemu zifuatazo, tutaangalia jinsi tunaweza kupeleka kulingana na thamani ya hoja. Hebu kusema sisi ni kuandika maktaba ambayo inaruhusu watumiaji kitabu mikutano juu ya kalenda ya uchaguzi wao. Sisi expose kitabu_mkutano kazi. Hoja ya kazi hii ni mfano wa mkutano data darasa ambayo ina habari kuhusu mkutano, na kalenda ambayo inapaswa kuwa booked. Tutaanza kwa kuongeza enum ambayo inawakilisha kalenda ambazo tunasaidia. "Kisha, tutaongeza darasa la data ambalo linawakilisha mkutano kama ""Import DataClasses"" kama ""DC"" Mwishowe, tutaanza kuunda uso kwa kuongeza kazi ambazo zitawasilisha kulingana na thamani ya kalenda iliyo ndani ya mfano wa ""Tutaunda Usajili ambao unaonyesha orodha kwa kazi yake inayolingana." Kazi inachukua kama pembejeo mkutano kitu na anarudi boolean kuonyesha kama mkutano ilikuwa mafanikio booked au la. kutoka typing import Callable, TypeVar Next sisi kuongeza kitabu_mkutano kazi. Hii ni ambapo sisi dispatch kwa kazi sahihi kulingana na mkutano kitu kwamba ni kupokea kama hoja. book_meeting(mkutano: Mkutano) -> def bool: Ili kuwa na uwezo wa kusajili kazi ambayo ina mantiki kwa ajili ya kalenda maalum, sisi <unk>ll kujenga decorator aitwaye def register(kalenda: Kalenda): register inakubali kama hoja kalenda ambayo sisi <unk>re kusajili kazi. Ni anarudi mwingine juu ya utaratibu kazi ambayo huweka kazi halisi katika usajili. Kwa kuwa mantiki halisi ya utekelezaji ni katika kazi mapambo, sisi tu kurudi kazi ya awali Hatimaye, sisi kusajili kazi kwa ajili ya kalenda tofauti. Tutaweka kanuni hii yote katika hatua kwa kujaribu kitabu mkutano juu ya Google kalenda. ifname__ == "__main__": Hii prints <unk>Booked Google mkutano<unk>, kama sisi <unk>d kutarajia. Sasa tunaweza kuendelea kuongeza kazi zaidi ambayo yana mantiki kwa ajili ya kalenda maalum. Maktaba yetu inaweza kubadilika bila mabadiliko yoyote ya interface wazi. Pia inawezekana kuandaa kazi katika modules yao wenyewe, kuagiza kumbukumbu decorator, na kupamba yao kuongeza yao kwa usajili. Hilo lina faida mbili kuu. Kwanza, tunahifadhi kanuni hiyo ikiwa imepangwa vizuri. Mbili, kanuni kwa ajili ya matoleo tofauti ya kalenda moja inaweza kukaa tofauti; sisi kuepuka kuwa na kuandika kama kuangalia kuona toleo kalenda tangu kwamba inaweza kufanywa sehemu ya enum yenyewe, kama Hiyo ni. Hiyo ni jinsi unaweza kujenga facade katika Python.