Design Patters: Bridge

Credit: Concepts explained here are based on a book, “増補改訂版Java言語で学ぶデザインパターン入門”.

A bridge pattern builds a bridge between two kinds of classes: one working as abstraction and one working as implantation.

Let’s see a concrete example. Display and CountDisplay work as abstraction or User Interface. These classes delegate actual work to a class working as implementation. Display class functions so as to show some objects. An impl variable given to the constructor is an instance of a class working as an implementation. CountDisplay class is a subclass of Display class and shows some objects for specified times.

# display.py
class Display:
    def __init__(self, impl):
        self._impl = impl

    def open(self):
        self._impl.raw_open()
    
    def print(self):
        self._impl.raw_print()
    
    def close(self):
        self._impl.raw_close()

    def display(self):
        self.open()
        self.print()
        self.close()
# count_display.py
from display import Display

class CountDisplay(Display):
    def __init__(self, impl):
        super().__init__(impl)
    
    def multi_display(self, times):
        self.open()
        for _ in range(times):
            self.print()
        self.close()

Now let’s create classes working as an implementation. DisplayImpl class is an abstract method and StringDisplayImpl class is a subclass of DisplayImpl and implements abstract methods defined in its parent class. This class encapsulates a given string with some characters and prints them out.

# display_impl.py
from abc import ABC, abstractmethod

class DisplayImpl(ABC):
    @abstractmethod
    def raw_open(self):
        pass
    @abstractmethod
    def raw_print(self):
        pass
    @abstractmethod
    def raw_close(self):
        pass
# string_display_impl.py
from display_impl import DisplayImpl

class StringDisplayImpl(DisplayImpl):
    def __init__(self, string):
        self._string = string
        self._width = len(string.encode("utf-8"))
    
    def raw_open(self):
        self.print_line()

    def raw_print(self):
        print(f"|{self._string}|")
    
    def raw_close(self):
        self.print_line()
    
    def print_line(self):
        print("+", end="")
        for _ in range(self._width):
            print("-", end="")
        print("+")

Let’s run this program.

# main.py
from display import Display
from count_display import CountDisplay
from string_display_impl import StringDisplayImpl


def main():
    d1 = Display(StringDisplayImpl("Hello, Japan."))
    d2 = Display(StringDisplayImpl("Hello, World."))
    d3 = CountDisplay(StringDisplayImpl("Hello, Universe."))
    d1.display()
    d2.display()
    d3.display()
    d3.multi_display(5)

if __name__ == "__main__":
    main()
$ python main.py 
+-------------+
|Hello, Japan.|
+-------------+
+-------------+
|Hello, World.|
+-------------+
+----------------+
|Hello, Universe.|
+----------------+
+----------------+
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
+----------------+

One benefit of a Bridge pattern: Extending or modifying code in classes working as abstraction does not affect classes working as implementation and vice versa which means that there is no need to modify a class working as a different function. This is possible since each class is independent of each other.

Leave a comment