Refactoring and Design Patterns
This page
This chapter
All
 

Discussing the Bridge Pattern

 

Intent (GoF)

 

Decouple an abstraction from its implementation so that the two can vary independently.

 

 

Bridge basics

 

 

 

When/what?

 

When you need a Class that can change its behavior/implementation

You use the Bridge when you need a Class that can change its behavior and concrete implementation when needed.

 

A Dynamic Base Class

The Bridge is intended to act as a Dynamic Base Class, to be extended by your code. While you only extend one Base Class, that Base Class can internally instantiate any concrete implementation of a specific functionality.

 

A Pretender

The Bridge can be seen as a Pretender of the Classes it can Instantiate. While Your code thinks it is working with one single object or Class (the Bridge), it is actually interacting with the Instantiated object wrapped by the Bridge.

 

 

Other info

 

Using delegation

The Bridge uses delegation of actions to do its work.

 

Using a Factory or Object Map to get the delegate

One way to look at the Bridge is as a wrapper around the Factory or Object Map. Using either the Factory or the Object Map you can return any object in a selected group and use it as the delegate for the delegation of actions.

 

Inversion of Control

By adding an extra layer related to the object creation or retrieval within the Command Patter, you can define from the outside which object will be used to delegate against.

 

 

Diagrams

 

Visual summary of the Bridge Pattern

 

 

 

Notes:

 

Decoupling the “how” from the “what”

The Bridge decouples the “how” (the concrete execution of actions) from the “what” (the Class you instantiate or extend in your code).

 

Abstract and concrete implementation

On the left hand side of the diagram is the Abstract implementation of the Bridge. These classes have little to no internal code and perform little to no concrete actions.

The concrete implementation is on the right hand side and allows for maximum freedom in what is implemented, as long as that concrete implementation uses the Base Class or Interface your Base Abstraction expects.

 

Adapting the Concrete Implementation

It is possible that the Classes you use to implement the actions for the Bridge have a different Interface and fingerprint from what your Base Abstraction expects.

 

Polymorphic Class, Base Abstraction can change Concrete Implementor

The Base Abstraction can change the Concrete Implementor while running and – as a result – also changes its behavior. This makes the Bridge a polymorphic class.

 

Base Abstraction / Base Class

The Base Abstraction of a Bridge can be used and extended as a Base Class.

 

All actions are delegated to Concrete Implementor

All actions required form the Base Abstraction are passed through to the Concrete Implementor.

 

 

Base class diagram

 

 

Notes:

 

As true as possible to implementation in “Design Patterns”

You will find a very similar diagram in “Design Patterns”. What I focused on are the relationships between the Classes.

 

Interface or base class?

The Bridge has no dependencies from the Implementor to the Abstraction. Each Concrete Implementor can be a completely different Class with completely different behaviors. In this case, a Interface might be the most logical choice.

 

Abstraction as a polymorphic class

Later in this chapter you will find me referring to the Bridge as a: “Polymorphic Class”. The Abstraction Class in the Bridge Pattern, according to: “Design Patterns” can be extendend and used as a Base Class.

As the Abstraction can internally change the object that populates the implementor, the Abstraction Class can change its behavior during runtime.

 

Implementing the same Interface on the Abstraction

As the Abstraction Class is mainly a pass-through station like the Adapter, you might as well implement the very same Interface on the Abstraction Class as well.

 

 

Setting the context in which the Bridge operates

In the diagram above you will find an additional method on the Abstraction called: “setContext”. By using this further abstraction you can instruct the Abstraction object to change the Concrete Implementor when needed. The contextID can be a constant value mapped to a Concrete Implementor.

 

Using a Factory to get the Concrete Implementor

On the next pages I discuss using a Factory to get the Concrete Implementor.

 

Using a Object Map to store and retrieve the Concrete Implementors

As discussed in the State Pattern you can use a Object Map to store and retrieve the Concrete Implementors.

 

 

 

What about the Flyweight?

“Design Patterns” makes mention of the Flyweight in cases of re-use of objects. The Flyweight is very similar to the Object Map in the result it produces. What differs between the Flyweight and the Object Map is the way the objects are mapped and retrieved.

 

 

 


 

 

Simple example of the Bridge Pattern

 

 

Dependencies:

 

Your Class:

1: Uses or Extends the Bridge

 

Bridge:

2.a: Uses either a Factory Method or Strategy

2.b: Pretends to be a Product or Strategy

 

Factory or Object Map

3: Returns the Product / Strategy

 

 

Notes:

 

Offering one object with choice in the concrete implementation

The Bridge as described in “Design Patterns” by the Gang of Four allows for different Implementations for one single object, which the object that is Instantiated by your code is always the same object

 

A Dynamic Base Class

The Bridge is intended to act as a Dynamic Base Class, to be extended by your code. While you only extend one Base Class, that Base Class can internally instantiate any concrete implementation of a specific functionality.

 

Using delegation

The Bridge uses delegation of actions to do its work.

 

Using a Factory or Object Map to get the delegate

One way to look at the Bridge is as a wrapper around the Factory or Object Map. Using either the Factory or the Object Map you can return any object in a selected group and use it as the delegate for the delegation of actions.

 


 

 

More detailed discussion on the Bridge Pattern

 

 

Dependencies:

 

Your code:

1.a: Has a specific Context it lives in.

1.b: Extends or uses Bridge A

 

Bridge A:

2.a: Implements Interface A, that is also implemented by Options A, B and C

2.b: Pretends to be the Product or Strategy that is returned from the Factory or Strategy

2.c: Uses Factory A or Strategy B to produce concrete Strategies or Products

 

Factory A/ Object Map B:

3.a: Instantiates / retrieves Option A, B or C

3.b: Returns a Concrete Product or Strategy

 

Option A, B and C:

4: Implement InterfaceA: the same Interface as implemented by the Bridge

 

Product X:

5: Is either Option A, B or C

 

 

Notes:

 

Shared Interface: Interchangeable Bridge

By using the same Interface for Option A, B, C and the Bridge itself, the Bridge becomes interchangeable with any Option instantiated by the Factory or Strategy used by the Bridge.  The Bridge becomes a direct Representative of those Classes and the objects you create.

 

Bridge as Adapter

When you use the Bridge as an Adapter, the game changes. You might want to implement a different Interface on the Bridge than on the objects you Instantiate to do the actual work. They might even differ deliberately.

 

Simple pass-through

Like the Adapter, the Bridge can be used to simply passes through any request / method call to the instantiated Class, pretending to be that Instantiated Class.

 

Using a Factory: stateless, some risks

In the most simple implementation, the Options are created on demand by using a Factory. Each object returned is in that case a disposable object, not retaining any internal State, as each next time the Factory is used, a new object is created to offer Option A, B or C to the Bridge.

The risk with using a Factory is that you can create a memory leak. Each object you instantiated needs to be destroyed. If some reference remains to each object you created, the garbage collector will not collect that object and it will remain in memory.

 

Using a Object Map: creation on demand?

When you want to retain some sort of State or reduce the risks to memory leaks, you can choose to use a Object Map. Each Option is stored under a specific Context ID and retrieved via that specific Context ID.  You can combine the Object Map with a Factory so you can produce the object on demand: avoiding the costs of creating them all beforehand.

 

 

 


 

 

Looking more closely at the implementation of the Bridge

 

 

Dependencies:

 

Bridge A:

1.a: Instanitates / is bridge to / pretends to be Implementation A, B, C

1.b: Contains / calls methods on variable A

1.c: Implements Interface A / Base Class B

 

Variable A:

2.a: Is of type Interface A / Base Class B

2.b: Is either Implementation A, B or C

 

Implementation A, B and C:

3:  Implement Interface A / BaseClass B

 

 

Notes:

 

Implementing or extending the same Interface or Base Class

One of the main ingredients of the Bridge is implementing the same Interface / Base Class on the Bridge and the Concrete Implementations.

 

Using an Adapter when your Concrete Implementations differ

When your Concrete Implementations come from  different sources (for instance: loading the data from files, loading the data from a Web Service and loading the data from a Database, all having a different type of implementation) you can use Adapters to adapt the original code, Interfaces and Classes 

This is illustrated on the next pages.

 

Using a Factory or Strategy Provider

In the next pages I show how a Factory or Strategy Provider can be used to offer the proper Implementation based on the Context your Class lives in.

 

 

When you need to adapt the classes you use in the Bridge

 

Variable A

1.a: Is of type Interface A / BaseClass B

1.b: Is either

 

Implementations A, B and C

2: Are not comparible with Interface A / BaseClass B

 

Adapter A, B and C:

3.a: Implenent Interface A / BaseClass B

3.b: Adapt Implementation A, B and C

 

 

Notes:

 

Adapting incompatible Classes

When your Class is not compatible with the Interface you want to use and Instantiate in your Bridge you can use an Adapter to adapt that Class. 

 

 

When do you use Bridge?

 

Different implementations for the same set of functionalities

When you need different implementations for the same set of functionalities

 

Only one object as access point

When you do not want to use different objects for different implementations, like can be the case with Strategy and some implementations of Simple Factory

 

Polymorphic Base Class

To create a Dynamic / Polymorphic BaseClass that can take any shape you need it to take.

 

Any type of object within the scope of its Interface

To create an object that can be any type and shape within the scope of its Interface.

 

Abstract the choice of the actual implementation

To isolate, abstract and separate the choice of the actual implementation of the functionalities (the Classes) from your project code

 

To make your code more Agile

As you encapsulate the creation process of objects AND by offering one single object to the outside world, any change behind the scenes of the Bridge can be dealt with and solved by the Bridge itself. By taking this role, the Bridge reduces the impact of change in your project

 

To work with one concrete object instead of Interfaces and multiple Concrete Classes

The Bridge wraps the choice and implementation of specific approaches in one single Class. Instead of implementing a specific Class, you simply pass the Context to your Bridge Class and the Bridge will instantiate the right Class for you.

 

 

 


 

 

Comparisons and differences

 

Patterns with Similarities

 

1: Adapter, Decorator, Proxy

 

Similarities:  encapsulation of the Concrete system

Adapter, Decorator and Adapter Abstract and Encapsulate the real object and passes all method Calls through to the Concrete Class it represents.

 

Adapter: represents only objects of one Type

An Adapter is usually created for one single type of object, instead of a possible collection of object Types.

 

Decorator: explicit Injection of objects to Represent, extending possibilities of object

Unlike the Decorator, the Bridge does not receive an Injected object, but creates it by it self. The Bridge also is more like the Adapter in the sense that it normally does not add any extra functionality to the object it Represents.

 

Proxy: late binding and buffering of settings

Unlike the Bridge, the Proxy allows Late Binding to the actual object, taking care of all requests and settings on Values by acting as a Buffer. The Bridge does not allow Late Binding (unless it uses a Proxy as the object it bridges).

 

 

2: Façade, Mediator, Manager, Operator

 

Similarities: Encapsulation and Abstraction of the actual processes

The Façade, Mediator, Manager and Operator offer one single object (the Concrete Façade, Mediator, Manager, Operator) to offer access to a set of functionalities located in a set of other objects and Classes. Like the Bridge they use other objects to perform the real actions on.

 

Façade: only one type of implementation, represents a subsystem, fixed

The Bridge offers access to different implementations, the Façade only to one specific implementation of a set of functionalities. Unlike the Façade, the Bridge does not represent a Subsystem, but only one object. Unlike the Bridge, the Façade cannot switch/change its Implementation or Subsystem.

 

Mediator: known to the subsystem, used by that subsystem, fixed

The Mediator is an active player in the Subsystem it Mediates. Instead of a Top Down approach, where the Mediator directs and the subsystems follows, the Mediator “works together with” the subsystem and Mediates which action should follow when, based on calls from the players in the subsystem.

The Mediator can not switch to a different implementation when needed

 

 

3: Delegate, State

 

Similarities: Close relatives, delegation of the actions

The Delegate might be the closest relative to the Bridge. Like the Bridge you can and would use the Delegate to encapsulate and abstract actions into a separate set of Classes you then use to create a “dynamic class” with dynamic and context-specific behavior.

Like the Bridge, the State uses one single object within the wrapper to implement the Concrete Actions and this object can be an Instantiation of different Classes.

 

State: active change of implementation

The State Pattern actively changes the Implementation when a State changes: changing the behaviors behind the methods called on the wrapper of the State Pattern. If you want to change the object that Implements the methods of the Bridge, this needs to be done by the Bridge object itself.

 

 

 

Cooperation with other Patterns

 

1: Object Map, Singleton, Multiton

 

Re-using the same objects

In some cases where the Bridge only executes actions without any State (meaning that there are no values saved within the Bridge object) you could choose to use a Singleton, Multiton or Obect Map to map the Obejct you use over all instanitations of the Bridge.

 

Alternative for the Factory

The Object Map and Multiton specifically might be an alternative for the Factory in this case, where re-use of the same objects is preferred over creation when needed.

 

 

2: Factory

 

Creating the objects you want to use

In order to be able to switch the Concrete Implementations you can use a (Simple) Factory. Pass it the Context in which you want the Bridge to operate and the (Simple) Factory will produce the required object and Concrete Implementation.

 

 

3: Factory / Object Map

 

Combining the Factory and Object Map

When a Bridge changes the Concrete Implemention quite regularly due to your implementation, you might like to do only one instantiation of that object per Concrete Bridge, so that you can recall the already instantiated Implementation from a (local) Object Map instead of creation yet a new object.

 

 

4: Adapter

 

Adapting the Concrete Implementation

When you use objects from another source than your code Base, you might need and want to adapt the Classes to the needs of the Bridge and your code.


 

Summary

 

Bridge as Pretender

Bridge is a Pretender of the Concrete Classes it can implement internally

 

Decouple actual implementation from the instantiated object

The Bridge is used to de-couple the actual implementation of specific functionalities from the object (the Bridge) you use or instantiate

 

Used as base class

A Bridge can be used as a Base Class, to be extended by your specific Classes

 

Multiple possible Implementations, only one Main Class

The Bridge Pattern creates a solution in which one Class and one object (the Bridge) can represent and instantiate multiple Classes with multiple Implementations of the same thing (the Concrete Implementations of the Abstraction).

 

Bridge as an Encapsulated Strategy and Factory

Bridge can be seen as an Encapsulated implementation of the Strategy and Factory pattern, where the object itself you instantiate always remains the same and the way the functionalities are implemented is handled behind the scenes

 

Bridge as a Polymorphic Adapter

Bridge can be seen as a flexible or polymorphic version of the Adapter, “Adapting” the concrete classes it can implement to one single Class and passing through any method call on the Bridge to the Concrete Implementor.

 

Context to decide which object to instantiate

Bridge uses a specific Context to decide which Concrete Class it will implement. This Context is passed by the Requester or can be retrieved from a generic variable, like the System Settings.

 

Decoupling of methods and their implementation

Bridge decouples the concrete implementation of functionalities from the Bridge object that you Access or Extend

 

Factory to instantiate the Class

Bridge generally uses a Factory to instantiate the actual Class

 

Different strategies for the same task

Bridge can offer  different strategies to solve a specific task, but all through one concrete object as the access point

 

Using Adapters for incompatible Classes

As discussed before, when you want to use or need to use Classes not compatible with your Interface or Base Class, you can use an Adapter for that Class to adapt the Interface.

 

Inversion of Control within the Bridge Pattern by using Object Maps, Singletons or Multitons

Instead of letting the Bridge decide which Concrete Implementations will be used in what Context, you can use a Object Map, Singleton or Multiton to map (or store) Concrete Implementations with the Context as the key. When the Bridge requests the appropriate Implementation for a specific Context, the Map will return the Implementation you set and defined elsewhere.

 

Inversion of Control and Unit tests

This also allows you to make the Bridge easier to adapt for Unit tests. Instead of using the concrete implementation for the actual application, you can use and run specific classes you need to make Unit Testing of the Bridge easier.

 

 

See also

 

Overview of the Patterns based on type and use

There are many ways to look at the Patterns in this book. In the overview on Patterns based on type and use you will find the patterns sorted on type and use.

 

 

Overview of the Creational, Structural and Behavioral patterns

Design Patterns have a basic classification in three different groups: Creational, Structural and Behavioral Patterns.

 

 

The Wrapper Family Tree

The Wrapper Family Tree shows the various variation on the Wrapper theme and the relationships and differences for each Wrapper.

 

 

The Delegate Pattern: when you simply want to delegate stuff

The Delegate Pattern is very closely related to the Bridge and could be a direct copy of the Bridge when it is implemented with a Factory. Where Bridge can be used to create dynamic Classes, the Delegate can be used to create a dynamic object: to execute actions in a specific way according to your needs.

 

 

Variations on the Bridge: when you need something special

The most common implementations of the Bridge instantiate the concrete implementation in your Bridge via a Factory, a Strategy Provider or using internal code. Instead, you might need slightly different ways to do this, using for instance a State Pattern or a Data Map.

[[::Link: PAT-BRI-VAR]]

State Pattern: similar behavior, changes from the inside

The State Pattern uses the same setup as most of the Wrappers described in “Wrapping, Interfacing and Abstraction”. In “Relationships between the patterns” the State is mentioned as well.

 

 

The Factory Pattern

As discussed, the Factory Pattern can be used to provide the object that will be used to perform the concrete actions.

 

 

Object Maps, Multitons, Singletons: using existing objects / Inversion of Control

Instead of using a Factory, you can use a object Map, Singleton or Multiton to provide the object the Bridge will work with. 

[[::Link:DTAPO]]

Inversion of Control

As discussed before, the Bridge can use a object Map, Multiton or Singleton to achieve Inversion of Control.

 

 

 

 

Bonus material

 

Using the Adapter within the Bridge for incompatible classes

 

 

 

Dependencies:

 

Bridge A:

1.a: Uses Factory A / Strategy B

1.b: Contains / calls methods on variable A

1.c: Implements / extends Interface A / BaseClass B

 

Factory A / Strategy B

2: Returns Implementation A, B or C

 

Variable A:

3.a: Is of type Interface A / BaseClass B

3.b: Is either Implementation A, B or C

 

Implementation A, B and C:

4.a: Implement Interface A / BaseClass B

4.b: Can actually be an Adapter (Adapter A, B and C, only A displayed)

 

Adapter A:

5.a: Implements Interface A / BaseClass B

5.b: Extends / Adapts Class C

 

 

Notes:

 

Using either the Factory Method or Object Map

 

 

 

Dependencies:

 

Your Class:

1.a: Has a Context

1.b: Uses / extends Bridge A

 

Bridge A:

2.a: Uses either Factory Method or Strategy

2.b: Pretends to be  Product X / Strategy Y

 

Factory Mathod / Strategy:

3: Returns Product X / Strategy Y

 

Product X / Strategy Y:

4: Relates to the Context on Your Class

 

 

Notes:

 

Factory and Strategy are one option

You can achieve the dynamic aspect of the Bridge by using the Factory and Strategy Patterns. Another options is to use the State Pattern. More on that later.

 

Using a Context

You need to inform your Bridge somehow what kind of object(s) it should instantiate. For this, you can pass a Context that is used by the Factory or Strategy Provider to return the appropriate Product or Strategy.

 

Implementing the Bridge Pattern with a Factory or ObjecT Map

 

 

Dependencies:

 

BridgeA:

1.a: Uses – Factory A or Strategy B to create a Concrete object

1.b: Contains and Calls methods on variable A

1.c: Implements or Extends InterfaceA / AbstractClass/BaseClassB

 

Factory A / Strategy B:

2: Returns an object, based on the concrete implementation A, B or C

 

Variable A:

3.a: Is of type InterfaceA or AbstractClassB

3.b: Is either Implenetation A, B or C

 

Concrete Implementations A, B, C

4: Implements Interface A or extend Abstract Class B

 


 

 

Notes:

 

Bridge can implement any Concrete Implementation

As discussed, this setup makes it possible to implement any of A, B and C and access them via Bridge A and variable A.

 

Bridge can pretend to be any object

It allows Bridge A to pretend to be any object it instantiates this way, going as far as allowing you to Extend Bridge A as a Concrete class which can take any Concrete shape as you use it in your code.

 

Using Adapters for incompatible Classes

As discussed before, when you want to use or need to use Classes not compatible with your Interface or Base Class, you can use an Adapter for that Class to adapt the Interface.


 

 

Using a Object Map, Multiton or Singleton to Bridge and access an existing object

 

 

 

Dependencies:

 

Bridge A:

1.a: Uses Singleton A, Multiton B, Object Map C

1.b: Contains / calls methods on variable A

1.c: Implements / extends Interface A / BaseClass B

1.d: Has a Context

 

Singleton A / Multiton B / Object Map C:

2.a: Uses the Context

2.b: Returns Implementation A, B or C

 

Variable A:

3.a: Is of type Interfface A / BaseClass B

3.b: Contains either Implementation A, B or C

 

Implementation A, B or C:

4: Implement / extend Interface A / BaseClass B

 


 

 

Notes:

 

Using a Factory / Strategy Provider

To get a specific Object Map / Singleton / Multiton you might want to use a Factory / Strategy provider

 

The difference in approach: Mapping re-usable objects

The difference in approach to the previous implementations in this chapter on the Bridge Pattern is the use of three Patterns that allow you to map, store, share, re-use and retrieve already Instantiated objects with a specific implementation.

Where the Singleton simply returns one Instance, the Multiton and Object Map allow you to store and retrieve several Instantiated (persistent) objects.

 

Using the Context to retrieve a specific object

You can map the Concrete Implementations in your object Map and Multiton using the Context as your reference.

 

Inversion of Control and Dependency Injection

This variation on the Bridge allows you to apply Inversion of Control and Dependency Injection, by moving the responsibility of choosing which Implementation should be used under what Context outside the Bridge.

 

Using Adapters for incompatible Classes

As discussed before, when you want to use or need to use Classes not compatible with your Interface or Base Class, you can use an Adapter for that Class to adapt the Interface.

 

The difference in this implementation is in using a Object Map, Multiton or Singleton in step 1.a.

 

As long as the returned object uses the same Interface (InterfaceA) or BaseClass (AbstractClassB) it is interchangeable with any other Class and object in that range and useable by the Bridge.

 

 


 

 

Aspects from “Design Patterns”

 

From: Motivation

[..] When an abstraction can have one of several possible implementations, the usual way to accommodate them is to use inheritance. An abstract class defines the interface to the abstraction, and concrete subclasses implement it in different ways.

[..]

The Bridge pattern addresses these problems by putting the Window abstraction and its implementation in separate class hierarchies.

 

From: Consequences

[..] An implementation is not bound permanently to an interface. The implementation of an abstraction can be configured at run-time. It's even possible for an object to change its implementation at run-time.

 

From: Implementation

[..] In situations where there's only one implementation, creating an abstract Implementor class isn't necessary.

[..]

How, when, and where do you decide which Implementor class to instantiate when there's more than one?

[..] If Abstraction knows about all ConcreteImplementor classes, then it can instantiate one of them in its

constructor; it can decide between them based on parameters passed to its constructor.

[..] Another approach is to choose a default implementation initially and change it later according to usage.

[..] It's also possible to delegate the decision to another object altogether. In the Window/WindowImp example, we can introduce a factory object

 

From: Related patterns

An  Abstract Factory can create and configure a particular Bridge.

 

The Adapter pattern is geared toward making unrelated classes work together. [..] Bridge, on the other hand, is used up-front in a design to let abstractions and implementations vary independently.


 

Peter Kaptein © 2012 Created: December 17, 2011 Last update: April 22, 2012

Refactoring and Design Patterns

Peter Kaptein

 

Last update: June 01, 2012

 

Status

Chapters and Patterns marked with: ** are in revision

Patterns marked with a: * are not addressed yet

 

Downloads

Downloads include: presentations and cheat sheets on Refactoring, OOP and Design Patterns.

No permission is needed to use this content to teach to classes or to use it within your company.

 

Sites and blogs

My Professional blog

 

Beyond the Keyboard

Blog posts on hardware hacking and tinkering

HotForestGreen

Internet of Things Framework

 

© 2011, 2012