Bridge Pattern | Structural Design Pattern

 Bridge Pattern

The Bridge Pattern is a structural design pattern that separates abstraction from implementation. It allows both to evolve independently without the need to modify each other. This pattern is particularly useful when you want to avoid a permanent binding between an abstraction and its implementation.

Here are the key components of the Bridge Pattern:

Abstraction: Defines the abstraction's interface and maintains a reference to an object of type Implementor.

Refined Abstraction: Extends the abstraction and adds finer details.

Implementor: Defines the interface for implementation classes. It doesn't have to correspond directly to the abstraction's interface.

Concrete Implementor: Implements the Implementor interface and defines its concrete implementation.


This pattern is beneficial when there are multiple dimensions of variability, and you want to avoid a Cartesian product of variations. It promotes flexibility and extensibility by allowing the abstraction and implementation to vary independently.

Here's a simple analogy using a drawing application:

Abstraction: Represents a shape in the drawing.

Refined Abstraction: Adds specific details to the shape, like color or border style.

Implementor: Defines drawing methods, such as drawing on a canvas.

Concrete Implementor: Implements the drawing methods for a specific rendering engine, like OpenGL or DirectX.

This way, we can have different shapes with various details drawn on different platforms without having to create a specific class for each combination.

Example of the Bridge Pattern in C# using a drawing application. 

We'll have shapes (abstraction) that can be drawn using different rendering engines (implementation).



______
using System;

// Implementor interface
public interface IDrawingImplementor
{
    void DrawCircle(int radius, int x, int y);
    void DrawRectangle(int width, int height, int x, int y);
}

// Concrete Implementor 1
public class OpenGLDrawing : IDrawingImplementor
{
    public void DrawCircle(int radius, int x, int y)
    {
        Console.WriteLine($"Drawing Circle in OpenGL at ({x}, {y}) with radius {radius}");
    }

    public void DrawRectangle(int width, int height, int x, int y)
    {
        Console.WriteLine($"Drawing Rectangle in OpenGL at ({x}, {y}) with width {width} and height {height}");
    }
}

// Concrete Implementor 2
public class DirectXDrawing : IDrawingImplementor
{
    public void DrawCircle(int radius, int x, int y)
    {
        Console.WriteLine($"Drawing Circle in DirectX at ({x}, {y}) with radius {radius}");
    }

    public void DrawRectangle(int width, int height, int x, int y)
    {
        Console.WriteLine($"Drawing Rectangle in DirectX at ({x}, {y}) with width {width} and height {height}");
    }
}

// Abstraction
public abstract class Shape
{
    protected IDrawingImplementor drawingImplementor;

    protected Shape(IDrawingImplementor drawingImplementor)
    {
        this.drawingImplementor = drawingImplementor;
    }

    public abstract void Draw();
}

// Refined Abstraction 1
public class Circle : Shape
{
    private int radius;
    private int x;
    private int y;

    public Circle(IDrawingImplementor drawingImplementor, int radius, int x, int y)
        : base(drawingImplementor)
    {
        this.radius = radius;
        this.x = x;
        this.y = y;
    }

    public override void Draw()
    {
        drawingImplementor.DrawCircle(radius, x, y);
    }
}

// Refined Abstraction 2
public class Rectangle : Shape
{
    private int width;
    private int height;
    private int x;
    private int y;

    public Rectangle(IDrawingImplementor drawingImplementor, int width, int height, int x, int y)
        : base(drawingImplementor)
    {
        this.width = width;
        this.height = height;
        this.x = x;
        this.y = y;
    }

    public override void Draw()
    {
        drawingImplementor.DrawRectangle(width, height, x, y);
    }
}

class Program
{
    static void Main()
    {
        // Using OpenGL for drawing
        IDrawingImplementor openGLDrawing = new OpenGLDrawing();

        Shape circle = new Circle(openGLDrawing, 5, 10, 20);
        Shape rectangle = new Rectangle(openGLDrawing, 8, 6, 15, 25);

        circle.Draw();
        rectangle.Draw();

        // Using DirectX for drawing
        IDrawingImplementor directXDrawing = new DirectXDrawing();

        circle = new Circle(directXDrawing, 3, 30, 40);
        rectangle = new Rectangle(directXDrawing, 10, 7, 35, 45);

        circle.Draw();
        rectangle.Draw();
    }
}
______


In this example:

IDrawingImplementor is the implementor interface defining methods for drawing circles and rectangles.

OpenGLDrawing and DirectXDrawing are concrete implementors providing specific implementations for drawing using OpenGL and DirectX.

Shape is the abstraction class that uses the implementor interface.

Circle and Rectangle are refined abstractions that extend Shape and provide specific details for drawing circles and rectangles.

This allows us to draw shapes using different rendering engines without having to create a specific class for each combination, demonstrating the flexibility of the Bridge Pattern.

Another example using the Bridge Pattern in the context of a remote control for electronic devices. We'll have different types of devices (TV and Radio) and different remote control implementations.



______
using System;

// Implementor interface
public interface IDevice
{
    void TurnOn();
    void TurnOff();
    void SetChannel(int channel);
}

// Concrete Implementor 1
public class TV : IDevice
{
    private bool isOn = false;
    private int currentChannel = 0;

    public void TurnOn()
    {
        isOn = true;
        Console.WriteLine("TV is ON");
    }

    public void TurnOff()
    {
        isOn = false;
        Console.WriteLine("TV is OFF");
    }

    public void SetChannel(int channel)
    {
        if (isOn)
        {
            currentChannel = channel;
            Console.WriteLine($"Set TV channel to {channel}");
        }
        else
        {
            Console.WriteLine("TV is OFF. Please turn it on first.");
        }
    }
}

// Concrete Implementor 2
public class Radio : IDevice
{
    private bool isOn = false;
    private int currentChannel = 0;

    public void TurnOn()
    {
        isOn = true;
        Console.WriteLine("Radio is ON");
    }

    public void TurnOff()
    {
        isOn = false;
        Console.WriteLine("Radio is OFF");
    }

    public void SetChannel(int channel)
    {
        if (isOn)
        {
            currentChannel = channel;
            Console.WriteLine($"Set Radio channel to {channel}");
        }
        else
        {
            Console.WriteLine("Radio is OFF. Please turn it on first.");
        }
    }
}

// Abstraction
public abstract class RemoteControl
{
    protected IDevice device;

    protected RemoteControl(IDevice device)
    {
        this.device = device;
    }

    public abstract void TurnOn();
    public abstract void TurnOff();
    public abstract void SetChannel(int channel);
}

// Refined Abstraction 1
public class BasicRemoteControl : RemoteControl
{
    public BasicRemoteControl(IDevice device)
        : base(device)
    {
    }

    public override void TurnOn()
    {
        device.TurnOn();
    }

    public override void TurnOff()
    {
        device.TurnOff();
    }

    public override void SetChannel(int channel)
    {
        device.SetChannel(channel);
    }
}

// Refined Abstraction 2
public class AdvancedRemoteControl : RemoteControl
{
    public AdvancedRemoteControl(IDevice device)
        : base(device)
    {
    }

    public void Mute()
    {
        Console.WriteLine("Muting the device");
    }
}

class Program
{
    static void Main()
    {
        // Using TV with a basic remote control
        IDevice tv = new TV();
        RemoteControl basicRemote = new BasicRemoteControl(tv);

        basicRemote.TurnOn();
        basicRemote.SetChannel(5);
        basicRemote.TurnOff();

        // Using Radio with an advanced remote control
        IDevice radio = new Radio();
        AdvancedRemoteControl advancedRemote = new AdvancedRemoteControl(radio);

        advancedRemote.TurnOn();
        advancedRemote.SetChannel(101);
        advancedRemote.Mute();
        advancedRemote.TurnOff();
    }
}
______


In this example:

IDevice is the implementor interface defining methods for turning on, turning off, and setting channels for electronic devices.

TV and Radio are concrete implementors providing specific implementations for TV and radio devices.

RemoteControl is the abstraction class that uses the implementor interface.

BasicRemoteControl and AdvancedRemoteControl are refined abstractions that extend RemoteControl and provide specific features for basic and advanced remote controls.


This allows us to control different electronic devices using different types of remote controls without having to create a specific class for each combination, illustrating the flexibility of the Bridge Pattern.




Comments