Structural Design Pattern: The Bridge Pattern

Bridge Design Pattern

The Bridge Pattern is designed for enhanced flexibility - a programming design choice that may cover future uncertainty by decoupling abstraction and implementation of code. It is part of a structural pattern, as it eases the design by making creating simpler relationships between the entities - allowing you to easily form larger structures using the simple relationships.

To get a better idea ...

Imagine the relationship between a client and server. The client only need the interface that the server has implemented in order to know what we can and cannot do with the server. You may for example choose to do this for security reasons - you wouldn't want the client to handle all username and password transactions and validate themselves, would you? Whatever your reasons may be, the Bridge Pattern can be useful in many such situations.

Advantages using the Bridge Pattern

  • By decoupling abstraction from the implementation, we gain greater flexibility as the two are independent.
  • We only need to know the interface, and do not need to know the actual implementation.
  • A great pattern to add cross-platform support (Java is an exception to this).

Disadvantages using the Bridge Pattern

  • Increases code complexity.
  • Hiding details from "client".
  • Can have performance issues having to send messages along the bridge.

Bridge Pattern Example in Java

The UML Class diagram shows us the basic relationship between the Abstraction and Implementor. The way it could be implemented is demonstrated in the Java code below.

In this Java Example, we have an Abstractor (imagine it to be the client) and the Implementor (imagine it to be the server).

The Abstractor expects an Implementor sent to it in the constructor. We do not know how the implementor intends to implement the operation. We only know what it implements.
As of such, inside the operation() method we run the implementor's implementedOperation().

We have two different Concrete Implementors that run implementedOperation(), and prints out different strings to the console

The flexibility of the bridge design pattern allows us to easily add more Refined Abstractions from the Abstraction, or more Concrete Implementors as needed - without having to change existing code.

public class Main
{

	/* Abstractor */
	abstract class Abstractor
	{

		protected Implementor impl;

		public Abstractor(Implementor impl)
		{
			this.impl = impl;
		}

		public abstract void operation();
	}

	/* Implementor */
	interface Implementor
	{

		public void implementedOperation();
	}

	/* Refined Abstraction 1*/
	class RefinedAbstraction1 extends Abstractor
	{

		public RefinedAbstraction1(Implementor impl)
		{
			super(impl);
		}

		@Override
		public void operation()
		{
			impl.implementedOperation();
		}
	}

	/* Concrete Implementor 1 */
	class ConcreteImplementor1 implements Implementor
	{

		@Override
		public void implementedOperation()
		{
			System.out.println("Running implemented operation 1");
		}
	}

	/* Concrete Implementor 2 */
	class ConcreteImplementor2 implements Implementor
	{

		@Override
		public void implementedOperation()
		{
			System.out.println("Running implemented operation 2");
		}
	}

	/**
	 * @param args the command line arguments
	 */
	public static void main(String[] args)
	{
		// We create an instance of Main in order to run directly from main.
		// (Otherwise we will get a non-static variable error)
		Main main = new Main();
		main.run();
	}

	/* Run Demo */
	private void run()
	{
		Implementor concreteImplementor1 = new ConcreteImplementor1();
		Abstractor refinedAbstraction1 = new RefinedAbstraction1(concreteImplementor1);
		refinedAbstraction1.operation();

		Implementor concreteImplementor2 = new ConcreteImplementor2();
		refinedAbstraction1 = new RefinedAbstraction1(concreteImplementor2);
		refinedAbstraction1.operation();
	}
}

Output:

Running implemented operation 1
Running implemented operation 2

You can find my additional demos on GitHub.