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.
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-
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()
PythonOutput of the above code will be –
Subsystem 1: operation 1
Subsystem 2: operation 3
Subsystem 1: operation 2
Subsystem 3: operation 5
PlaintextExamples
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]
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
)
PythonPoint Class [subsystem class]
# 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
PythonDirection Class [subsystem class]
# 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
PythonToll Class [subsystem class]
# 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)
PythonWeather Class [subsystem class]
# 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")
PythonFacade Class
# 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}")
PythonDemo
# 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()
PythonOutput
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
PlaintextSource Code
Use the following link to get the source code:
Example | Source Code Link |
---|---|
Example #1: Travel Plan | GitHub |
Other Code Implementations
Use the following links to check Facade pattern implementation in other programming languages.