The Bridge design pattern suggests connecting various components present in an application through abstraction.
The main motivation behind Bridge pattern is, Bridge Design Pattern prevents a “Cartesian Product” complexity explosion.
A Bridge suggests implementation that decouples an interface(hierarchy) from an implementation(hierarchy). These are the formal definitions, which complicate the scenario. Let’s understand everything with an example.
Example:
Consider there is a shape abstract class, for various shapes(i.e. square, circle, etc):
class shape:
def no_of_sides(self, sides):
self.sides = sides
@abstractmethod
def Find_area(self):
pass
Expanding Shape
class in – Square
and Circle
concrete classes:
class Square(shape):
def __init__(self, width, sides):
self.width = width
super().no_of_sides(sides)
def Find_area(self):
return self.width ** 2
class Circle(shape):
def __init__(self, radius, sides):
self.width = radius
super().no_of_sides(0)
def Find_area(self):
return 3.14*self.radius**2
Now, if we want to expand the shapes hierarchy in color, that is, to have the Red and Blue shapes. We have to create 4 combinations of subclasses, like BlueCircle
and RedSquare
.
class RedSquare(shape):
pass
class BlueSquare(shape):
pass
class RedCircle(shape):
pass
class RedCircle(shape):
pass
What if we add a Triangle shape? The number of combinations possible will be 8 now. The increasing number of combinations is the complexity explosion. Here number combinations are increasing in powers of 2.
The problem can be resolved by decoupling the interface, which is here Shape
and its implementations, that are RedSquare
, BlueCircle
etc. Decoupling simply means creating a new interface for the implementations themselves. Interface for color.
class Color:
def fill_color(self):
pass
Now, subclasses for Color:
class RedColor(Color):
def __init__(self):
self.fill_color()
def fill_color(self):
self._color = 'Red'
With separate implementation for both hierarchies adding new color classes, won’t need to alter the shape classes, and vice versa.
Full implementation will be like this:
from abc import abstractmethod
"""Abstract class for Shapes"""
class shape:
def no_of_sides(self, sides):
self.sides = sides
def set_color(self, color):
self.color = color._color
@abstractmethod
def Find_area(self):
pass
class Square(shape):
def __init__(self, width, sides, color):
self.width = width
super().no_of_sides(sides)
super().set_color(color)
def Find_area(self):
return self.width ** 2
"""Method to get information of the class."""
def __str__(self):
return f'Shape: {type(self).__name__}, Side: {self.sides}, Colour: {self.color}, Area: {self.Find_area()}'
class Circle(shape):
def __init__(self, radius, sides, color):
self.radius = radius
super().no_of_sides(0)
super().set_color(color)
def Find_area(self):
return 3.14*self.radius**2
def __str__(self):
return f'Shape: {type(self).__name__}, Side: {self.sides}, Colour: {self.color}, Area: {self.Find_area()}'
"""Interface for implementation"""
class Color:
def fill_color(self):
pass
"""Subclass for implementation interface."""
class RedColor(Color):
def __init__(self):
self.fill_color()
def fill_color(self):
self._color = 'Red'
class BlueColor(Color):
def __init__(self):
self.fill_color()
def fill_color(self):
self._color = 'Blue'
#Driver Code
objS = Square(4, 4, RedColor())
objC = Circle(2, 0, BlueColor())
print(objS, objC, sep="\n")
Output:
Shape: Square, Side: 4, Colour: Red, Area: 16
Shape: Circle, Side: 0, Colour: Blue, Area: 12.56
Bridge Pattern can help in:
- Where class contains a number of functionalities and needs to be divided into individual classes with specific funtionalities.
- Implementation of entities needs to be changed during the execution of a program.
Pros and Cons of Bridge Pattern
Pros
- Allows building platform-independent programs.
- Hides unnecessary or dangerous implementation details from the client code.
Cons
- Complicates the program code due to the introduction of additional classes.