1. Definition

The Bridge Design Pattern decouples 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.

2. Problem Statement

Imagine you are developing a graphics system that needs to draw both shapes and texts. These elements can be drawn in various colors. Without using a design pattern, you’d need a subclass for every shape-color combination, resulting in combinatorial explosion of classes.

3. Solution

The Bridge Pattern allows the Shape and Color hierarchies to evolve independently without each having to subclass the other. This way, adding a new shape or a new color does not affect the existing system.

4. Real-World Use Cases

1. A multi-platform window system where each window can be drawn using various rendering engines.

2. Implementing device drivers where the software interface remains constant, but the hardware interface varies.

5. Implementation Steps

1. Identify the two dimensions independent of each other. In our case, these are Shape and Color.

2. Define an interface for one of the dimensions.

3. For each concrete class of one dimension, define an object of the other dimension.

6. Implementation

// Color interface
public interface Color {
    void applyColor();
}

// Concrete implementations for Color
public class RedColor implements Color {
    public void applyColor(){
        System.out.println("Applying red color");
    }
}

public class BlueColor implements Color {
    public void applyColor(){
        System.out.println("Applying blue color");
    }
}

// Abstraction for Shape
public abstract class Shape {
    protected Color color;

    public Shape(Color c){
        this.color = c;
    }

    abstract public void applyColor();
}

// Concrete implementations for Shape
public class Triangle extends Shape {
    public Triangle(Color c) {
        super(c);
    }

    @Override
    public void applyColor() {
        System.out.print("Triangle filled with color ");
        color.applyColor();
    }
}

public class Square extends Shape {
    public Square(Color c) {
        super(c);
    }

    @Override
    public void applyColor() {
        System.out.print("Square filled with color ");
        color.applyColor();
    }
}

// Client code
public class Client {
    public static void main(String[] args) {
        Shape triangle = new Triangle(new RedColor());
        triangle.applyColor();

        Shape square = new Square(new BlueColor());
        square.applyColor();
    }
}

Output:

Triangle filled with color Applying red color
Square filled with color Applying blue color

Explanation:

The Bridge Pattern decouples the Shape hierarchy from the Color hierarchy. Shapes (Triangle, Square) have a relationship with Color but they delegate the job of coloring to the Color hierarchy instead of having the coloring logic embedded into the Shape class. This decoupling allows adding new shapes or colors without modifying the existing structure.

7. When to use?

1. Use the Bridge pattern when you want to avoid a permanent binding between an abstraction and its implementation.

2. When both the abstractions and their implementations should be extensible by subclassing.

3. Changes in the implementation of an abstraction should have no impact on clients.

4. When you want to hide the implementation of an abstraction completely from clients.