The Bridge Pattern

Handle-Body

Problem

Inheritance hierarchies can be hierarchies of concepts or hierarchies of implementations. For example:

Mixing concepts with implementations can lead to hierarchies that are confusing and difficult to maintain.

Solution

Create two hierarchies: a conceptual hierarch and a hierarchy of implementations. The base of the implementation hierarchy is an Implementer interface. Use delegation to connect the two hierarchies:

Structure

Behavior

Discussion

Assume we want to define a portable GUI library. Our conceptual hierarchy might use the Composite Pattern to define a conceptual hierarchy of GUI components:

The draw() methods in the subclasses call upon protected utility methods inherited from the component base class:

class Button: public Component
{
private:
   string label;
public:
   void draw()
   {
      drawRectangle();
      drawText(xc, yc, label);
   }
};

The protected utility methods in the Component base class delegate to a private peer that implements a utility interface:

Here's the interface:

class ComponentImplementer
{
public:
   virtual void drawRectangle(int xc, int yc, int h, int w) = 0;
   virtual void drawText(int xc, int yc, string text) = 0;
};

Here's the complete code for the conceptual Component base class:

class Component
{
private:
   ComponentImplementer* peer;
protected:
   int height, width;
   int xc, yc; // co-ordinates of upper left corner
   ComponentImplementer* getPeer() { return peer; }
   void setPeer(ComponentImplementer* peer)
   {
      this->peer = peer;
   }
   // some utilities:
   void drawRectangle()
   {
      peer->drawRectangle(xc, yc, height, width);
   }
   void drawText(int xc, int yc, string text)
   {
      peer->drawText(xc, yc, text);
   }
public:
   Component(ComponentImplementer* peer = 0)
   {
      xc = yc = 0;
      height = width = 150;
      this->peer = peer;
   }
   virtual void draw() = 0;
};


Suppose X-Kit and Y-Lib are two libraries for creating platform-dependent GUIs. For each library we will need to provide an implementation of the Implementer interface. This can be regarded as a special case of the Adapter Pattern:

class XComponent: public ComponentImplementer
{
public:
   void drawRectangle(int xc, int yc, int h, int w)
   {
      // X-kit specific code goes here
   }
   void drawText(int xc, int yc, string text)
   {
      // X-kit specific code goes here
   }
};

Examples

Drivers

Concept = JDBC Statement

ConceptA = JDBC PreparedStatement

Implementor = Driver

ImplementationX = ODBCBridge

ImplementationY = OracleDriver

Java uses the JDBC API for interacting with relational database systems. The application first uses a driver manager to register all available database drivers on the local machine. The manager asks each registered driver to start a session (Connection) with the database specified by the application. Once a session begins, the application uses a Statement object to query the database.

There are several types of drivers:

Graphical Contexts

Concept = Shape

ConceptA = Circle

Implementor = GC

ImplementationX = PoorMansGC

ImplementationY = OpenGL adapter

GUI Toolkits

Concept = GUI Component

ConceptA = Button

Implementor = IComponent

ImplementationX = XWindowsComponentAdapter

ImplementationY = MSWindowsComponentAdapter

Platform Independence

Concept = AbstractPlatform

ConceptA =

Implementor = PlatformInterface

ImplementationX = UnixPlatformAdapter

ImplementationY = WindowsPlatformAdapter

Drawing Shapes

Concept

ConceptA

Implementor

ImplementationX

ImplementationY

List

Concept = Deque

ConceptA = Stack or Queue

Implementor = List

ImplementationX = LinkedList

ImplementationY = ArrayList

Cascading Bridges