Bridge Pattern in Python: Separate Abstraction from Implementation
Implement the Bridge design pattern in Python so that an abstraction (remote control) can operate on different device implementations independently.
Python code
99 linesclass RemoteControl:
"""Abstraction: controls a device without knowing implementation details."""
def __init__(self, device):
self.device = device
def toggle_power(self):
if self.device.is_enabled():
self.device.disable()
return "Power off"
else:
self.device.enable()
return "Power on"
def volume_up(self):
volume = self.device.get_volume()
self.device.set_volume(volume + 10)
return f"Volume: {self.device.get_volume()}"
class AdvancedRemoteControl(RemoteControl):
"""Refined abstraction: adds mute functionality."""
def mute(self):
self.device.set_volume(0)
return "Muted"
class Device:
"""Implementation interface."""
def enable(self):
pass
def disable(self):
pass
def is_enabled(self):
pass
def get_volume(self):
pass
def set_volume(self, percent):
pass
class TV(Device):
"""Concrete implementation for TV."""
def __init__(self):
self._on = False
self._volume = 30
def enable(self):
self._on = True
def disable(self):
self._on = False
def is_enabled(self):
return self._on
def get_volume(self):
return self._volume
def set_volume(self, percent):
self._volume = max(0, min(100, percent))
class Radio(Device):
"""Concrete implementation for Radio."""
def __init__(self):
self._on = False
self._volume = 20
def enable(self):
self._on = True
def disable(self):
self._on = False
def is_enabled(self):
return self._on
def get_volume(self):
return self._volume
def set_volume(self, percent):
self._volume = max(0, min(100, percent))
if __name__ == "__main__":
tv = TV()
remote = AdvancedRemoteControl(tv)
print(remote.toggle_power())
print(remote.volume_up())
print(remote.mute())
radio = Radio()
basic_remote = RemoteControl(radio)
print(basic_remote.toggle_power())
print(basic_remote.volume_up())
Output
Power on
Volume: 40
Muted
Power on
Volume: 30
How it works
The Bridge pattern decouples an abstraction from its implementation so they can evolve independently. Here, RemoteControl defines the high-level operations and holds a reference to a Device interface. Concrete devices (TV, Radio) implement the interface, allowing any remote to control any device. This composition over inheritance keeps changes to one side from rippling to the other.
Common mistakes
- Forgetting to pass a device instance when creating a remote control (TypeError or None attribute access).
- Implementing device logic directly inside the remote class, which defeats the purpose of the bridge.
- Not using an interface class, leading to tight coupling between remote and concrete devices.
Variations
- Use `Protocol` from typing to define the device interface (structural subtyping).
- Add more refined abstractions like `AdvancedRemoteControl` with additional methods while keeping devices unchanged.
Real-world use cases
- GUI frameworks where the same window abstraction drives different widgets on Windows/macOS.
- Database abstraction layers that let an app switch from MySQL to PostgreSQL without changing business logic.
- Cross-platform UI controllers that operate devices like TVs and projectors via the same remote interface.
Sponsored
More from OOP & classes
- Add property getter setter validation in Python easy
- Binary Tree Inorder Traversal in Python easy
- Borg pattern shared state in Python medium
- Composable Predicates with the &, |, ~ Operators in Python medium
- Composition over Inheritance: How to Build a Wallet Account in Python easy
- Compute Derived Fields with @dataclass __post_init__ in Python easy
Keep learning
Related tutorials and quizzes for this topic.