avatarEsteban Thilliez

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

3988

Abstract

lass="hljs-keyword">if</span> <span class="hljs-keyword">not</span> user_database.user_exists(sender_id): <span class="hljs-keyword">raise</span> Exception(<span class="hljs-string">"Sender does not exist"</span>) <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> user_database.user_exists(recipient_id): <span class="hljs-keyword">raise</span> Exception(<span class="hljs-string">"Recipient does not exist"</span>) message_id = message_queue.enqueue_message(sender_id, recipient_id, message_text) notification_service.send_notification(recipient_id, <span class="hljs-string">"You have a new message"</span>) <span class="hljs-keyword">return</span> message_id</pre></div><p id="e93c">The problem with this design is that the code is hard to understand and maintain because it's not clear at a glance what the <code>send_message</code> 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.</p><h2 id="83f4">Solution</h2><p id="201d">We can move our <code>send_message</code> 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.</p><div id="4615"><pre><span class="hljs-keyword">class</span> <span class="hljs-title class_">MessagingFacade</span>: <span class="hljs-keyword">def</span> <span class="hljs-title function_">init</span>(<span class="hljs-params">self, user_database, message_queue, notification_service</span>): self._user_database = user_database self._message_queue = message_queue self._notification_service = notification_service

<span class="hljs-keyword">def</span> <span class="hljs-title function_">send_message</span>(<span class="hljs-params">self, sender_id, recipient_id, message_text</span>):
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> self._user_database.user_exists(sender_id):
        <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"Sender does not exist"</span>)
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> self._user_database.user_exists(recipient_id):
        <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"Recipient does not exist"</span>)
    message_id = self._message_queue.enqueue_message(sender_id, recipient_id, message_text)
    self._notification_service.send_notification(recipient_id, <span class="hljs-string">"You have a new message"</span>)
    <span class="hljs-keyword">return</span> message_id

<span class="hljs-keyword">def</span> <span class="hljs-title function_">read_message</span>(<span class="hljs-params">self, user_id, message_id</span>):
    <span class="hljs-string">"""
    Returns the text of a message for a given user and message id
    """</span>
    <span class="hljs-keyword">return</span> self._message_queue.get_message_text(user_id, message_id)

<span class="hljs-keyword">def</span> <span class="hljs-title function_">delete_message</span>(<span class="hljs-params">self, user_id, message_id</span>):
    <span class="hljs-string">"""
    Deletes a message for a given user and message id
    """</span>
    self._message_queue.delete_message(user_id, message_id)

<span class="hljs-keyword">def</span> <span class="hljs-title function_">list_messages</span>(<span class="hljs-params">self, user_id</span>):
    <span class="hljs-string">"""
    Returns a list of messages for a given user
    """</span>
    <span class="hljs-keyword">return</span> self._message_queue.list_messages(user_id)

<span class="hljs-keyword">def</span> <span class="hljs-title function_">add_user</span>(<span class="hljs-params">self, use

Options

r_id, user_name</span>): <span class="hljs-string">""" Add user to the user_database """</span> self._user_database.add_user(user_id, user_name)</pre></div><p id="c667">This way, instead of calling each object independently, we can just create a Facade with these objects and call methods of the Facade.</p><p id="3975">The Facade completely opacify your system, making things easier to understand.</p><h2 id="95e6">Application</h2><ul><li>Hiding the complexity of a system behind a simpler interface, making it easier to use for clients.</li><li>Providing a single point of entry for a complex system, making it easier to maintain and extend.</li><li>Decoupling a client from the details of a subsystem, allowing the client to change the implementation of the subsystem without affecting its behavior.</li></ul><h2 id="8b86">Advantages</h2><ul><li>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.</li><li>Makes the subsystem more reusable, since it is no longer tied to a specific client.</li><li>Makes the client code more readable and understandable, since it only needs to interact with a simplified interface.</li></ul><h2 id="3eec">Disadvantages</h2><ul><li>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.</li><li>Can lead to increased complexity if the facade interface is not designed carefully.</li><li>May add an additional level of indirection, which can make the system slower.</li></ul><h2 id="8ecd">Final Note</h2><p id="da44">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.</p><p id="ea4b">I use it sometimes when working with multiple third-party libraries that I want to simplify to mix into one interface.</p><p id="8bee"><i>To explore the other stories of this series, click below!</i></p><div id="ad69" class="link-block"> <a href="https://readmedium.com/improve-your-python-coding-with-design-patterns-4430a5de82b5"> <div> <div> <h2>Improve your Python Coding with Design Patterns</h2> <div><h3>Common Design Solutions to Solve Common Design Problems</h3></div> <div><p>medium.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*OHUcHuz31VW1jXvW)"></div> </div> </div> </a> </div><p id="dc9e"><i>To explore more of my Python stories, click <a href="https://readmedium.com/tech-aa824bad0d67">here</a>! You can also access all my content by checking <a href="https://readmedium.com/about-me-d63607c8c341">this page</a>.</i></p><p id="8692"><i>If you want to be notified every time I publish a new story, subscribe to me via email by clicking <a href="https://medium.com/subscribe/@estebanthi">here</a>!</i></p><p id="7770"><i>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:</i></p><div id="a2d6" class="link-block"> <a href="https://medium.com/@estebanthi/membership"> <div> <div> <h2>Join Medium with my referral link — Esteban Thilliez</h2> <div><h3>Read every story from Esteban Thilliez (and thousands of other writers on Medium). Your membership fee directly…</h3></div> <div><p>medium.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*IoN4BofrwCNWA_bS)"></div> </div> </div> </a> </div></article></body>

Facade Pattern in Python

Photo by JOHN TOWNER on Unsplash

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_id

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_id

The 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:

Design Patterns
Programming
Python
Coding
Software Development
Recommended from ReadMedium