Structural Patterns  «Prev 

Bridge Pattern Code in Java

Explain the purpose of the "Structural Pattern" known as the Bridge Pattern. Provide an example using Java SE 17.
Bridge Pattern (Structural Pattern) The "Bridge Pattern" is a structural design pattern that decouples an abstraction from its implementation, allowing both to evolve independently. It achieves this by placing the abstraction and implementation in separate class hierarchies and connecting them through composition instead of inheritance.
Purpose of the Bridge Pattern:
  • To decouple an abstraction from its implementation, promoting flexibility and maintainability.
  • To enable independent extension of abstraction and implementation without affecting each other.
  • To reduce class explosion that results from multiple inheritance or deep class hierarchies.
  • To improve code readability and scalability by separating concerns.

Example: Implementing the Bridge Pattern in Java SE 17 Let's consider a Shape Drawing System where different shapes (e.g., Circle, Rectangle) can be drawn in different colors (e.g., Red, Blue). Instead of tightly coupling shape classes with color variations, we use the Bridge Pattern to separate Shape and Color implementations.
Step 1: Define the Implementor Interface (Color)
// Implementor - Color
interface Color {
    void applyColor();
}

Step 2: Implement Concrete Implementors for Different Colors
// Concrete Implementor - Red Color
class RedColor implements Color {
    @Override
    public void applyColor() {
        System.out.println("Applying Red Color");
    }
}

// Concrete Implementor - Blue Color
class BlueColor implements Color {
    @Override
    public void applyColor() {
        System.out.println("Applying Blue Color");
    }
}

Step 3: Define the Abstraction (Shape)
// Abstraction - Shape
abstract class Shape {
    protected Color color; // Composition - Bridge to Implementor

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

    abstract void draw();
}

Step 4: Implement Refined Abstractions (Specific Shapes)
// Refined Abstraction - Circle
class Circle extends She {
    public Circle(Color color) {
        super(color);
    }

    @Override
    public void draw() {
        System.out.print("Drawing Circle with ");
        color.applyColor();
    }
}

// Refined Abstraction - Rectangle
class Rectangle extends Shape {
    public Rectangle(Color color) {
        super(color);
    }

    @Override
    public void draw() {
        System.out.print("Drawing Rectangle with ");
        color.applyColor();
    }
}

Step 5: Demonstrate the Bridge Pattern Usage
public class BridgePatternDemo {
    public static void main(String[] args) {
        Shape redCircle = new Circle(new RedColor());
        Shape blueRectangle = new Rectangle(new BlueColor());

        redCircle.draw();      // Output: Drawing Circle with Applying Red Color
        blueRectangle.draw();  // Output: Drawing Rectangle with Applying Blue Color
    }
}

Key Benefits of the Bridge Pattern:
  • ✅ Decouples abstraction and implementation → Shapes and colors can evolve independently.
  • ✅ Avoids class explosion → Instead of creating multiple shape-color combinations as subclasses, we use composition.
  • ✅ Promotes code maintainability and flexibility → New shapes and colors can be added without modifying existing classes.
The Java code below displays the pattern in action using the remote control example from Head First Design Patterns.
First, we have our TV implementation interface:

package com.java.structural.bridge;
//Implementor 
public interface TV{
	public void on();
	public void off(); 
	public void tuneChannel(int channel);
}

And then we create two specific implementations - one for Sony and one for Philips:
package com.java.structural.bridge;
//Concrete Implementor 
public class Sony implements TV{
  public void on(){
    //Sony specific on
	}
  public void off(){
    //Sony specific off
  }
  public void tuneChannel(int channel){
    //Sony specific tuneChannel
  }
}

package com.java.structural.bridge;
//Concrete Implementor 
public class Philips implements TV{
  public void on(){
    //Philips specific on
	}
  public void off()	{
    //Philips specific off
  }
  public void tuneChannel(int channel){
    //Philips specific tuneChannel
	}
}

These classes deal with the specific implementations of the TV from each vendor.
Now, we create a remote control abstraction to control the TV:

package com.java.structural.bridge;
//Abstraction
public abstract class RemoteControl{
  private TV implementor; 
  public void on(){
    implementor.on();
  }
  public void off(){
    implementor.off();
  }
  public void setChannel(int channel){
    implementor.tuneChannel(channel);
  }
}

As the remote control holds a reference to the TV, it can delegate the methods through to the interface.
But what if we want a more specific remote control, one that has the
+ / - 
buttons for moving through the channels? All we need to do is extend our RemoteControl abstraction to contain these concepts:

package com.java.structural.bridge;
//Refined abstraction
public class ConcreteRemote extends RemoteControl{
  private int currentChannel; 
  public void nextChannel(){
    currentChannel++;
    setChannel(currentChannel);
  }
  public void prevChannel(){
    currentChannel--;
    setChannel(currentChannel);
  }  
}

Disadvantage of the Bridge Pattern

One of the major disadvantages of this pattern is that, because it provides flexibility, the complexity is increased.
There is also possible performance issues with the indirection of messages. The abstraction needs to pass messages along to the implementator for the operation to get executed.