Bridge design pattern in Java and Python

Structural Design Patterns: Structural patterns provide different ways to create a class structure, for example using inheritance and composition to create a large object from small objects. Following design patterns come under this category.

  • Adapter Pattern
  • Composite Pattern
  • Proxy Pattern
  • Flyweight Pattern
  • Facade Pattern
  • Bridge Pattern
  • Decorator Pattern

When we have interface hierarchies in both interfaces as well as implementations, then Bridge design pattern is used to decouple the interfaces from implementation and hiding the implementation details from the client programs.

According to GoF bridge design pattern is:

Decouple an abstraction from its implementation so that the two can vary independently.

This pattern involves an interface which acts as a bridge which makes the functionality of concrete classes independent from interface implementer classes. Both types of classes can be altered structurally without affecting each other.

In the bridge pattern, we separate an abstraction and its implementation and develop separate inheritance structures for both the abstraction and the implementer. The Bridge pattern’s intent is to decouple an abstraction from its implementation so that the two can vary independently. It puts the abstraction and implementation into two different class hierarchies so that both can be extend independently.

Before applying Bridge design pattern:

dp_bridge1.gif

After applying Bridge design pattern:

dp_bridge2.gif

The figure below shows a UML class diagram for the Bridge pattern:

dp_bridge.png

The participants classes in the bridge pattern are:

  • Abstraction. Abstraction defines abstraction interface.
  • AbstractionImpl. Implements the abstraction interface using a reference to an object of type Implementor.
  • Implementor. Implementor defines the interface for implementation classes. This interface does not need to correspond directly to abstraction interface and can be very different. Abstraction imp provides an implementation in terms of operations provided by Implementor interface.
  • ConcreteImplementor1, ConcreteImplementor2. Implements the Implementor interface.

Summary of Bridge design pattern:

  • Creates two different hierarchies. One for abstraction and another for implementation.
  • Avoids permanent binding by removing the dependency between abstraction and implementation.
  • We create a bridge that coordinates between abstraction and implementation.
  • Abstraction and implementation can be extended separately.
  • Should be used when we have needed to switch implementation at runtime.
  • Client should not be impacted if there is modification in implementation of abstraction.
  • Best used when you have multiple implementations.

We are demonstrating use of Bridge pattern via following example in which a circle can be drawn in different colors using same abstract class method but different bridge implementer classes.

Java

Following example includes following elements:

  • Shape (Abstraction)
  • Rectangle (RefinedAbstraction)
  • Circle (RefinedAbstraction)
  • Color (Implementor)
  • RedColor (ConcreteImplementor)
  • BlueColor (ConcreteImplementor)

We have two inheritance hierarchies here i.e. Shape and Color. Now with help of Bridge design pattern both can vary independently.

This is our abstraction class.

abstract class Shape {
    Color color;
    Shape(Color color)   {
        this.color = color;
    }
    abstract public void colorIt();
}
public class Rectangle extends Shape {
    Rectangle(Color color) {
        super(color);
    }

    public void colorIt() {
        System.out.print("Rectangle filled with ");
        color.fillColor();
    }
}
public class Circle extends Shape{
    Circle(Color color) {
        super(color);
    }

    public void colorIt() {
        System.out.print("Circle filled with ");
        color.fillColor();
    }
}

This is our implementor class.

public interface Color {
    public void fillColor();
}
public class RedColor implements Color {
    public void fillColor() {
        System.out.println("red color");
    }
}
public class BlueColor implements Color {
    public void fillColor() {
        System.out.println("blue color");
    }
}
public class BridgeDesignPatternMain {
    public static void main(String[] args) {
        Shape s1 = new Rectangle(new RedColor());
        s1.colorIt();

        Shape s2 = new Circle(new BlueColor());
        s2.colorIt();
    }
}

Python 3

import abc

class Color(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def fill_color(self):
        pass

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

    @abc.abstractmethod
    def color_it(self):
        pass

class Rectangle(Shape):
    def __init__(self, color):
        super(Rectangle, self).__init__(color)

    def color_it(self):
        print("Rectangle filled with ", end="")
        self.color.fill_color()

class Circle(Shape):
    def __init__(self, color):
        super(Circle, self).__init__(color)

    def color_it(self):
        print("Circle filled with ", end="")
        self.color.fill_color()

class RedColor(Color):
    def fill_color(self):
        print("red color")

class BlueColor(Color):
    def fill_color(self):
        print("blue color")

if __name__ == '__main__':
    s1 = Rectangle(RedColor())
    s1.color_it()

    s2 = Circle(BlueColor())
    s2.color_it()
comments powered by Disqus