StructuralPythonverifiedVerified
Bridge Pattern in Python
Decouples an abstraction from its implementation so that the two can vary independently.
How to Implement the Bridge Pattern in Python
1Step 1: Define the implementation interface
from abc import ABC, abstractmethod
class Renderer(ABC):
@abstractmethod
def render_circle(self, radius: float) -> None: ...
@abstractmethod
def render_square(self, side: float) -> None: ...2Step 2: Create concrete implementations
class VectorRenderer(Renderer):
def render_circle(self, r: float) -> None:
print(f"Drawing circle (r={r}) as vector")
def render_square(self, s: float) -> None:
print(f"Drawing square (s={s}) as vector")
class RasterRenderer(Renderer):
def render_circle(self, r: float) -> None:
print(f"Drawing circle (r={r}) as pixels")
def render_square(self, s: float) -> None:
print(f"Drawing square (s={s}) as pixels")3Step 3: Define the abstraction hierarchy
class Shape(ABC):
def __init__(self, renderer: Renderer) -> None:
self._renderer = renderer
@abstractmethod
def draw(self) -> None: ...
@abstractmethod
def resize(self, factor: float) -> None: ...
class Circle(Shape):
def __init__(self, renderer: Renderer, radius: float) -> None:
super().__init__(renderer)
self._radius = radius
def draw(self) -> None:
self._renderer.render_circle(self._radius)
def resize(self, factor: float) -> None:
self._radius *= factor
class Square(Shape):
def __init__(self, renderer: Renderer, side: float) -> None:
super().__init__(renderer)
self._side = side
def draw(self) -> None:
self._renderer.render_square(self._side)
def resize(self, factor: float) -> None:
self._side *= factor4Step 4: Mix and match abstractions with implementations
vector = VectorRenderer()
raster = RasterRenderer()
Circle(vector, 5).draw() # Drawing circle (r=5) as vector
Square(raster, 10).draw() # Drawing square (s=10) as pixels"""Notification system: abstraction (Notification) bridged to channel implementations."""
from abc import ABC, abstractmethod
import logging
logger = logging.getLogger(__name__)
# [step] Define the implementation interface (channels)
class NotificationChannel(ABC):
@abstractmethod
async def send(self, recipient: str, subject: str, body: str) -> None: ...
class EmailChannel(NotificationChannel):
async def send(self, to: str, subject: str, body: str) -> None:
logger.info("[Email -> %s] Subject: %s\n%s", to, subject, body)
class SmsChannel(NotificationChannel):
async def send(self, to: str, _subject: str, body: str) -> None:
truncated = body[:157] + "..." if len(body) > 160 else body
logger.info("[SMS -> %s] %s", to, truncated)
class PushChannel(NotificationChannel):
async def send(self, to: str, subject: str, body: str) -> None:
logger.info("[Push -> %s] %s: %s", to, subject, body[:80])
# [step] Define the abstraction hierarchy (notification types)
class Notification(ABC):
def __init__(self, channel: NotificationChannel) -> None:
self._channel = channel
@abstractmethod
async def notify(self, recipient: str, data: dict[str, str]) -> None: ...
class AlertNotification(Notification):
async def notify(self, recipient: str, data: dict[str, str]) -> None:
subject = f"Alert: {data.get('event', 'Unknown event')}"
body = (
f"An alert was triggered at {data.get('timestamp', 'N/A')}.\n"
f"Details: {data.get('details', 'n/a')}"
)
await self._channel.send(recipient, subject, body)
class WelcomeNotification(Notification):
async def notify(self, recipient: str, data: dict[str, str]) -> None:
subject = "Welcome to the platform!"
body = f"Hi {data.get('name', 'there')}, your account is ready. Enjoy!"
await self._channel.send(recipient, subject, body)
# [step] Mix and match: same notification type, different channels
async def main() -> None:
email_alert = AlertNotification(EmailChannel())
sms_alert = AlertNotification(SmsChannel())
push_welcome = WelcomeNotification(PushChannel())
alert_data = {
"event": "CPU spike",
"timestamp": "2026-03-11T14:00:00Z",
"details": "CPU usage exceeded 95% for 5 minutes",
}
await email_alert.notify("[email protected]", alert_data)
await sms_alert.notify("+14155550100", alert_data)
await push_welcome.notify("user-987", {"name": "Alice"})
if __name__ == "__main__":
import asyncio
asyncio.run(main())Bridge Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
Bridge Pattern in the Real World
“A TV remote (abstraction) works with any brand of television (implementation) because both sides communicate through an agreed IR protocol (the bridge). Samsung and LG can redesign their TVs, and universal remote makers can add new button layouts—neither side needs to know about the other’s internal design, only the shared protocol.”