C# Bridge Pattern

Summary: in this tutorial, you’ll learn about the C# Bridge pattern that decouples an abstraction from its implementation so that the two can vary independently.

Introduction to the C# Bridge pattern

By definition, a Bridge pattern allows you to decouple an abstraction from its implementation so that you can change the two independently.

Suppose you have the following shape class hierarchy:

C# Bridge Pattern - Shape Hierarchy

To make both triangles and rectangles rounded and sketched, you may end up with the subclasses of the Triangle and Rectangle classes like this:

C# Bridge Pattern - Shape Hierarchy Explosion

But this approach is not flexible because inheritance binds a style (implementation) to a shape (abstraction) permanently.

For example, if you want to add a new shape like a pentagon, you need to define three classes Pentagon, RoundedPentagon, and SketchedPentagon.

Additionally, if you want to add a more shape style like embossed, you need to add a new class to each shape, which does not scale well.

To solve this problem, you can use the Bridge pattern to separate the style from the shape hierarchy like this:

C# Bridge Pattern

C# Bridge pattern implementation

The following program illustrates the bridge pattern:

using static System.Console;

namespace BridgePattern;

public abstract class Style
{
    public abstract void Decorate();
}

public class RoundedStyle : Style
{
    public override void Decorate() => WriteLine("rounded.");
}

public class SketchedStyle : Style
{
    public override void Decorate() => WriteLine("sketched.");
}

public abstract class Shape
{
    protected Style Style;

    public Shape(Style style)
    {
        Style = style;
    }

    public abstract void ApplyStyle();

}

public class Triangle : Shape
{
    public Triangle(Style style) : base(style)
    {
    }

    public override void ApplyStyle()
    {
        Write("Make the triangle ");
        Style.Decorate();
    }
}

public class Rectangle : Shape
{
    public Rectangle(Style style) : base(style)
    {
    }

    public override void ApplyStyle()
    {
        Write("Make the rectangle ");
        Style.Decorate();
    }
}
public class Program
{
    public static void Main(string[] args)
    {
        var triangle = new Triangle(new SketchedStyle());
        triangle.ApplyStyle();

        var rectangle = new Rectangle(new RoundedStyle());
        rectangle.ApplyStyle();
    }
}Code language: C# (cs)

Output:

Make the triangle sketched.
Make the rectangle rounded.

How it works.

First, define the Style abstract class that has the Decorate() abstract method:

public abstract class Style
{
    public abstract void Decorate();
}Code language: C# (cs)

Second, define the RoundedStyle class that inherits from the Style class. The Decorate() method displays the " rounded." message to the Console:

public class RoundedStyle : Style
{
    public override void Decorate() => WriteLine("rounded.");
}Code language: C# (cs)

Third, define the SketchedStyle class that also inherits from the Style class. The Decorate() method shows the " sketched." message to the Console:

public class SketchedStyle : Style
{
    public override void Decorate() => WriteLine("sketched.");
}Code language: C# (cs)

Fourth, define the Shape abstract class that has a Style object as a property and an abstract method ApplyStyle() that calls the Decorate() method of the Style object to set the style for shape:

public abstract class Shape
{

    protected Style Style;

    public Shape(Style style)
    {
        Style = style;
    }

    public abstract void ApplyStyle();

}Code language: C# (cs)

Fifth, define the Triangle class that extends the Shape class. The ApplyStyle() method writes a message to the console and calls the Decorate() method of the style object:

public class Triangle : Shape
{
    public Triangle(Style style) : base(style)
    {
    }

    public override void ApplyStyle()
    {
        Write("Make the triangle ");
        Style.Decorate();
    }
}Code language: C# (cs)

Sixth, define the Rectangle class that inherits from the Shape class. The ApplyStyle() method also writes a message to the console and calls the Decorate() method of the Style object:

public class Rectangle : Shape
{
    public Rectangle(Style style) : base(style)
    {
    }

    public override void ApplyStyle()
    {
        Write("Make the rectangle ");
        Style.Decorate();
    }
}Code language: C# (cs)

Finally, create an instance of the Triangle class with the SketchedStyle and calls the ApplyStyle() method to set its style to sketch. Similarly, create an instance of the Rectangle class and calls the ApplyStyle() method to set its style to rounded.

public class Program
{
    public static void Main(string[] args)
    {
        var triangle = new Triangle(new SketchedStyle());
        triangle.ApplyStyle();

        var rectangle = new Rectangle(new RoundedStyle());
        rectangle.ApplyStyle();
    }
}Code language: C# (cs)

Summary

  • Use the C# Bridge pattern to decouple an abstraction from its implementation so that the two can vary independently.
Was this tutorial helpful ?