The Bridge design pattern decouples an abstraction from its implementation so that the two can vary independently.
C# code examples of the Bridge design pattern is provided in 3 forms:
A visualization of the classes and objects participating in this pattern.
The classes and objects participating in this pattern include:
BusinessObject
)
CustomersBusinessObject
)
DataObject
)
CustomersDataObject
)
This structural code demonstrates the Bridge pattern which separates (decouples) the interface from its implementation. The implementation can evolve without changing clients which use the abstraction of the object.
using System;
namespace Bridge.Structural
{
/// <summary>
/// Bridge Design Pattern
/// </summary>
public class Program
{
public static void Main(string[] args)
{
Abstraction ab = new RefinedAbstraction();
// Set implementation and call
ab.Implementor = new ConcreteImplementorA();
ab.Operation();
// Change implemention and call
ab.Implementor = new ConcreteImplementorB();
ab.Operation();
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'Abstraction' class
/// </summary>
public class Abstraction
{
protected Implementor implementor;
public Implementor Implementor
{
set { implementor = value; }
}
public virtual void Operation()
{
implementor.Operation();
}
}
/// <summary>
/// The 'Implementor' abstract class
/// </summary>
public abstract class Implementor
{
public abstract void Operation();
}
/// <summary>
/// The 'RefinedAbstraction' class
/// </summary>
public class RefinedAbstraction : Abstraction
{
public override void Operation()
{
implementor.Operation();
}
}
/// <summary>
/// The 'ConcreteImplementorA' class
/// </summary>
public class ConcreteImplementorA : Implementor
{
public override void Operation()
{
Console.WriteLine("ConcreteImplementorA Operation");
}
}
/// <summary>
/// The 'ConcreteImplementorB' class
/// </summary>
public class ConcreteImplementorB : Implementor
{
public override void Operation()
{
Console.WriteLine("ConcreteImplementorB Operation");
}
}
}
This real-world code demonstrates the Bridge pattern in which a BusinessObject abstraction is decoupled from the implementation in DataObject. The DataObject implementations can evolve dynamically without changing any clients.
using System;
using System.Collections.Generic;
namespace Bridge.RealWorld
{
/// <summary>
/// Bridge Design Pattern
/// </summary>
public class Program
{
public static void Main(string[] args)
{
// Create RefinedAbstraction
var customers = new Customers();
// Set ConcreteImplementor
customers.Data = new CustomersData("Chicago");
// Exercise the bridge
customers.Show();
customers.Next();
customers.Show();
customers.Next();
customers.Show();
customers.Add("Henry Velasquez");
customers.ShowAll();
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'Abstraction' class
/// </summary>
public class CustomersBase
{
private DataObject dataObject;
public DataObject Data
{
set { dataObject = value; }
get { return dataObject; }
}
public virtual void Next()
{
dataObject.NextRecord();
}
public virtual void Prior()
{
dataObject.PriorRecord();
}
public virtual void Add(string customer)
{
dataObject.AddRecord(customer);
}
public virtual void Delete(string customer)
{
dataObject.DeleteRecord(customer);
}
public virtual void Show()
{
dataObject.ShowRecord();
}
public virtual void ShowAll()
{
dataObject.ShowAllRecords();
}
}
/// <summary>
/// The 'RefinedAbstraction' class
/// </summary>
public class Customers : CustomersBase
{
public override void ShowAll()
{
// Add separator lines
Console.WriteLine();
Console.WriteLine("------------------------");
base.ShowAll();
Console.WriteLine("------------------------");
}
}
/// <summary>
/// The 'Implementor' abstract class
/// </summary>
public abstract class DataObject
{
public abstract void NextRecord();
public abstract void PriorRecord();
public abstract void AddRecord(string name);
public abstract void DeleteRecord(string name);
public abstract string GetCurrentRecord();
public abstract void ShowRecord();
public abstract void ShowAllRecords();
}
/// <summary>
/// The 'ConcreteImplementor' class
/// </summary>
public class CustomersData : DataObject
{
private readonly List<string> customers = new List<string>();
private int current = 0;
private string city;
public CustomersData(string city)
{
this.city = city;
// Loaded from a database
customers.Add("Jim Jones");
customers.Add("Samual Jackson");
customers.Add("Allen Good");
customers.Add("Ann Stills");
customers.Add("Lisa Giolani");
}
public override void NextRecord()
{
if (current <= customers.Count - 1)
{
current++;
}
}
public override void PriorRecord()
{
if (current > 0)
{
current--;
}
}
public override void AddRecord(string customer)
{
customers.Add(customer);
}
public override void DeleteRecord(string customer)
{
customers.Remove(customer);
}
public override string GetCurrentRecord()
{
return customers[current];
}
public override void ShowRecord()
{
Console.WriteLine(customers[current]);
}
public override void ShowAllRecords()
{
Console.WriteLine("Customer City: " + city);
foreach (string customer in customers)
{
Console.WriteLine(" " + customer);
}
}
}
}
The .NET optimized code demonstrates the same code as above but uses
more modern C# and .NET features.
Here is an elegant C# Bridge solution.
namespace Bridge.NetOptimized;
using static System.Console;
/// <summary>
/// Bridge Design Pattern
/// </summary>
public class Program
{
public static void Main()
{
// Create RefinedAbstraction and
// set ConcreteImplementor
var customers = new Customers(
new CustomersData("Chicago"));
// Exercise the bridge
customers.Show();
customers.Next();
customers.Show();
customers.Next();
customers.Show();
customers.Add("Henry Velasquez");
customers.ShowAll();
// Wait for user
ReadKey();
}
}
/// <summary>
/// The 'Abstraction' class
/// </summary>
public class CustomersBase(IDataObject<string> dataObject)
{
public virtual void Next() => dataObject.NextRecord();
public virtual void Prior() => dataObject.PriorRecord();
public virtual void Add(string name) => dataObject.AddRecord(name);
public virtual void Delete(string name) => dataObject.DeleteRecord(name);
public virtual void Show() => dataObject.ShowRecord();
public virtual void ShowAll() => dataObject.ShowAllRecords();
}
/// <summary>
/// The 'RefinedAbstraction' class
/// </summary>
public class Customers(IDataObject<string> dataObject) : CustomersBase(dataObject)
{
public override void ShowAll()
{
// Add separator lines
WriteLine();
WriteLine("------------------------");
base.ShowAll();
WriteLine("------------------------");
}
}
/// <summary>
/// The 'Implementor' interface
/// </summary>
public interface IDataObject<T>
{
void NextRecord();
void PriorRecord();
void AddRecord(T t);
void DeleteRecord(T t);
T GetCurrentRecord();
void ShowRecord();
void ShowAllRecords();
}
/// <summary>
/// The 'ConcreteImplementor' class
/// </summary>
public class CustomersData : IDataObject<string>
{
private readonly string city;
private readonly List<string> customers;
private int current = 0;
// Constructor
public CustomersData(string city)
{
this.city = city;
// Simulate loading from database
customers =
["Jim Jones", "Samual Jackson", "Allan Good",
"Ann Stills", "Lisa Giolani" ];
}
public void NextRecord()
{
if (current <= customers.Count - 1)
{
current++;
}
}
public void PriorRecord()
{
if (current > 0)
{
current--;
}
}
public void AddRecord(string customer) =>
customers.Add(customer);
public void DeleteRecord(string customer) =>
customers.Remove(customer);
public string GetCurrentRecord() =>
customers[current];
public void ShowRecord() =>
WriteLine(customers[current]);
public void ShowAllRecords()
{
WriteLine("Customer Group: " + city);
customers.ForEach(customer =>
WriteLine(" " + customer));
}
}