The Bridge Pattern is a structural design pattern that lets you split a large class or a set of closely related classes into two separate hierarchies—Abstraction and Implementation—which can be developed independently.
The Problem: The "Cartesian Product" Explosion
Imagine you are building a Remote Control System for your website to manage smart home devices. You have different types of remotes (Basic and Advanced) and different types of devices (TVs and Radios).
If you use standard inheritance, you end up with a mess:
BasicRemoteTVAdvancedRemoteTVBasicRemoteRadioAdvancedRemoteRadio
If you add a new device (like a Speaker), you have to create two new classes. If you add a new remote type, you have to create three. This is called a Class Explosion.
The Solution: The Bridge
Instead of having one big inheritance tree, you split it.
- Abstraction: The high-level control (the Remote).
- Implementation: The low-level platform-specific logic (the Device).
The Remote "has a" reference to a Device. This reference is the Bridge.
Step-by-Step Java Implementation
1. The Implementation Interface
This defines the basic operations for all devices.
// Device.java
public interface Device {
void setPower(boolean enabled);
void setVolume(int percent);
}
2. Concrete Implementations
Specific logic for different hardware.
// TV.java
public class TV implements Device {
public void setPower(boolean enabled) { System.out.println("TV Power: " + enabled); }
public void setVolume(int percent) { System.out.println("TV Volume set to " + percent + "%"); }
}
// Radio.java
public class Radio implements Device {
public void setPower(boolean enabled) { System.out.println("Radio Power: " + enabled); }
public void setVolume(int percent) { System.out.println("Radio Volume set to " + percent + "%"); }
}
3. The Abstraction
This holds a reference to a Device and defines the interface for the user.
// RemoteControl.java
public class RemoteControl {
protected Device device; // The Bridge
public RemoteControl(Device device) {
this.device = device;
}
public void togglePower() {
device.setPower(true);
}
}
4. Refined Abstraction
A more advanced version of the remote that uses the same device interface.
// AdvancedRemote.java
public class AdvancedRemote extends RemoteControl {
public AdvancedRemote(Device device) {
super(device);
}
public void mute() {
System.out.println("Remote: Muting device");
device.setVolume(0);
}
}
Full Code for Testing
This test shows how we can mix and match remotes and devices freely.
// Save as BridgeTest.java
// 1. Implementation
interface Device {
void turnOn();
void turnOff();
void setVolume(int vol);
}
// 2. Concrete Implementations
class TV implements Device {
public void turnOn() { System.out.println("TV: Powered ON"); }
public void turnOff() { System.out.println("TV: Powered OFF"); }
public void setVolume(int vol) { System.out.println("TV: Volume set to " + vol); }
}
class Radio implements Device {
public void turnOn() { System.out.println("Radio: Powered ON"); }
public void turnOff() { System.out.println("Radio: Powered OFF"); }
public void setVolume(int vol) { System.out.println("Radio: Volume set to " + vol); }
}
// 3. Abstraction
class Remote {
protected Device device;
public Remote(Device device) { this.device = device; }
public void power() { device.turnOn(); }
}
// 4. Refined Abstraction
class AdvancedRemote extends Remote {
public AdvancedRemote(Device device) { super(device); }
public void mute() {
System.out.println("Advanced Remote: Muting...");
device.setVolume(0);
}
}
// 5. Test
public class BridgeTest {
public static void main(String[] args) {
System.out.println("--- Bridge Pattern Test ---\n");
Device myTV = new TV();
Remote basicRemote = new Remote(myTV);
basicRemote.power();
System.out.println("\n--- Switching Device/Remote Combo ---");
Device myRadio = new Radio();
AdvancedRemote advRemote = new AdvancedRemote(myRadio);
advRemote.power();
advRemote.mute();
System.out.println("\n--- Bridge Successful ---");
}
}
Why use Bridge?
- Avoid Class Explosion: You don't need a class for every combination of remote and device.
- Independence: You can update the
AdvancedRemotewithout touching theTVcode, and vice versa. - Hide Implementation: The client only sees the high-level Abstraction (Remote) and doesn't care about the low-level Implementation (Device).