• Giacomo Debidda
    • Articles
    • Projects
    • Services
    • Notes
    • Likes
    • Bookmarks
    • Photos
    • About
    • Contact

Façade pattern in Python

26 Nov 2016 Giacomo Debidda
  • article
  • design patterns
  • Python

Table of Contents 📑
  1. # Façade is a structural design pattern
  2. # Façade Pattern in Python

Let’s continue our journey through the most used design patterns by implementing a Façade pattern in Python.

# Façade is a structural design pattern

Façade can be used to define a simpler, leaner, higher-level, more consistent interface to expose to a client a specific subset of functionalities provided by one or more subsystems. Typically these lower-level subsystems are called complex parts. All complex parts controlled by the Façade are often parts of smaller subsystems that are associated one to another.

Façade builds a convenient interface which saves a client the hassle of
dealing with complex parts.

Note that the client can still have direct access to these functionalities: the Façade does not - and should not - prevent the client from accessing the complex parts.

Subsystem implementation gains flexibility, client gains simplicity.

The caret package in R is a great example of a Façade, because it wraps a collection of APIs into a single well-designed API. The R programming language contains a huge number of packages for implementing almost all statistical models ever created. Unfortunately, more often then not, these packages have their own specific syntax, so when training/testing the model, one must know the syntax for the model being used. Caret implements a set of functions that provide a uniform interface when creating predictive models (e.g. the functions train and predict), but if you want you can still use the original syntax to train/test a specific model.

# Façade Pattern in Python

To illustrate a Façade pattern I will use a car as an example: you would like to have access to a set of functionalities when using a car (e.g. drive, park, etc), but you probably don’t want to deal with all the complex parts a car is composed of.

In this example I decided to implement the complex parts as private classes. Since this is python, we can still access these classes without any issue. I just make them private to suggest that the client should call the Façade, not the complex parts directly.

The Complex parts.

class _IgnitionSystem(object):

    @staticmethod
    def produce_spark():
        return True


class _Engine(object):

    def __init__(self):
        self.revs_per_minute = 0

    def turnon(self):
        self.revs_per_minute = 2000

    def turnoff(self):
        self.revs_per_minute = 0


class _FuelTank(object):
    def __init__(self, level=30):
        self._level = level

    @property
    def level(self):
        return self._level

    @level.setter
    def level(self, level):
        self._level = level


class _DashBoardLight(object):

    def __init__(self, is_on=False):
        self._is_on = is_on

    def __str__(self):
        return self.__class__.__name__

    @property
    def is_on(self):
        return self._is_on

    @is_on.setter
    def is_on(self, status):
        self._is_on = status

    def status_check(self):
        if self._is_on:
            print('{}: ON'.format(str(self)))
        else:
            print('{}: OFF'.format(str(self)))


class _HandBrakeLight(_DashBoardLight):
    pass


class _FogLampLight(_DashBoardLight):
    pass


class _Dashboard(object):

    def __init__(self):
        self.lights = {'handbreak': _HandBrakeLight(), 'fog': _FogLampLight()}

    def show(self):
        for light in self.lights.values():
            light.status_check()

The Façade.

class Car(object):
    def __init__(self):
        self.ignition_system = _IgnitionSystem()
        self.engine = _Engine()
        self.fuel_tank = _FuelTank()
        self.dashboard = _Dashboard()

    @property
    def km_per_litre(self):
        return 17.0

    def consume_fuel(self, km):
        litres = min(self.fuel_tank.level, km / self.km_per_litre)
        self.fuel_tank.level -= litres

    def start(self):
        print('\nStarting...')
        self.dashboard.show()
        if self.ignition_system.produce_spark():
            self.engine.turnon()
        else:
            print('Can\'t start. Faulty ignition system')

    def has_enough_fuel(self, km, km_per_litre):
        litres_needed = km / km_per_litre
        if self.fuel_tank.level > litres_needed:
            return True
        else:
            return False

    def drive(self, km=100):
        print('\n')
        if self.engine.revs_per_minute > 0:
            while self.has_enough_fuel(km, self.km_per_litre):
                self.consume_fuel(km)
                print('Drove {}km'.format(km))
                print('{:.2f}l of fuel still left'.format(self.fuel_tank.level))
        else:
            print('Can\'t drive. The Engine is turned off!')

    def park(self):
        print('\nParking...')
        self.dashboard.lights['handbreak'].is_on = True
        self.dashboard.show()
        self.engine.turnoff()

    def switch_fog_lights(self, status):
        print('\nSwitching {} fog lights...'.format(status))
        boolean = True if status == 'ON' else False
        self.dashboard.lights['fog'].is_on = boolean
        self.dashboard.show()

    def fill_up_tank(self):
        print('\nFuel tank filled up!')
        self.fuel_tank.level = 100

The Client here is simply the main function.

def main():
    car = Car()
    car.start()
    car.drive()

    car.switch_fog_lights('ON')
    car.switch_fog_lights('OFF')

    car.park()
    car.fill_up_tank()
    car.drive()

    car.start()
    car.drive()

if __name__ == '__main__':
    main()

You need the code? Grab it here!


  • article
  • design patterns
  • Python

🗣️ Let's have a chat!

Each week, I carve out some time for anyone to contact me. Feel free to reach out to me. I'll do my best to help you with whatever you need.

Reserve your spot here:
https://cal.com/giacomodebidda/30min

If no time slot fits you, send me a DM on LinkedIn or Twitter.

Webmentions

Did you mention this blog post on your website? Let me know the URL of your article using the form below.

Upon form submission, your webmention will be sent to Webmention.io.

    Webmentions collected by Bridgy.

    No webmentions to show.

    • Articles feed
    • Notes feed
    • Talks feed
    • GitHub
    • Twitter
    • Linkedin
    • Mastodon
    • Stack Overflow
    Copyright © 2020 – 2024 Giacomo Debidda – All rights reserved