avatarEsteban Thilliez

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

5218

Abstract

hljs-subst">{color}</span>"</span>)

<span class="hljs-keyword">if</span> name == <span class="hljs-string">"main"</span>: canvas = Canvas() shape = Shape(<span class="hljs-string">"red"</span>) shape.draw(canvas)</pre></div><h2 id="2c59">Solution</h2><p id="ae82">To solve these problems, you can use the Bridge design pattern to decouple the <code>Shape</code> class from the <code>Canvas</code> class. Here is how you could refactor the code using the Bridge design pattern:</p><div id="547c"><pre><span class="hljs-keyword">class</span> <span class="hljs-title class_">Shape</span>: <span class="hljs-keyword">def</span> <span class="hljs-title function_">init</span>(<span class="hljs-params">self, color</span>): self.color = color

<span class="hljs-keyword">class</span> <span class="hljs-title class_">LineShape</span>(<span class="hljs-title class_ inherited__">Shape</span>): <span class="hljs-keyword">def</span> <span class="hljs-title function_">draw</span>(<span class="hljs-params">self, canvas</span>): canvas.draw_line(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>, self.color)

<span class="hljs-keyword">class</span> <span class="hljs-title class_">Canvas</span>: <span class="hljs-keyword">def</span> <span class="hljs-title function_">draw_line</span>(<span class="hljs-params">self, x1, y1, x2, y2, color</span>): <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Drawing line from (<span class="hljs-subst">{x1}</span>, <span class="hljs-subst">{y1}</span>) to (<span class="hljs-subst">{x2}</span>, <span class="hljs-subst">{y2}</span>) with color <span class="hljs-subst">{color}</span>"</span>)

<span class="hljs-keyword">if</span> name == <span class="hljs-string">"main"</span>: canvas = Canvas() shape = LineShape(<span class="hljs-string">"red"</span>) shape.draw(canvas)</pre></div><p id="5c05">In this refactored design, the <code>Shape</code> class represents the abstraction (the geometric shape), and the <code>LineShape</code> class represents the implementation (the color of the shape). The <code>LineShape</code> class is responsible for drawing the shape on the canvas, using the <code>color</code> attribute of the <code>Shape</code> class. This decouples the <code>Shape</code> class from the <code>Canvas</code> class, allowing the two to vary independently.</p><p id="7d78">Now, you will probably say that the <code>LineShape</code> class is coupled with the <code>Canvas</code> class. And that’s true. But where is the problem?</p><p id="4304">The <code>LineShape</code> class is the implementation. It doesn’t represent the abstraction, so it’s not a problem.</p><p id="8003">This pattern is a bit tricky to understand, let’s check another example.</p><p id="0611">Suppose you are building a system that allows users to send messages to each other. The system supports multiple message delivery channels, such as email, SMS, and push notifications.</p><p id="65e5">You could use the Bridge design pattern to decouple the message delivery from the message itself:</p><div id="924f"><pre><span class="hljs-keyword">class</span> <span class="hljs-title class_">Message</span>: <span class="hljs-keyword">def</span> <span class="hljs-title function_">init</span>(<span class="hljs-params">self, text, delivery_channel</span>): self.text = text self.delivery_channel = delivery_channel

<span class="hljs-keyword">def</span> <span class="hljs-title function_">send</span>(<span class="hljs-params">self</span>):
    self.delivery_channel.send(self.text)

<span class="hljs-keyword">class</span> <span class="hljs-title class_">EmailDelivery</span>: <span class="hljs-keyword">def</span> <span class="hljs-title function_">send</span>(<span class="hljs-params">self, text</span>): <span class="hljs-comment"># Code to send an email with the specified text</span> <span class="hljs-keyword">pass</span>

<span class="hljs-keyword">class</span> <span class="hljs-title class_">SMSDelivery</span>: <span class="hljs-keyword">def</span> <span class="hljs-title function_">send</span>(<span class="hljs-params">self, text</span>): <span class="hljs-comment"># Code to send an SMS with the specified text</span> <span class="hljs-keyword">pass</span>

<span class="hljs-keyword">class</span> <span class="hljs-title class_">PushNotificationDelivery</span>: <span class="hljs-keyword">def</span> <span class="hljs-title function_">send</span>(<span class="hljs-params">self, text</span>): <span class="hljs-comment"># Code to send a push notification with the specified text</span> <span class="hljs-keyword">pass</span></pre></div><p id="65a4">The <code>Message</code> class represents the abstraction (the message to be sent), and the <code>EmailDelivery</code>, <code>SMSDelivery</code>, and <code>PushNotificationDelivery</code> classes represent the implementation (the delivery channel for the message). The <code>Message</code> class is decoupled from the delivery channel imp

Options

lementation, because it only calls the <code>send</code> method of the <code>delivery_channel</code> object, without knowing the specifics of how the message is delivered.</p><p id="dd16">This design allows you to use the same message in different delivery channels, without having to modify the message itself. For example, you could create a message and send it via email, SMS, and push notification.</p><p id="bd35">The code without using the Bridge pattern would look like this:</p><div id="f27a"><pre><span class="hljs-keyword">class</span> <span class="hljs-title class_">Message</span>: <span class="hljs-keyword">def</span> <span class="hljs-title function_">init</span>(<span class="hljs-params">self, text, delivery_channel</span>): self.text = text self.delivery_channel = delivery_channel

<span class="hljs-keyword">def</span> <span class="hljs-title function_">send_via_email</span>(<span class="hljs-params">self</span>):
    <span class="hljs-comment"># Code to send an email with the specified text</span>
    <span class="hljs-keyword">pass</span>

<span class="hljs-keyword">def</span> <span class="hljs-title function_">send_via_sms</span>(<span class="hljs-params">self</span>):
    <span class="hljs-comment"># Code to send an SMS with the specified text</span>
    <span class="hljs-keyword">pass</span>

<span class="hljs-keyword">def</span> <span class="hljs-title function_">send_via_push_notification</span>(<span class="hljs-params">self</span>):
    <span class="hljs-comment"># Code to send a push notification with the specified text</span>
    <span class="hljs-keyword">pass</span></pre></div><h2 id="78b7">Application</h2><ul><li>When you want to use the same functionality in different contexts, and you want to be able to change the implementation without affecting the abstraction.</li><li>When you want to decouple an abstraction from its implementation, allowing the two to vary independently.</li><li>When you want to reduce the complexity of a system by separating the abstraction from the implementation.</li><li>When you want to make a system more maintainable and easier to understand by focusing on one aspect of the system at a time.</li></ul><h2 id="c2be">Advantages</h2><ul><li>Decouples an abstraction from its implementation, allowing the two to vary independently.</li><li>Makes the system more maintainable and easier to understand if you take the time to understand the additional classes.</li><li>Increases flexibility, as it allows you to change the implementation without affecting the abstraction.</li></ul><h2 id="a4fc">Disadvantages</h2><ul><li>Requires the creation of additional classes, which can eventually make the system more difficult to understand.</li><li>Introduces a slight performance overhead due to the additional layer of abstraction</li></ul><h2 id="b8c0">Final Note</h2><p id="4289">It’s important to note that the Bridge pattern is not a one-size-fits-all solution, and you should only use it when it makes sense for your specific use case.</p><p id="787b">As with any design pattern, it’s important to carefully consider the trade-offs and decide whether the benefits of using the pattern outweigh the added complexity and overhead.</p><p id="ef45"><i>To explore the other stories of this story, 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>

Bridge Design Pattern in Python

Photo by Modestas Urbonas 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 Bridge design pattern is a structural pattern that divides an object’s interface from its implementation, allowing the two to vary independently. This pattern is used to decouple an abstraction from its implementation so that the two can vary independently.

The Bridge design pattern is often used when you want to create a relationship between two classes that are structurally unrelated but share some kind of functionality. The pattern allows you to create a “bridge” between the two classes, which allows you to use the functionality of one class in the other class.

Problems the Bridge can Solve

Suppose you have a class called Shape that represents a geometric shape. The Shape class has a method called draw that takes a Canvas object as an argument and draws the shape on the canvas. The Canvas class has a method called draw_line that draws a line on the canvas.

Here is the initial implementation of the Shape and Canvas classes:

class Shape:
    def draw(self, canvas):
        pass

class Canvas:
    def draw_line(self, x1, y1, x2, y2):
        print(f"Drawing line from ({x1}, {y1}) to ({x2}, {y2})")

Now suppose you want to add support for drawing shapes in different colors. You could do this by adding a color attribute to the Shape class and modifying the draw method to use this attribute when drawing the shape. However, this approach has several problems:

  1. The Shape class is now tightly coupled to the Canvas class, because it directly calls the draw_line method of the Canvas class. This makes it difficult to change the implementation of the Shape class without affecting the Canvas class.
  2. The Shape class is now responsible for both the abstraction (the geometric shape) and the implementation (the color of the shape). This makes the Shape class more complex and harder to understand.
class Shape:
    def __init__(self, color):
        self.color = color

    def draw(self, canvas):
        canvas.draw_line(1, 1, 2, 2, self.color)

class Canvas:
    def draw_line(self, x1, y1, x2, y2, color):
        print(f"Drawing line from ({x1}, {y1}) to ({x2}, {y2}) with color {color}")


if __name__ == "__main__":
    canvas = Canvas()
    shape = Shape("red")
    shape.draw(canvas)

Solution

To solve these problems, you can use the Bridge design pattern to decouple the Shape class from the Canvas class. Here is how you could refactor the code using the Bridge design pattern:

class Shape:
    def __init__(self, color):
        self.color = color


class LineShape(Shape):
    def draw(self, canvas):
        canvas.draw_line(1, 1, 2, 2, self.color)


class Canvas:
    def draw_line(self, x1, y1, x2, y2, color):
        print(f"Drawing line from ({x1}, {y1}) to ({x2}, {y2}) with color {color}")


if __name__ == "__main__":
    canvas = Canvas()
    shape = LineShape("red")
    shape.draw(canvas)

In this refactored design, the Shape class represents the abstraction (the geometric shape), and the LineShape class represents the implementation (the color of the shape). The LineShape class is responsible for drawing the shape on the canvas, using the color attribute of the Shape class. This decouples the Shape class from the Canvas class, allowing the two to vary independently.

Now, you will probably say that the LineShape class is coupled with the Canvas class. And that’s true. But where is the problem?

The LineShape class is the implementation. It doesn’t represent the abstraction, so it’s not a problem.

This pattern is a bit tricky to understand, let’s check another example.

Suppose you are building a system that allows users to send messages to each other. The system supports multiple message delivery channels, such as email, SMS, and push notifications.

You could use the Bridge design pattern to decouple the message delivery from the message itself:

class Message:
    def __init__(self, text, delivery_channel):
        self.text = text
        self.delivery_channel = delivery_channel
        
    def send(self):
        self.delivery_channel.send(self.text)

class EmailDelivery:
    def send(self, text):
        # Code to send an email with the specified text
        pass

class SMSDelivery:
    def send(self, text):
        # Code to send an SMS with the specified text
        pass

class PushNotificationDelivery:
    def send(self, text):
        # Code to send a push notification with the specified text
        pass

The Message class represents the abstraction (the message to be sent), and the EmailDelivery, SMSDelivery, and PushNotificationDelivery classes represent the implementation (the delivery channel for the message). The Message class is decoupled from the delivery channel implementation, because it only calls the send method of the delivery_channel object, without knowing the specifics of how the message is delivered.

This design allows you to use the same message in different delivery channels, without having to modify the message itself. For example, you could create a message and send it via email, SMS, and push notification.

The code without using the Bridge pattern would look like this:

class Message:
    def __init__(self, text, delivery_channel):
        self.text = text
        self.delivery_channel = delivery_channel
        
    def send_via_email(self):
        # Code to send an email with the specified text
        pass
    
    def send_via_sms(self):
        # Code to send an SMS with the specified text
        pass
    
    def send_via_push_notification(self):
        # Code to send a push notification with the specified text
        pass

Application

  • When you want to use the same functionality in different contexts, and you want to be able to change the implementation without affecting the abstraction.
  • When you want to decouple an abstraction from its implementation, allowing the two to vary independently.
  • When you want to reduce the complexity of a system by separating the abstraction from the implementation.
  • When you want to make a system more maintainable and easier to understand by focusing on one aspect of the system at a time.

Advantages

  • Decouples an abstraction from its implementation, allowing the two to vary independently.
  • Makes the system more maintainable and easier to understand if you take the time to understand the additional classes.
  • Increases flexibility, as it allows you to change the implementation without affecting the abstraction.

Disadvantages

  • Requires the creation of additional classes, which can eventually make the system more difficult to understand.
  • Introduces a slight performance overhead due to the additional layer of abstraction

Final Note

It’s important to note that the Bridge pattern is not a one-size-fits-all solution, and you should only use it when it makes sense for your specific use case.

As with any design pattern, it’s important to carefully consider the trade-offs and decide whether the benefits of using the pattern outweigh the added complexity and overhead.

To explore the other stories of this story, 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
Python Programming
Python
Programming
Coding
Recommended from ReadMedium