Facade Pattern in Python
This story is part of the “Design Patterns” series. You can find the other stories of this series here:
You can also find all the code used through this series on GitHub.
The Facade pattern is a structural pattern that provides a simplified interface to a complex system of classes, modules or libraries. The Facade pattern defines a higher-level interface that makes the subsystem easier to use.
The Facade pattern is often used when a system is very complex or difficult to understand because the system has a large number of interdependent classes or its source code is unavailable.
Problems the Facade can Solve
Imagine you are building a system that allows users to send messages to one another. The system consists of several classes: a UserDatabase class for managing user accounts, a MessageQueue class for handling message sending and delivery, and a NotificationService class for sending notifications to users when they receive new messages.
Without using the facade pattern, the code to send a message might look like this:
def send_message(sender_id, recipient_id, message_text, user_database, message_queue, notification_service):
if not user_database.user_exists(sender_id):
raise Exception("Sender does not exist")
if not user_database.user_exists(recipient_id):
raise Exception("Recipient does not exist")
message_id = message_queue.enqueue_message(sender_id, recipient_id, message_text)
notification_service.send_notification(recipient_id, "You have a new message")
return message_idImagine you are building a system that allows users to send messages to one another. The system consists of several classes: a UserDatabase class for managing user accounts, a MessageQueue class for handling message sending and delivery, and a NotificationService class for sending notifications to users when they receive new messages.
Without using the facade pattern, the code to send a message might look like this:
def send_message(sender_id, recipient_id, message_text, user_database, message_queue, notification_service):
if not user_database.user_exists(sender_id):
raise Exception("Sender does not exist")
if not user_database.user_exists(recipient_id):
raise Exception("Recipient does not exist")
message_id = message_queue.enqueue_message(sender_id, recipient_id, message_text)
notification_service.send_notification(recipient_id, "You have a new message")
return message_idThe problem with this design is that the code is hard to understand and maintain because it's not clear at a glance what the send_message method does or what classes it depends on. Also, if in future, the underlying classes needs to change, you have to refactor lot of code, that might cause a ripple effect, breaking other parts of the system.
Solution
We can move our send_message function in a class to create a Facade pattern. By the way, we can add other functions to our class, so that our functions are centralized at the same place, where it makes sense.
class MessagingFacade:
def __init__(self, user_database, message_queue, notification_service):
self._user_database = user_database
self._message_queue = message_queue
self._notification_service = notification_service
def send_message(self, sender_id, recipient_id, message_text):
if not self._user_database.user_exists(sender_id):
raise ValueError("Sender does not exist")
if not self._user_database.user_exists(recipient_id):
raise ValueError("Recipient does not exist")
message_id = self._message_queue.enqueue_message(sender_id, recipient_id, message_text)
self._notification_service.send_notification(recipient_id, "You have a new message")
return message_id
def read_message(self, user_id, message_id):
"""
Returns the text of a message for a given user and message id
"""
return self._message_queue.get_message_text(user_id, message_id)
def delete_message(self, user_id, message_id):
"""
Deletes a message for a given user and message id
"""
self._message_queue.delete_message(user_id, message_id)
def list_messages(self, user_id):
"""
Returns a list of messages for a given user
"""
return self._message_queue.list_messages(user_id)
def add_user(self, user_id, user_name):
"""
Add user to the user_database
"""
self._user_database.add_user(user_id, user_name)This way, instead of calling each object independently, we can just create a Facade with these objects and call methods of the Facade.
The Facade completely opacify your system, making things easier to understand.
Application
- Hiding the complexity of a system behind a simpler interface, making it easier to use for clients.
- Providing a single point of entry for a complex system, making it easier to maintain and extend.
- Decoupling a client from the details of a subsystem, allowing the client to change the implementation of the subsystem without affecting its behavior.
Advantages
- Promotes loose coupling between the client and the subsystem, making it easier to change or replace the implementation of the subsystem without affecting the client.
- Makes the subsystem more reusable, since it is no longer tied to a specific client.
- Makes the client code more readable and understandable, since it only needs to interact with a simplified interface.
Disadvantages
- Can hide important details of the subsystem from the client, which can make it more difficult to understand the behavior of the system as a whole.
- Can lead to increased complexity if the facade interface is not designed carefully.
- May add an additional level of indirection, which can make the system slower.
Final Note
The Facade pattern is a powerful design pattern that can be used in Python to simplify the interface of a complex system of classes or modules. It is a great way to promote loose coupling, reusability, and readability in your code.
I use it sometimes when working with multiple third-party libraries that I want to simplify to mix into one interface.
To explore the other stories of this series, click below!
To explore more of my Python stories, click here! You can also access all my content by checking this page.
If you want to be notified every time I publish a new story, subscribe to me via email by clicking here!
If you’re not subscribed to medium yet and wish to support me or get access to all my stories, you can use my link:
