Design Pattern: Facade Pattern in Python

In a large system, we end up with many submodules or sub-systems, and we do not want to expose all the submodules for different reasons(security, complexity, etc.). In that case, Facade pattern can help us to hide the internal complexity, and expose only the required simplified functions/interfaces.

Facade works as an abstraction layer, over the underlying complex system. The client communicates with the facade, and the facade communicates with the subsystems.

Facade Pattern
Facade Pattern

NOTES

In this article, we discuss the implementation of the Facade Pattern in Python.

See the Facade in other languages in the “Other Code Implementations” section. Or, use the link below to check the details of the Facade Pattern-

Implementation

Follow the steps below to implement Facade pattern in Python-

Create a subsystem class “Subsystem1” and define methods “operation1” and “operation2“. There can be any number of methods depending on the requirement of the subsystem.
Create a subsystem class “Subsystem2” and define methods “operation3” and “operation4“. There can be any number of methods depending on the requirement of the subsystem.
Create a subsystem class “Subsystem3” and define method “operation5“.
Define a “Facade” class and in the “__init__” method initialize all the subsystem classes and create objects of those subsystems.
Define required method in the “Facade” class, and in the method definition, use the subsystem object by calling methods from those classes. We can use the method from subsystem object as we want, and there any be other steps as per requirement.

NOTES

There is no hard rule for creating subsystems. We will define the subsystems as per our requirements.

Create the facade and use the subsystem objects, to call methods(or attributes) from the subsystems. Define whatever methods that the clients need.

Just make sure that the client only uses methods from the facade. And the client never uses the subsystems directly.

Here is a simple example of Facade implementation-

# First subsystem
class Subsystem1:
    def operation1(self):
        print("Subsystem 1: operation 1")

    def operation2(self):
        print("Subsystem 1: operation 2")


# Second subsystem
class Subsystem2:
    def operation3(self):
        print("Subsystem 2: operation 3")

    def operation4(self):
        print("Subsystem 2: operation 4")


# Third subsystem
class Subsystem3:
    def operation5(self):
        print("Subsystem 3: operation 5")


# Facade
class Facade:
    def __init__(self):
        # Initialize subsystems
        self.subsystem1 = Subsystem1()
        self.subsystem2 = Subsystem2()
        self.subsystem3 = Subsystem3()

    def exampleOp1(self):
        # Use operation from subsystem as required
        self.subsystem1.operation1()
        self.subsystem2.operation3()

    def exampleOp2(self):
        # Use operation from subsystem as required
        self.subsystem1.operation2()
        self.subsystem3.operation5()


# Demo usage
if __name__ == "__main__":
    facade = Facade()

    facade.exampleOp1()
    facade.exampleOp2()
Python

Output of the above code will be –

Subsystem 1: operation 1
Subsystem 2: operation 3
Subsystem 1: operation 2
Subsystem 3: operation 5
Plaintext

Examples

Let’s take a look at examples where we can use the Facade pattern.

Example #1: Travel Plan

Here we are building and facade for planning a travel. A travel plan has different parts, like-

  • Car/Vehicle: for managing the car operations.
  • Direction: for getting the direction of the path.
  • Toll: for toll calculation.
  • Weather: for getting weather information.

Car Class [subsystem class]

Create “Car” class.
Define methods for engine start and stop.
Also define methods for going in different directions- left, right, straight.
Define any other required methods that the car can use.
import random


# Subsystem: Car
class Car:
    def start_engine(self):
        print("Start Engine")

    def stop_engine(self):
        print("Stop Engine")

    def go_straight(self):
        print("Go Straight: ↑")

    def go_left(self):
        print("Go Left: ←")

    def go_right(self):
        print("Go Right: →")

    def get_distance_travelled(self):
        # Random calculation for demo purposes
        return round(
            (random.randint(0, 180) * ((10000 - 100) * 10 + 1) + 100 * 10) / 10.0, 2
        )
Python

Point Class [subsystem class]

Define a class “Point“. This is for storing the latitude and longitude of a geolocation point.
Define getter, setter, deleter for latitude and longitude.
# Subsystem: Point (for coordinates)
class Point:
    def __init__(self, lat: float, lng: float):
        self._lat = lat
        self._lng = lng

    # Getters and Setters for Latitude
    @property
    def lat(self):
        return self._lat

    @lat.setter
    def lat(self, value: float):
        self._lat = value

    @lat.deleter
    def lat(self):
        del self._lat

    # Getters and Setters for Longitude
    @property
    def lng(self):
        return self._lng

    @lng.setter
    def lng(self, value: float):
        self._lng = value

    @lng.deleter
    def lng(self):
        del self._lng
Python

Direction Class [subsystem class]

Create “Direction” class, for operations related to getting directions. Like, starting and end point, etc.
Define methods as per requirement.
# Subsystem: Direction
class Direction:
    def __init__(
        self, start_lat: float, start_lng: float, end_lat: float, end_lng: float
    ):
        self._start_lat = start_lat
        self._start_lng = start_lng
        self._end_lat = end_lat
        self._end_lng = end_lng

    def get_location_details(self, lat: float, lng: float):
        print("Country: ABC")
        print("City: DEF")
        print("State: GHI")
        print("Zip: 101010")

    def get_current_location(self):
        # Random calculation for demo purposes
        current_lat = random.uniform(-90, 90)
        current_lng = random.uniform(-180, 180)
        return Point(current_lat, current_lng)

    def get_next_move(self):
        # Randomly choose the next move
        next_moves = ["straight", "left", "right"]
        return random.choice(next_moves)

    def get_full_route(self):
        points = []
        for _ in range(10):
            # Random calculation for demo purposes
            current_lat = random.uniform(-90, 90)
            current_lng = random.uniform(-180, 180)
            points.append(Point(current_lat, current_lng))
        return points
Python

Toll Class [subsystem class]

Define “Toll” class and define methods as per requirement for handling toll related operations.
# Subsystem: Toll
class Toll:
    def get_toll_points(self, lat: float, lng: float):
        points = []
        for _ in range(10):
            current_lat = random.uniform(-90, 90)
            current_lng = random.uniform(-180, 180)
            points.append(Point(current_lat, current_lng))
        return points

    def get_toll_amount(self, toll_point_id: float):
        return random.uniform(0, 100)

    def get_total_toll(self, lat: float, lng: float):
        return random.uniform(0, 100)
Python

Weather Class [subsystem class]

Created “Weather” class.
Define method for getting weather information.
# Subsystem: Weather
class Weather:
    def get_weather_info(self, lat: float, lng: float):
        # Dummy weather info for demo purpose
        print("Temperature: 20.7°C")
        print("Precipitation: 1%")
        print("Humidity: 73%")
        print("Wind: 8 km/h")
Python

Facade Class

Create a “TravelFacade” class.
In the “__init__” method accept any required info, like start and end points.
In the “__init__” method, create object of each subsystem and save those objects in attributes.
Define methods as per the requirement and use methods from subsystem object when needed.
# Facade: TravelFacade
class TravelFacade:
    def __init__(
        self, start_lat: float, start_lng: float, end_lat: float, end_lng: float
    ):
        self._start_lat = start_lat
        self._start_lng = start_lng
        self._end_lat = end_lat
        self._end_lng = end_lng
        self._direction = Direction(start_lat, start_lng, end_lat, end_lng)
        self._car = Car()
        self._toll = Toll()
        self._weather = Weather()

    def get_route(self):
        return self._direction.get_full_route()

    def get_location_info(self, lat: float, lng: float):
        self._direction.get_location_details(lat, lng)
        self._weather.get_weather_info(lat, lng)

    def get_current_location(self):
        return self._direction.get_current_location()

    def operate_car(self):
        full_route = self._direction.get_full_route()
        self._car.start_engine()
        for point in full_route:
            next_move = self._direction.get_next_move()
            if next_move == "straight":
                self._car.go_straight()
            elif next_move == "left":
                self._car.go_left()
            elif next_move == "right":
                self._car.go_right()
        self._car.stop_engine()

    def get_total_toll_amount(self, lat: float, lng: float):
        print(f"Total Toll Amount: {self._toll.get_total_toll(lat, lng):.2f}")
Python

Demo

# Demo: Using the Facade
if __name__ == "__main__":
    travel_facade = TravelFacade(10, 10, 20, 30)
    current_location = travel_facade.get_current_location()
    print(f"Current Latitude: {current_location.lat}")
    print(f"Current Longitude: {current_location.lng}")

    travel_facade.get_location_info(20, 30)
    travel_facade.get_total_toll_amount(20, 30)
    travel_facade.operate_car()
Python

Output

Output of the above demo code will be as below. Output may differ for you as most of the methods here use dummy(with random) code.

Current Latitude: 3.942649018722733
Current Longitude: 75.2548665877676

Country: ABC
City: DEF
State: GHI
Zip: 101010
Temperature: 20.7°C
Precipitation: 1%
Humidity: 73%
Wind: 8 km/h

Total Toll Amount: 91.00

Start Engine
Go Right: →
Go Left: ←
Go Straight: ↑
Go Left: ←
Go Straight: ↑
Go Straight: ↑
Go Left: ←
Go Straight: ↑
Go Straight: ↑
Go Straight: ↑
Stop Engine
Plaintext

Source Code

Use the following link to get the source code:

Other Code Implementations

Use the following links to check Facade pattern implementation in other programming languages.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.