Bridge
The Bridge pattern is a structural design pattern that lets you split an interface into separate interfaces. This pattern is useful when you want to avoid a tight coupling between an abstraction and its implementation, allowing you to vary them independently. It’s particularly effective when you anticipate that both the abstraction and implementation will change in different ways.
In essence, the Bridge introduces an Implementor interface which provides the core functionality, and an Abstractor interface which uses the Implementor to deliver a higher-level abstraction. This decoupling allows for flexibility and extensibility. Different implementations can be swapped without affecting the abstraction, and vice versa.
Usage
The Bridge pattern is commonly used in the following scenarios:
- Database Abstraction: When your application needs to work with different database systems (e.g., MySQL, PostgreSQL, Oracle), you can use the Bridge pattern to isolate the database-specific implementation details from the application’s core logic.
- Graphics Rendering: When you have different rendering engines (e.g., OpenGL, DirectX, SVG), a Bridge pattern allows you to switch between them easily without altering the code that uses them.
- Platform Independence: When application logic must be independent of the underlying operating system (Windows, macOS, Linux), the Bridge can separate platform-specific calls.
- Message Queues: Using different message queue systems (RabbitMQ, Kafka, Redis Pub/Sub) requires the abstraction of the messaging implementation.
Examples
1. Java Virtual Machine (JVM)
The JVM internally uses a Bridge pattern. The Java language specification defines the Abstractor – the bytecode instructions and the Java API. The actual Implementor is the underlying native code execution environment, which differs for each operating system (Windows, macOS, Linux). The JVM bridges the gap between the platform-independent Java bytecode and the platform-dependent hardware instructions.
2. Remote Control with Different Protocols
Consider a remote control that can control different devices. The remote control’s button presses (Abstractor) need to be translated into specific commands for the device. The communication protocol (Implementor) – such as infrared, Bluetooth, or Wi-Fi – can be changed without needing to modify the remote control’s core logic. You could have a RemoteControl class paired with interfaces like InfraredCommandExecutor, BluetoothCommandExecutor, and WiFiCommandExecutor.
java // Implementor interface CommandExecutor { void execute(String command); }
class InfraredCommandExecutor implements CommandExecutor { @Override public void execute(String command) { System.out.println(“Sending infrared command: " + command); } }
class BluetoothCommandExecutor implements CommandExecutor { @Override public void execute(String command) { System.out.println(“Sending bluetooth command: " + command); } }
// Abstractor class RemoteControl { private CommandExecutor executor;
public RemoteControl(CommandExecutor executor) { this.executor = executor; }
public void pressButton(String command) { executor.execute(command); } }
// Example Usage public class BridgeExample { public static void main(String[] args) { RemoteControl irRemote = new RemoteControl(new InfraredCommandExecutor()); irRemote.pressButton(“channelUp”);
RemoteControl btRemote = new RemoteControl(new BluetoothCommandExecutor());
btRemote.pressButton("volumeDown");
} }
Specimens
15 implementationsThe Bridge pattern is a structural design pattern that lets you split an abstraction from its implementation so that objects can have different implementations. This is useful when you want to avoid a tight coupling between abstract and concrete classes, allowing you to change implementations independently without affecting the abstraction.
The code demonstrates a Shape abstraction with Color implementations. Shape has a render() method that delegates to a Color object to actually do the coloring. Different Color implementations (e.g., RedColor, BlueColor) provide different coloring behaviors. This allows us to combine different shapes with different colors without creating a combinatorial explosion of shape-color classes. The use of interfaces (Color) and abstract classes (Shape) is idiomatic Dart for defining contracts and abstractions.
// Define the abstraction
abstract class Shape {
Color color;
Shape(this.color);
void render();
}
// Define the implementation interface
abstract class Color {
void applyColor();
}
// Concrete implementations
class RedColor implements Color {
@override
void applyColor() {
print('Applying red color.');
}
}
class BlueColor implements Color {
@override
void applyColor() {
print('Applying blue color.');
}
}
// Concrete abstractions
class Circle extends Shape {
Circle(Color color) : super(color);
@override
void render() {
print('Rendering a circle with ');
color.applyColor();
}
}
class Square extends Shape {
Square(Color color) : super(color);
@override
void render() {
print('Rendering a square with ');
color.applyColor();
}
}
// Usage
void main() {
final redCircle = Circle(RedColor());
redCircle.render();
final blueSquare = Square(BlueColor());
blueSquare.render();
}
The Bridge pattern is a structural design pattern that lets you split an abstraction from its implementation so that both can vary independently. It’s useful when you have an abstraction with multiple possible implementations, and you want to avoid a combinatorial explosion of classes.
This Scala example demonstrates the Bridge pattern with a Shape abstraction and Color implementations. Shape has a color attribute, which is an instance of a Color trait. Different Color implementations (e.g., RedColor, BlueColor) provide specific coloring behaviors. We can add new shapes or colors without modifying existing ones. The use of traits for Color and case classes for Shape are idiomatic Scala; traits allow for flexible interface implementation, and case classes provide concise data modeling. The shapes delegate coloring to the specific color implementation.
trait Color {
def applyColor(shape: String): String
}
case class RedColor() extends Color {
override def applyColor(shape: String): String = s"Red ${shape}"
}
case class BlueColor() extends Color {
override def applyColor(shape: String): String = s"Blue ${shape}"
}
abstract class Shape(color: Color) {
def draw(): String
def setColor(newColor: Color): Shape
def getColorName(): String = color.getClass.getSimpleName
}
case class Circle(color: Color) extends Shape(color) {
override def draw(): String = color.applyColor("Circle")
}
case class Square(color: Color) extends Shape(color) {
override def draw(): String = color.applyColor("Square")
}
object BridgeExample {
def main(args: Array[String]): Unit = {
val redCircle = Circle(RedColor())
println(redCircle.draw()) // Output: Red Circle
val blueSquare = Square(BlueColor())
println(blueSquare.draw()) // Output: Blue Square
val greenColor = new Color() {
override def applyColor(shape: String): String = s"Green ${shape}"
}
val circleWithGreen = redCircle.setColor(greenColor)
println(circleWithGreen.draw()) // Output: Green Circle
}
}
The Bridge pattern is a structural design pattern that lets you split an abstraction from its implementation so that objects can have different implementations. This is useful when you want to avoid a tight coupling between abstract and concrete classes, allowing you to change implementations independently without affecting the abstraction.
The code demonstrates a Shape abstraction and concrete abstractions like Circle and Rectangle. These rely on an Color interface implemented by RedColor and BlueColor. A shape’s color isn’t tied to its type; you can have a red circle or a blue rectangle. This decoupling is achieved by the Shape classes holding an instance of the Color interface. This approach is idiomatic PHP as it leverages interfaces for loose coupling and allows for flexible composition over inheritance.
<?php
// Color Interface (Implementation)
interface Color
{
public function applyColor(): string;
}
// Concrete Implementations of Color
class RedColor implements Color
{
public function applyColor(): string
{
return "Applying Red Color.";
}
}
class BlueColor implements Color
{
public function applyColor(): string
{
return "Applying Blue Color.";
}
}
// Shape Abstraction
interface Shape
{
public function draw(): string;
}
// Concrete Implementations of Shape
class Circle implements Shape
{
protected Color $color;
public function __construct(Color $color)
{
$this->color = $color;
}
public function draw(): string
{
return "Drawing a Circle " . $this->color->applyColor();
}
}
class Rectangle implements Shape
{
protected Color $color;
public function __construct(Color $color)
{
$this->color = $color;
}
public function draw(): string
{
return "Drawing a Rectangle " . $this->color->applyColor();
}
}
// Client Code
$redCircle = new Circle(new RedColor());
$blueRectangle = new Rectangle(new BlueColor());
echo $redCircle->draw() . PHP_EOL;
echo $blueRectangle->draw() . PHP_EOL;
?>
The Bridge pattern is a structural design pattern that lets you split an abstraction from its implementation so that objects can have different implementations. This is useful when there is a need to avoid a hard coupling between an abstraction and its implementation, allowing both to vary independently.
The code demonstrates a Device abstraction and two concrete implementations: Radio and TV. A RemoteControl acts as the ‘remote’ (the abstraction) and can operate on either implementation without knowing the specifics. This separation is achieved by the RemoteControl holding an instance of the Device interface. Ruby’s duck typing and flexible nature make this pattern a natural fit, as the RemoteControl doesn’t enforce a strict class hierarchy, but relies on the Device objects responding to the necessary methods.
# Device interface
module Device
def button_pressed
raise NotImplementedError
end
def get_volume
raise NotImplementedError
end
def set_volume(volume)
raise NotImplementedError
end
end
# Concrete Implementations
class Radio
include Device
def initialize
@volume = 3
end
def button_pressed
puts "Radio is playing!"
end
def get_volume
@volume
end
def set_volume(volume)
@volume = volume
end
end
class TV
include Device
def initialize
@volume = 5
@channel = 1
end
def button_pressed
puts "TV is displaying!"
end
def get_volume
@volume
end
def set_volume(volume)
@volume = volume
end
end
# Abstraction
class RemoteControl
def initialize(device)
@device = device
end
def button_pressed
@device.button_pressed
end
def volume_up
@device.set_volume(@device.get_volume + 1)
end
def volume_down
@device.set_volume(@device.get_volume - 1)
end
def display_volume
puts "Current volume: #{@device.get_volume}"
end
end
# Usage
radio = Radio.new
tv = TV.new
remote_radio = RemoteControl.new(radio)
remote_tv = RemoteControl.new(tv)
remote_radio.button_pressed
remote_radio.volume_up
remote_radio.display_volume
remote_tv.button_pressed
remote_tv.volume_down
remote_tv.display_volume
The Bridge pattern is a structural design pattern that lets you split an abstraction from its implementation so that the two can vary independently. It’s useful when you have an abstraction with multiple, potentially varying implementations. This avoids a proliferation of classes caused by combining each abstraction and implementation.
The code defines a RenderingImplementation protocol representing the different rendering engines. Concrete implementations like VectorRendering and RasterRendering provide specific rendering logic. The Shape abstraction doesn’t directly implement rendering; instead, it holds an instance of RenderingImplementation. This allows changing the rendering engine at runtime without modifying the Shape classes themselves. This approach is idiomatic Swift because of its strong use of protocols for defining interfaces and dependency injection for loose coupling.
// RenderingImplementation protocol
protocol RenderingImplementation {
func render(shape: Shape)
}
// Concrete implementations
struct VectorRendering: RenderingImplementation {
func render(shape: Shape) {
print("Rendering \(shape.name) as vector graphics.")
}
}
struct RasterRendering: RenderingImplementation {
func render(shape: Shape) {
print("Rendering \(shape.name) as raster graphics.")
}
}
// Abstraction
struct Shape {
let name: String
private let renderer: RenderingImplementation
init(name: String, renderer: RenderingImplementation) {
self.name = name
self.renderer = renderer
}
func draw() {
renderer.render(shape: self)
}
}
// Usage
let vectorRenderer = VectorRendering()
let rasterRenderer = RasterRendering()
let circle = Shape(name: "Circle", renderer: vectorRenderer)
circle.draw()
let square = Shape(name: "Square", renderer: rasterRenderer)
square.draw()
The Bridge pattern is a structural design pattern that lets you split one or several interfaces from their implementation(s), allowing you to vary them independently. It’s beneficial when you have a class that can be configured with different abstractions or implementations without causing tight coupling.
This Kotlin example implements a Bridge with a Device interface (the abstraction) and concrete RemoteControl and DirectControl implementing it. A PowerSupply interface represents the implementation, with Battery and ACAdapter as concrete implementations. The DeviceImpl class bridges the Device and PowerSupply interfaces, allowing different device control methods to work with different power sources without modification. This leverages Kotlin’s interface and class features for a clean, decoupled design, fitting its emphasis on conciseness and readability.
// Abstraction
interface Device {
fun control(): String
fun setPowerSupply(powerSupply: PowerSupply)
}
// Refined Abstraction
class RemoteControl(private var powerSupply: PowerSupply) : Device {
override fun control(): String = "Remote Control: ${powerSupply.powerOn()}"
override fun setPowerSupply(powerSupply: PowerSupply) {
this.powerSupply = powerSupply
}
}
class DirectControl(private var powerSupply: PowerSupply) : Device {
override fun control(): String = "Direct Control: ${powerSupply.powerOn()}"
override fun setPowerSupply(powerSupply: PowerSupply) {
this.powerSupply = powerSupply
}
}
// Implementation
interface PowerSupply {
fun powerOn(): String
}
// Concrete Implementations
class Battery : PowerSupply {
override fun powerOn(): String = "Powered by Battery"
}
class ACAdapter : PowerSupply {
override fun powerOn(): String = "Powered by AC Adapter"
}
// Bridge
class DeviceImpl(device: Device) {
private var device: Device
init {
this.device = device
}
fun operate(): String {
return device.control()
}
}
fun main() {
val battery = Battery()
val acAdapter = ACAdapter()
val remoteControl = RemoteControl(battery)
val directControl = DirectControl(acAdapter)
val remoteDevice = DeviceImpl(remoteControl)
val directDevice = DeviceImpl(directControl)
println(remoteDevice.operate()) // Output: Remote Control: Powered by Battery
remoteControl.setPowerSupply(acAdapter)
println(remoteDevice.operate()) // Output: Remote Control: Powered by AC Adapter
println(directDevice.operate()) // Output: Direct Control: Powered by AC Adapter
}
The Bridge pattern is a structural design pattern that lets you split an abstraction from its implementation so that objects can have different abstract interfaces. This is useful when you want to avoid a rigid hierarchy of classes. In this Rust example, we have an AudioPlayer abstraction and different AudioFormat implementations (MP3, WAV). The AudioPlayer doesn’t care how the audio is formatted; it delegates that to the AudioFormat trait object. This allows us to add new audio formats without modifying the AudioPlayer itself. Rust’s trait objects and ownership system naturally lend themselves to this pattern, promoting flexibility and avoiding tight coupling.
// Define the AudioFormat trait
trait AudioFormat {
fn play(&self, data: &[u8]);
}
// Concrete implementation for MP3
struct MP3Format;
impl AudioFormat for MP3Format {
fn play(&self, data: &[u8]) {
println!("Playing MP3: {:?}", data);
}
}
// Concrete implementation for WAV
struct WAVFormat;
impl AudioFormat for WAVFormat {
fn play(&self, data: &[u8]) {
println!("Playing WAV: {:?}", data);
}
}
// The AudioPlayer abstraction
struct AudioPlayer<'a> {
format: &'a dyn AudioFormat,
}
impl<'a> AudioPlayer<'a> {
fn new(format: &'a dyn AudioFormat) -> Self {
AudioPlayer { format }
}
fn play(&self, data: &[u8]) {
self.format.play(data);
}
}
fn main() {
let mp3_player = AudioPlayer::new(&MP3Format);
mp3_player.play(&[1, 2, 3]);
let wav_player = AudioPlayer::new(&WAVFormat);
wav_player.play(&[4, 5, 6]);
}
The Bridge pattern is a structural design pattern that lets you split an abstraction from its implementation so that objects can have different implementations. This is useful when you want to avoid a tight coupling between abstract and concrete implementations, or when you have multiple independent abstractions that might require different implementations.
The Go implementation defines an Abstraction interface with a method that takes an Implementor interface as a dependency. Different Abstractions can use the same Implementor, and the same Abstraction can switch between different Implementors at runtime. This example presents a Shape abstraction and Color implementation, allowing for combinations like a “Red Circle” or “Blue Square” without creating rigid class hierarchies. Go’s interfaces and composition naturally lend themselves to this pattern, maximizing flexibility without inheritance.
package main
import "fmt"
// Implementor interface
type Implementor interface {
Operation() string
}
// Concrete Implementors
type ConcreteImplementorA struct{}
func (c *ConcreteImplementorA) Operation() string {
return "ConcreteImplementorA's operation"
}
type ConcreteImplementorB struct{}
func (c *ConcreteImplementorB) Operation() string {
return "ConcreteImplementorB's operation"
}
// Abstraction interface
type Abstraction interface {
SetImplementor(impl Implementor)
Operation() string
}
// Concrete Abstractions
type ConcreteAbstraction struct {
implementor Implementor
}
func (c *ConcreteAbstraction) SetImplementor(impl Implementor) {
c.implementor = impl
}
func (c *ConcreteAbstraction) Operation() string {
return c.implementor.Operation()
}
// Example Usage - Shape and Color analogy
type Shape interface {
Draw() string
}
type Circle struct {
color Implementor
}
func (c *Circle) SetColor(color Implementor) {
c.color = color
}
func (c *Circle) Draw() string {
return fmt.Sprintf("Drawing a circle with %s", c.color.Operation())
}
func main() {
implementorA := &ConcreteImplementorA{}
implementorB := &ConcreteImplementorB{}
abstraction := &ConcreteAbstraction{}
abstraction.SetImplementor(implementorA)
fmt.Println(abstraction.Operation()) // Output: ConcreteImplementorA's operation
abstraction.SetImplementor(implementorB)
fmt.Println(abstraction.Operation()) // Output: ConcreteImplementorB's operation
redColor := &ConcreteImplementorA{}
blueColor := &ConcreteImplementorB{}
circle := &Circle{}
circle.SetColor(redColor)
fmt.Println(circle.Draw()) // Output: Drawing a circle with ConcreteImplementorA's operation
circle.SetColor(blueColor)
fmt.Println(circle.Draw()) // Output: Drawing a circle with ConcreteImplementorB's operation
}
The Bridge pattern is a structural design pattern that lets you split an abstraction – such as a class – into separate abstraction and implementation hierarchies. This pattern is used when there’s a need to avoid a tight coupling between an abstraction and its implementation, allowing both to vary independently.
The code demonstrates a Shape abstraction and different Renderer implementations (e.g., drawing with ASCII characters or using a hypothetical graphics library). Shape doesn’t know the specifics of how it’s drawn – it just delegates to the Renderer interface. This is idiomatic C as it utilizes function pointers to achieve polymorphism without classical inheritance, fitting C’s procedural and flexible style. The use of structs for defining both abstractions and implementations is also standard C practice.
#include <stdio.h>
// Implementation (Renderer)
typedef struct Renderer {
void (*draw_circle)(int x, int y, int radius);
void (*draw_square)(int x, int y, int side);
} Renderer;
void ascii_draw_circle(int x, int y, int radius) {
printf("Drawing circle (ASCII) at (%d, %d) with radius %d\n", x, y, radius);
}
void ascii_draw_square(int x, int y, int side) {
printf("Drawing square (ASCII) at (%d, %d) with side %d\n", x, y, side);
}
void graphics_draw_circle(int x, int y, int radius) {
printf("Drawing circle (Graphics) at (%d, %d) with radius %d\n", x, y, radius);
}
void graphics_draw_square(int x, int y, int side) {
printf("Drawing square (Graphics) at (%d, %d) with side %d\n", x, y, side);
}
Renderer ascii_renderer = {ascii_draw_circle, ascii_draw_square};
Renderer graphics_renderer = {graphics_draw_circle, graphics_draw_square};
// Abstraction (Shape)
typedef struct Shape {
Renderer *renderer;
} Shape;
Shape* shape_create(Renderer *renderer) {
Shape *shape = (Shape*)malloc(sizeof(Shape));
shape->renderer = renderer;
return shape;
}
void shape_draw_circle(Shape *shape, int x, int y, int radius) {
shape->renderer->draw_circle(x, y, radius);
}
void shape_draw_square(Shape *shape, int x, int y, int side) {
shape->renderer->draw_square(x, y, side);
}
int main() {
Shape *circle_ascii = shape_create(&ascii_renderer);
Shape *square_ascii = shape_create(&ascii_renderer);
Shape *circle_graphics = shape_create(&graphics_renderer);
shape_draw_circle(circle_ascii, 10, 10, 5);
shape_draw_square(square_ascii, 20, 20, 4);
shape_draw_circle(circle_graphics, 5, 5, 3);
free(circle_ascii);
free(square_ascii);
free(circle_graphics);
return 0;
}
The Bridge pattern is a structural design pattern that lets you split an abstraction from its implementation so that objects can have different abstractions with different implementations, and vice versa. This is useful when you have multiple abstractions and implementations that you want to combine without creating a tight coupling between them.
The code demonstrates this by defining an Abstraction class (e.g., RemoteControl) which uses an interface Implementor (e.g., Device). Concrete Implementors (e.g., TV, Radio) handle specific device operations. Different concrete Abstractions can then use the same Implementor, or an abstraction can switch Implementors at runtime. This C++ implementation favors composition over inheritance, which aligns with modern C++ best practices, providing flexibility and avoiding the rigidity of tight coupling, and showcases polymorphism through the Implementor interface.
#include <iostream>
// Implementor interface
class Device {
public:
virtual void turnOn() = 0;
virtual void turnOff() = 0;
virtual void setChannel(int channel) = 0;
virtual ~Device() = default;
};
// Concrete Implementors
class TV : public Device {
public:
void turnOn() override { std::cout << "TV is turned on.\n"; }
void turnOff() override { std::cout << "TV is turned off.\n"; }
void setChannel(int channel) override { std::cout << "TV set to channel " << channel << ".\n"; }
};
class Radio : public Device {
public:
void turnOn() override { std::cout << "Radio is turned on.\n"; }
void turnOff() override { std::cout << "Radio is turned off.\n"; }
void setChannel(int channel) override { std::cout << "Radio set to frequency " << channel << ".\n"; }
};
// Abstraction
class RemoteControl {
private:
Device* device;
public:
RemoteControl(Device* device) : device(device) {}
void turnOn() { device->turnOn(); }
void turnOff() { device->turnOff(); }
void setChannel(int channel) { device->setChannel(channel); }
// Allows changing the device implementation at runtime
void setDevice(Device* device) { this->device = device; }
};
int main() {
TV tv;
Radio radio;
RemoteControl remote1(&tv);
remote1.turnOn();
remote1.setChannel(10);
remote1.turnOff();
RemoteControl remote2(&radio);
remote2.turnOn();
remote2.setChannel(915);
remote2.turnOff();
// Demonstrate changing the implementation at runtime
remote1.setDevice(&radio);
remote1.turnOn(); // Now controls the radio
remote1.setChannel(987);
return 0;
}
The Bridge pattern is a structural design pattern that lets you split an abstraction from its implementation so that objects can have different abstractions and implementations and can change them independently. This is useful when there’s a possibility of needing to change an abstraction or an implementation without affecting the other.
The example demonstrates a Shape abstraction with Circle and Rectangle concrete abstractions. These abstractions operate on a Renderer interface, implemented concretely by VectorRenderer and RasterRenderer. Each shape can use either renderer without modification, and you can introduce new shapes or renderers without altering existing classes. This C# implementation utilizes interfaces for both the abstraction and implementation to promote loose coupling, a key aspect of good C# design. Specifically, it showcases the principle of “favor composition over inheritance.”
// Abstraction
public interface IShape
{
void Draw(IRenderer renderer);
}
// Concrete Abstractions
public class Circle : IShape
{
public int Radius { get; }
public Circle(int radius)
{
Radius = radius;
}
public void Draw(IRenderer renderer)
{
renderer.RenderCircle(Radius);
}
}
public class Rectangle : IShape
{
public int Width { get; }
public int Height { get; }
public Rectangle(int width, int height)
{
Width = width;
Height = height;
}
public void Draw(IRenderer renderer)
{
renderer.RenderRectangle(Width, Height);
}
}
// Implementation
public interface IRenderer
{
void RenderCircle(int radius);
void RenderRectangle(int width, int height);
}
// Concrete Implementations
public class VectorRenderer : IRenderer
{
public void RenderCircle(int radius)
{
Console.WriteLine($"Drawing a circle with radius {radius} using vector graphics.");
}
public void RenderRectangle(int width, int height)
{
Console.WriteLine($"Drawing a rectangle with width {width} and height {height} using vector graphics.");
}
}
public class RasterRenderer : IRenderer
{
public void RenderCircle(int radius)
{
Console.WriteLine($"Drawing a circle with radius {radius} using raster graphics.");
}
public void RenderRectangle(int width, int height)
{
Console.WriteLine($"Drawing a rectangle with width {width} and height {height} using raster graphics.");
}
}
// Client
public class Client
{
public static void Main(string[] args)
{
Circle circle = new Circle(5);
Rectangle rectangle = new Rectangle(4, 6);
IRenderer vectorRenderer = new VectorRenderer();
IRenderer rasterRenderer = new RasterRenderer();
circle.Draw(vectorRenderer);
rectangle.Draw(rasterRenderer);
}
}
The Bridge pattern is a structural design pattern that lets you split an abstraction from its implementation so that objects can have different implementations. This is useful when there’s a need for multiple independent variations of an abstraction. In this example, we have a Shape abstraction with Circle and Rectangle concrete implementations, and separate Color and Fill implementations for how those shapes are rendered. The Shape classes delegate the actual rendering to a Renderer (either ColorRenderer or FillRenderer). This TypeScript implementation leverages interfaces for loose coupling and type safety, adhering to modern TypeScript best practices.
// Abstraction
interface Shape {
render(): string;
setRenderer(renderer: Renderer);
}
// Implementations (Color and Fill)
interface Renderer {
apply(shape: string): string;
}
class ColorRenderer implements Renderer {
apply(shape: string): string {
return `Rendering ${shape} with color!`;
}
}
class FillRenderer implements Renderer {
apply(shape: string): string {
return `Rendering ${shape} with fill!`;
}
}
// Concrete Abstractions
class Circle implements Shape {
private renderer: Renderer;
constructor() {
this.setRenderer(new ColorRenderer());
}
render(): string {
return this.renderer.apply("Circle");
}
setRenderer(renderer: Renderer): void {
this.renderer = renderer;
}
}
class Rectangle implements Shape {
private renderer: Renderer;
constructor() {
this.setRenderer(new ColorRenderer());
}
render(): string {
return this.renderer.apply("Rectangle");
}
setRenderer(renderer: Renderer): void {
this.renderer = renderer;
}
}
// Usage
const circle = new Circle();
console.log(circle.render()); // Rendering Circle with color!
circle.setRenderer(new FillRenderer());
console.log(circle.render()); // Rendering Circle with fill!
const rectangle = new Rectangle();
console.log(rectangle.render()); // Rendering Rectangle with color!
The Bridge pattern is a structural design pattern that lets you split an abstraction from its implementation so that objects can represent different abstractions supported by different implementations. This is useful when there is a need to avoid a permanent binding between an abstraction and its implementation. This allows for both to be varied independently.
The code demonstrates this by separating a Device (abstraction) from its RemoteControl (implementation). Different device types (TV, Radio) can work with the same remote control types (Basic, Advanced), and vice-versa. This is achieved through interfaces and composition. The Device class holds an instance of a RemoteControl, and each concrete device type utilizes a specific remote concrete implementation. This design is common in JavaScript, leveraging composition over inheritance for flexible relationships between objects.
// Device Abstraction
class Device {
constructor(remoteControl) {
this.remoteControl = remoteControl;
}
operate() {
this.remoteControl.buttonPressed();
}
}
// Concrete Abstractions: TV and Radio
class TV extends Device {
constructor(remoteControl) {
super(remoteControl);
}
display() {
console.log("Displaying TV content.");
}
}
class Radio extends Device {
constructor(remoteControl) {
super(remoteControl);
}
play() {
console.log("Playing radio.");
}
}
// Remote Control Interface
class RemoteControl {
buttonPressed() {
throw new Error("Method 'buttonPressed()' must be implemented.");
}
}
// Concrete Implementations: Basic and Advanced Remote Controls
class BasicRemoteControl extends RemoteControl {
buttonPressed() {
console.log("Basic remote control button pressed.");
}
}
class AdvancedRemoteControl extends RemoteControl {
buttonPressed() {
console.log("Advanced remote control button pressed.");
}
}
// Usage
const tv = new TV(new BasicRemoteControl());
tv.operate(); // Basic remote control button pressed.
tv.display(); // Displaying TV content.
const radio = new Radio(new AdvancedRemoteControl());
radio.operate(); // Advanced remote control button pressed.
radio.play(); // Playing radio.
The Bridge pattern is a structural design pattern that lets you split an abstraction from its implementation so that the two can vary independently. It’s useful when you have an abstraction that can have multiple implementations, and you want to avoid a combinatorial explosion of classes.
This Python example demonstrates a RemoteControl (the Abstraction) that can work with different Device (Implementor) types – TV and Radio. The RemoteControl doesn’t know how each device functions, only that they have a common interface (Device). This allows adding new devices without modifying the RemoteControl class, and changing the remote’s functionality without altering device specifics. The code leverages Python’s duck typing for a flexible implementation and uses basic classes to represent the abstraction and implementor.
from abc import ABC, abstractmethod
# Implementor: Defines the interface for device-specific operations.
class Device(ABC):
@abstractmethod
def turn_on(self):
pass
@abstractmethod
def turn_off(self):
pass
@abstractmethod
def set_volume(self, volume):
pass
# Concrete Implementors: Implement the Device interface for specific devices.
class TV(Device):
def turn_on(self):
print("TV is turned on")
def turn_off(self):
print("TV is turned off")
def set_volume(self, volume):
print(f"TV volume set to {volume}")
class Radio(Device):
def turn_on(self):
print("Radio is turned on")
def turn_off(self):
print("Radio is turned off")
def set_volume(self, volume):
print(f"Radio volume set to {volume}")
# Abstraction: Defines the high-level interface for using the devices.
class RemoteControl:
def __init__(self, device):
self.device = device
def press_button(self, button):
if button == "on":
self.device.turn_on()
elif button == "off":
self.device.turn_off()
elif button == "volume_up":
self.device.set_volume(self.device.get_volume() + 1) # Assuming get_volume exists
elif button == "volume_down":
self.device.set_volume(self.device.get_volume() - 1) # Assuming get_volume exists
def get_volume(self):
return self.device.get_volume() # Assuming get_volume exists - better to define in abstraction
# Example usage
if __name__ == "__main__":
tv = TV()
radio = Radio()
remote_tv = RemoteControl(tv)
remote_radio = RemoteControl(radio)
remote_tv.press_button("on")
remote_tv.press_button("volume_up")
remote_radio.press_button("on")
remote_radio.press_button("off")
The Bridge pattern is a structural design pattern that lets you split one or several interfaces from their implementations. This pattern allows implementations to vary independently of interfaces, avoiding rigid inheritance hierarchies. It promotes loose coupling, making it easy to switch implementations without affecting clients.
In this Java example, RemoteControl is the Abstraction, defining a high-level interface for controlling a device. Device is the Implementor interface, representing the concrete device operations. ConcreteRemoteControl is a specific abstraction, using the Device interface. TV and Radio are concrete implementors providing device-specific functionalities. The client interacts with ConcreteRemoteControl through the Device interface. This implementation uses interfaces, an idiomatic Java practice, favoring composition over inheritance and allowing for flexible implementation changes.
// Implementor Interface
interface Device {
void turnOn();
void turnOff();
void setVolume(int volume);
}
// Concrete Implementors
class TV implements Device {
@Override
public void turnOn() {
System.out.println("TV is on!");
}
@Override
public void turnOff() {
System.out.println("TV is off!");
}
@Override
public void setVolume(int volume) {
System.out.println("Setting TV volume to " + volume);
}
}
class Radio implements Device {
@Override
public void turnOn() {
System.out.println("Radio is on!");
}
@Override
public void turnOff() {
System.out.println("Radio is off!");
}
@Override
public void setVolume(int volume) {
System.out.println("Setting Radio volume to " + volume);
}
}
// Abstraction Interface
interface RemoteControl {
void buttonPressed(String button);
}
// Concrete Abstraction
class ConcreteRemoteControl implements RemoteControl {
private Device device;
public ConcreteRemoteControl(Device device) {
this.device = device;
}
@Override
public void buttonPressed(String button) {
if (button.equals("on")) {
device.turnOn();
} else if (button.equals("off")) {
device.turnOff();
} else if (button.startsWith("volume")) {
int volume = Integer.parseInt(button.split(" ")[1]);
device.setVolume(volume);
} else {
System.out.println("Unknown button!");
}
}
}
// Client
public class BridgeExample {
public static void main(String[] args) {
Device tv = new TV();
RemoteControl tvRemote = new ConcreteRemoteControl(tv);
tvRemote.buttonPressed("on");
tvRemote.buttonPressed("volume 5");
tvRemote.buttonPressed("off");
Device radio = new Radio();
RemoteControl radioRemote = new ConcreteRemoteControl(radio);
radioRemote.buttonPressed("on");
radioRemote.buttonPressed("volume 3");
radioRemote.buttonPressed("off");
}
}