 .NET 1.1+Bridge Design PatternThe bridge pattern is a design pattern that separates the abstract elements of a class from its technical implementation. This provides a cleaner implementation of real-world objects and allows the implementation details to be changed easily.
What is the Bridge Pattern?
The bridge pattern is a Gang of Four design pattern. This is a structural pattern as it defines a manner for creating relationships between classes or entities. The bridge pattern is used to separate the abstract elements of a class from the implementation details. For example, the abstract elements may be the business logic of an application. They can be created without any knowledge of the implementation details of their data access or interoperability with the operating system. The pattern provides the means to replace the implementation details without modifying the abstraction. This permits, for example, changing operating systems, databases, etc. with no impact to the business logic.
When the bridge pattern is not used, you may find that implementation details are included within the abstraction. In this case, the way in which implementation details are changed is probably through inheritance, with subclasses providing different implementations. This can be problematic when refined abstractions are included, also through inheritance. The number of required classes can grow exponentially as new abstractions and implementations are added to a system. In this model, when there is a single implementation for a single abstraction, only one class is required whereas the bridge pattern would involve three classes. However, if there were four abstractions and five implementations, this would potentially require twenty classes versus the ten needed when using the bridge pattern. This is due to the pattern removing platform dependencies from the abstraction.
Another benefit of the bridge pattern is that it introduces the possibility of changing the implementation details at run-time. This could permit the user to switch implementations to determine how the software interoperates with other systems. For example, allowing the user to decide whether to store information in a database, XML file or using another storage mechanism.
An example of the bridge pattern could be used within a system that sends messages, perhaps describing system status changes, warnings and errors. An abstraction class that represents a message could include properties to hold the details of the message. Several implementations could then be included to permit sending the messages via email, over a messaging queue or to a web service. The implementation to be used could then be selected according to the availability of these resources.
Implementing the Bridge Pattern

The UML class diagram above describes an implementation of the bridge design pattern. The items in the diagram are described below:
- Abstraction. This class contains members that define an abstract business object and its functionality. It also acts as the base class for other, more refined, abstractions. Objects of this type hold a reference to the particular implementation that they are using for platform-specific functions. No other member of the class deals with implementation details.
- RefinedAbstraction. Many refined abstractions may inherit from the Abstraction class. Each provides a more specific variation upon the abstraction but still contains no implementation details other that those in the Implementation object reference that they hold.
- ImplementationBase. This abstract class is the base class for all classes that provide implementation details for the associated abstractions. The class provides a fixed interface that can be utilised by the abstractions. The interface need not be similar to that of the abstraction. This class may be implemented as an interface if it provides no real functionality for its subclasses.
- ConcreteImplementation. The ConcreteImplementation class inherits form the ImplementationBase class. There may be multiple concrete implementation classes, each providing the same interface but providing platform-specific functionality.
The following shows the basic code of the bridge design pattern implemented using C#. For brevity, the Abstraction class' Implementer property is declared using C# 3.0 automatically implemented property syntax. For earlier versions of the .NET framework, define the property in full and use the protected scope for the backing variable so that it is also available to refined abstractions.
public class Abstraction
{
public ImplementationBase Implementer { get; set; }
public virtual void Operation()
{
Console.WriteLine("ImplementationBase:Operation()");
Implementer.OperationImplementation();
}
}
public class RefinedAbstraction : Abstraction
{
public override void Operation()
{
Console.WriteLine("RefinedAbstraction:Operation()");
Implementer.OperationImplementation();
}
}
public abstract class ImplementationBase
{
public abstract void OperationImplementation();
}
public class ConcreteImplementation1 : ImplementationBase
{
public override void OperationImplementation()
{
Console.WriteLine("ConcreteImplementation1:OperationImplementation()");
}
}
public class ConcreteImplementation2 : ImplementationBase
{
public override void OperationImplementation()
{
Console.WriteLine("ConcreteImplementation2:OperationImplementation()");
}
}
Example Bridge
In the opening paragraphs of this article I described an example use for the bridge pattern. In the example, the abstraction would be used to build a message that must be sent to another system. Depending upon the available infrastructure, the message would be sent using email, using a message queue or via a web service. In the remaining sections of this article we will implement the basic structure of this messaging system and create a simple program that demonstrates its use.
The code to fully implement such a messaging system would be larger than it needs to be to demonstrate the concepts and would be difficult to test without a web server, mail server and message queue system. To simplify the example, we will not actually integrate with any of these systems. Instead we will output messages that describe what would happen in reality. Again, C# 3.0 automatic property syntax has been used and will need to be expanded for earlier versions of the .NET framework.
We can start by implementing the abstract base class for the implementation. This class defines a single method signature that will be used to send a message. The method will accept three parameters for the title and body of the message and the importance of the message, expressed as an integer.
public abstract class MessageSenderBase
{
public abstract void SendMessage(string title, string details, int importance);
}
We can now define the three initial concrete implementations. As we are using the bridge pattern, further messaging implementations could be added as required in the future.
public class EmailSender : MessageSenderBase
{
public override void SendMessage(string title, string body, int importance)
{
Console.WriteLine("Email\n{0}\n{1}\n{2}\n", title, body, importance);
}
}
public class MsmqSender : MessageSenderBase
{
public override void SendMessage(string title, string body, int importance)
{
Console.WriteLine("MSMQ\n{0}\n{1}\n{2}\n", title, body, importance);
}
}
public class WebServiceSender : MessageSenderBase
{
public override void SendMessage(string title, string body, int importance)
{
Console.WriteLine("Web Service\n{0}\n{1}\n{2}\n", title, body, importance);
}
}
The next step is to create the base class for abstractions. This class will describe a message with three properties for the title, body and importance of the message. A fourth property will be used to hold a reference to an implementation object. The class will also include a Send method that calls the SendMessage method of the referenced implementation.
public class Message
{
public MessageSenderBase MessageSender { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public int Importance { get; set; }
public virtual void Send()
{
MessageSender.SendMessage(Title, Body, Importance);
}
}
Finally, we will refine the Message class with a subclass that has an extra property for holding additional information. This class could be used when the software is being operated by a user. If an error occurs, the user could be asked for any information that they can provide to help determine the problem. These "UserComments" will be appended to the body of the message before sending.
public class UserEditedMessage : Message
{
public string UserComments { get; set; }
public override void Send()
{
string fullBody = string.Format("{0}\nCOMMENTS\n{1}", Body, UserComments);
MessageSender.SendMessage(Title, fullBody, Importance);
}
}
Testing the Bridge
To test the bridge example classes we can use a console application containing all of the above code. Add the code shown below to the Main method then execute the program. The test code creates a message and sends it using each type of message sender. It then creates another message, this time with user comments. Finally, it sends the new message using one of the available message sender implementations.
MessageSenderBase email = new EmailSender();
MessageSenderBase queue = new MsmqSender();
MessageSenderBase web = new WebServiceSender();
Message message = new Message();
message.Title = "Error";
message.Body = "An error occurred";
message.Importance = 1;
message.MessageSender = email;
message.Send();
message.MessageSender = queue;
message.Send();
message.MessageSender = web;
message.Send();
UserEditedMessage userEdited = new UserEditedMessage();
userEdited.Title = "Error";
userEdited.Body = "An error occurred";
userEdited.Importance = 1;
userEdited.UserComments = "Crashed when I clicked Submit";
userEdited.MessageSender = email;
userEdited.UserComments = "Additional comments";
userEdited.Send();
/* OUTPUT
Email
Error
An error occurred
1
MSMQ
Error
An error occurred
1
Web Service
Error
An error occurred
1
Email
Error
An error occurred
COMMENTS
Additional comments
1
*/
|