Jump to content

Bridge

25% developed
From Wikibooks, open books for an open world

Adapter Computer Science Design Patterns
Bridge
Builder

Bridge pattern is useful when a code often changes for an implementation as well as for a use of code. In your application, you should have provider classes and client classes:

Client1
-provider: Provider
 
Client2
-provider: Provider
...
ClientN
-provider: Provider
◊—
Provider
+doProcess()
...
Provider1
+doProcess()
Provider2
+doProcess()
...
ProviderN
+doProcess()

Each client class can interact with each provider class. However, if the implementation changes, the method signatures of the Provider interface may change and all the client classes have to change. In the same way, if the client classes need a new interface, we need to rewrite all the providers. The solution is to add a bridge, that is to say a class that will be called by the clients, that contains a reference to the providers and forward the client call to the providers.

Client1
-bridge: Bridge
 
Client2
-bridge: Bridge
...
ClientN
-bridge: Bridge
◊—
Bridge
-provider: Provider
+doProcessForClient()
◊—
Provider
+doProcess()
...
Provider1
+doProcess()
Provider2
+doProcess()
...
ProviderN
+doProcess()

In the future, the two interfaces (client/bridge and bridge/provider) may change independently and the bridge may trans-code the call order.

Examples

It's hard to find an example in a library as this pattern is designed for versatile specifications and a library does not change constantly between two versions.

Cost

The cost of this pattern is the same as the adapter. It can be planned at design time but the best way to decide to add it is the experience feedback. Implement it when you have frequently changed an interface in a short time.

Creation

Its implementation is easy but can be expensive. It depends on the complexity of the interface. The more methods you have, the more expensive it will be.

Maintenance

If you always update the client/bridge interface and the bridge/provider interface the same way, it would be more expensive than if you do not implement the design pattern.

Removal

This pattern can be easily removed as automatic refactoring operations can easily remove its existence.

Advices

  • Put the bridge term in the name of the bridge class to indicate the use of the pattern to the other developers.

Implementation

Implementation in Kotlin
/** "Implementor" */
fun interface MusicPlayer {
    fun play(song: String): String
}

/** "ConcreteImplementor" 1/4 */
val defaultPlayer = MusicPlayer{
    "Reproducing $it in ${MusicFormat.NONE}"
}

/** "ConcreteImplementor" 2/4 */
val mp3Player = MusicPlayer{
    "Reproducing $it in ${MusicFormat.MP3}"
}

/** "ConcreteImplementor" 3/4 */
val mp4Player = MusicPlayer{
    "Reproducing $it in ${MusicFormat.MP4}"
}

/** "ConcreteImplementor" 4/4 */
val vlcPlayer = MusicPlayer{
    "Reproducing \"$it\" in ${MusicFormat.VLC}"
}

/** "Abstraction" */
abstract class MusicPlayerUI {
    var musicPlayer : MusicPlayer = defaultPlayer
    abstract fun playMusic(song: String, musicFormat: MusicFormat)
}

 /** "Refined Abstraction" */
class MyMusicPlayerUI : MusicPlayerUI() {
    override fun playMusic(song: String, musicFormat: MusicFormat) {
        musicPlayer = when(musicFormat) {
            MusicFormat.NONE -> defaultPlayer
            MusicFormat.MP3 -> mp3Player
            MusicFormat.MP4 -> mp4Player
            MusicFormat.VLC -> vlcPlayer
        }
        println(musicPlayer.play(song))
    }

}

enum class MusicFormat{
    NONE,MP3,MP4,VLC
}

 /** "Client" */
object BridgePattern {
    @JvmStatic
    fun main(args: Array<String>) {
        val musicPlayer = MyMusicPlayerUI()
        musicPlayer.playMusic("The best song", MusicFormat.MP4)
    }
}
Implementation in Java

The following Java (SE 6) program illustrates the bridge pattern.

/**
 * Implementor
 */
interface DrawingAPI {
    public void drawCircle(double x, double y, double radius);
}
/**
 * ConcreteImplementor  1/2
 */
class DrawingAPI1 implements DrawingAPI {
   public void drawCircle(double x, double y, double radius) {
        System.out.printf("API1.circle at %f:%f radius %f\n", x, y, radius);
   }
}
/**
 * ConcreteImplementor 2/2
 */
class DrawingAPI2 implements DrawingAPI {
   public void drawCircle(double x, double y, double radius) {
        System.out.printf("API2.circle at %f:%f radius %f\n", x, y, radius);
   }
}
/**
 * Abstraction
 */
abstract class Shape {
   protected DrawingAPI drawingAPI;

   protected Shape(DrawingAPI drawingAPI) {
      this.drawingAPI = drawingAPI;
   }

   public abstract void draw();                             // low-level
   public abstract void resizeByPercentage(double pct);     // high-level
}
/**
 * Refined Abstraction
 */
class CircleShape extends Shape {
   private double x, y, radius;
   public CircleShape(double x, double y, double radius, DrawingAPI drawingAPI) {
      super(drawingAPI);
      this.x = x;
      this.y = y;
      this.radius = radius;
   }

   // low-level i.e. Implementation specific
   public void draw() {
        drawingAPI.drawCircle(x, y, radius);
   }

   // high-level i.e. Abstraction specific
   public void resizeByPercentage(double pct) {
        radius *= pct;
   }
}
/**
 * Client
 */
class BridgePattern {
   public static void main(String[] args) {
       Shape[] shapes = new Shape[] {
           new CircleShape(1, 2, 3, new DrawingAPI1()),
           new CircleShape(5, 7, 11, new DrawingAPI2()),
       };

       for (Shape shape : shapes) {
           shape.resizeByPercentage(2.5);
           shape.draw();
       }
   }
}

It will output:

API1.circle at 1.000000:2.000000 radius 7.5000000
API2.circle at 5.000000:7.000000 radius 27.500000
Implementation in PHP
interface DrawingAPI {
    function drawCircle($dX, $dY, $dRadius);
}

class DrawingAPI1 implements DrawingAPI {

    public function drawCircle($dX, $dY, $dRadius) {
        echo "API1.circle at ".$dX.":".$dY." radius ".$dRadius."<br/>";
    }
}

class DrawingAPI2 implements DrawingAPI {

    public function drawCircle($dX, $dY, $dRadius) {
        echo "API2.circle at ".$dX.":".$dY." radius ".$dRadius."<br/>";
    }
}

abstract class Shape {

    protected $oDrawingAPI;

    public abstract function draw();
    public abstract function resizeByPercentage($dPct);

    protected function __construct(DrawingAPI $oDrawingAPI) {
        $this->oDrawingAPI = $oDrawingAPI;
    }
}

class CircleShape extends Shape {

    private $dX;
    private $dY;
    private $dRadius;

    public function __construct(
            $dX, $dY,
            $dRadius,
            DrawingAPI $oDrawingAPI
    ) {
        parent::__construct($oDrawingAPI);
        $this->dX = $dX;
        $this->dY = $dY;
        $this->dRadius = $dRadius;
    }

    public function draw() {
        $this->oDrawingAPI->drawCircle(
                $this->dX,
                $this->dY,
                $this->dRadius
        );
    }

    public function resizeByPercentage($dPct) {
        $this->dRadius *= $dPct;
    }
}

class Tester {

    public static function main()  {
        $aShapes = array(
            new CircleShape(1, 3, 7,  new DrawingAPI1()),
            new CircleShape(5, 7, 11, new DrawingAPI2()),
        );

        foreach ($aShapes as $shapes) {
            $shapes->resizeByPercentage(2.5);
            $shapes->draw();
        }
    }
}

Tester::main();

Output:

   API1.circle at 1:3 radius 17.5
   API2.circle at 5:7 radius 27.5
Implementation in C#

C#

The following C# program illustrates the "shape" example given above and will output:

API1.circle at 1:2 radius 7.5
API2.circle at 5:7 radius 27.5
 using System;
 
 /** "Implementor" */
 interface IDrawingAPI {
    void DrawCircle(double x, double y, double radius);
 }
 
 /** "ConcreteImplementor" 1/2 */
 class DrawingAPI1 : IDrawingAPI {
    public void DrawCircle(double x, double y, double radius)
    {
        System.Console.WriteLine("API1.circle at {0}:{1} radius {2}", x, y, radius);
    }
 }
 
 /** "ConcreteImplementor" 2/2 */
 class DrawingAPI2 : IDrawingAPI
 {
    public void DrawCircle(double x, double y, double radius)
    {
        System.Console.WriteLine("API2.circle at {0}:{1} radius {2}", x, y, radius);
    }
 }
 
 /** "Abstraction" */
 interface IShape {
    void Draw();                             // low-level (i.e. Implementation-specific)
    void ResizeByPercentage(double pct);     // high-level (i.e. Abstraction-specific)
 }
 
 /** "Refined Abstraction" */
 class CircleShape : IShape {
    private double x, y, radius;
    private IDrawingAPI drawingAPI;
    public CircleShape(double x, double y, double radius, IDrawingAPI drawingAPI)
    {
        this.x = x;  this.y = y;  this.radius = radius;
        this.drawingAPI = drawingAPI;
    }
    // low-level (i.e. Implementation-specific)
    public void Draw() { drawingAPI.DrawCircle(x, y, radius); }
    // high-level (i.e. Abstraction-specific)
    public void ResizeByPercentage(double pct) { radius *= pct; }
 }
 
 /** "Client" */
 class BridgePattern {
    public static void Main(string[] args) {
        IShape[] shapes = new IShape[2];
        shapes[0] = new CircleShape(1, 2, 3, new DrawingAPI1());
        shapes[1] = new CircleShape(5, 7, 11, new DrawingAPI2());
 
        foreach (IShape shape in shapes) {
            shape.ResizeByPercentage(2.5);
            shape.Draw();
        }
    }
 }

C# using generics

The following C# program illustrates the "shape" example given above and will output:

API1.circle at 1:2 radius 7.5
API2.circle at 5:7 radius 27.5
 using System;
 
 /** "Implementor" */
 interface IDrawingAPI {
    void DrawCircle(double x, double y, double radius);
 }
 
 /** "ConcreteImplementor" 1/2 */
 struct DrawingAPI1 : IDrawingAPI {
    public void DrawCircle(double x, double y, double radius)
    {
        System.Console.WriteLine("API1.circle at {0}:{1} radius {2}", x, y, radius);
    }
 }
 
 /** "ConcreteImplementor" 2/2 */
 struct DrawingAPI2 : IDrawingAPI
 {
    public void DrawCircle(double x, double y, double radius)
    {
        System.Console.WriteLine("API2.circle at {0}:{1} radius {2}", x, y, radius);
    }
 }
 
 /** "Abstraction" */
 interface IShape {
    void Draw();                             // low-level (i.e. Implementation-specific)
    void ResizeByPercentage(double pct);     // high-level (i.e. Abstraction-specific)
 }
 
 /** "Refined Abstraction" */
 class CircleShape<T> : IShape
    where T : struct, IDrawingAPI
 {
    private double x, y, radius;
    private IDrawingAPI drawingAPI = new T();
    public CircleShape(double x, double y, double radius)
    {
        this.x = x;  this.y = y;  this.radius = radius;
    }
    // low-level (i.e. Implementation-specific)
    public void Draw() { drawingAPI.DrawCircle(x, y, radius); }
    // high-level (i.e. Abstraction-specific)
    public void ResizeByPercentage(double pct) { radius *= pct; }
 }
 
 /** "Client" */
 class BridgePattern {
    public static void Main(string[] args) {
        IShape[] shapes = new IShape[2];
        shapes[0] = new CircleShape<DrawingAPI1>(1, 2, 3);
        shapes[1] = new CircleShape<DrawingAPI2>(5, 7, 11);
 
        foreach (IShape shape in shapes) {
            shape.ResizeByPercentage(2.5);
            shape.Draw();
        }
    }
 }
Implementation in Python

The following Python program illustrates the "shape" example given above and will output:

API1.circle at 1:2 7.5
API2.circle at 5:7 27.5
# Implementor
class DrawingAPI:
    def drawCircle(x, y, radius):
        pass


# ConcreteImplementor 1/2
class DrawingAPI1(DrawingAPI):
    def drawCircle(self, x, y, radius):
        print("API1.circle at %f:%f radius %f" % (x, y, radius))


# ConcreteImplementor 2/2
class DrawingAPI2(DrawingAPI):
    def drawCircle(self, x, y, radius):
        print("API2.circle at %f:%f radius %f" % (x, y, radius))


# Abstraction
class Shape:
    # low-level
    def draw(self):
        pass

    # high-level
    def resizeByPercentage(self, pct):
        pass


# Refined Abstraction
class CircleShape(Shape):
    def __init__(self, x, y, radius, drawingAPI):
        self.__x = x
        self.__y = y
        self.__radius = radius
        self.__drawingAPI = drawingAPI

    # low-level i.e. Implementation specific
    def draw(self):
        self.__drawingAPI.drawCircle(self.__x, self.__y, self.__radius)

    # high-level i.e. Abstraction specific
    def resizeByPercentage(self, pct):
        self.__radius *= pct


def main():
    shapes = [
        CircleShape(1, 2, 3, DrawingAPI1()),
        CircleShape(5, 7, 11, DrawingAPI2())
    ]

    for shape in shapes:
        shape.resizeByPercentage(2.5)
        shape.draw()


if __name__ == "__main__":
    main()
Implementation in Ruby

An example in Ruby.

class Abstraction
  def initialize(implementor)
    @implementor = implementor
  end

  def operation
    raise 'Implementor object does not respond to the operation method' unless @implementor.respond_to?(:operation)
    @implementor.operation
  end
end

class RefinedAbstraction < Abstraction
  def operation
    puts 'Starting operation... '
    super
  end
end

class Implementor
  def operation
    puts 'Doing necessary stuff'
  end
end

class ConcreteImplementorA < Implementor
  def operation
    super
    puts 'Doing additional stuff'
  end
end

class ConcreteImplementorB < Implementor
  def operation
    super
    puts 'Doing other additional stuff'
  end
end

normal_with_a = Abstraction.new(ConcreteImplementorA.new)
normal_with_a.operation
# Doing necessary stuff
# Doing additional stuff

normal_with_b = Abstraction.new(ConcreteImplementorB.new)
normal_with_b.operation
# Doing necessary stuff
# Doing other additional stuff

refined_with_a = RefinedAbstraction.new(ConcreteImplementorA.new)
refined_with_a.operation
# Starting operation...
# Doing necessary stuff
# Doing additional stuff

refined_with_b = RefinedAbstraction.new(ConcreteImplementorB.new)
refined_with_b.operation
# Starting operation...
# Doing necessary stuff
# Doing other additional stuff
Implementation in Scala

A Scala implementation of the Java drawing example with the same output.

  /** "Implementor" */
  trait DrawingAPI {
    def drawCircle(x:Double, y:Double, radius:Double)
  }
 
  /** "ConcreteImplementor" 1/2 */
  class DrawingAPI1 extends DrawingAPI {
    def drawCircle(x:Double, y:Double, radius:Double) {
      printf("API1.circle at %f:%f radius %f\n", x, y, radius)
    }
  }
 
  /** "ConcreteImplementor" 2/2 */
  class DrawingAPI2 extends DrawingAPI {
    def drawCircle(x:Double, y:Double, radius:Double) {
      printf("API2.circle at %f:%f radius %f\n", x, y, radius)
    }
  }
 
  /** "Abstraction" */
  trait Shape {
     def draw()                             // low-level
     def resizeByPercentage(pct:Double)     // high-level
  }
 
  /** "Refined Abstraction" */
  class CircleShape(var x:Double, var y:Double,
    var radius:Double, val drawingAPI:DrawingAPI) extends Shape {
   
    // low-level i.e. Implementation specific
    def draw() = drawingAPI.drawCircle(x, y, radius)
   
    // high-level i.e. Abstraction specific
    def resizeByPercentage(pct:Double) = radius *= pct
  }
 
  /** "Client" */
  val shapes = List(
    new CircleShape(1, 2, 3, new DrawingAPI1),
    new CircleShape(5, 7, 11, new DrawingAPI2)
  )
 
  shapes foreach { shape =>
    shape.resizeByPercentage(2.5)
    shape.draw()
  }
Implementation in D

An example in D.

import std.stdio;

/** "Implementor" */
interface DrawingAPI {
    void drawCircle(double x, double y, double radius);
}

/** "ConcreteImplementor" 1/2 */
class DrawingAPI1: DrawingAPI {
    void drawCircle(double x, double y, double radius) {
        writefln("\nAPI1.circle at %f:%f radius %f", x, y, radius);
    }
}

/** "ConcreteImplementor" 2/2 */
class DrawingAPI2: DrawingAPI {
    void drawCircle(double x, double y, double radius) {
        writefln("\nAPI2.circle at %f:%f radius %f", x, y, radius);
    }
}

/** "Abstraction" */
interface Shape {
    void draw(); // low-level
    void resizeByPercentage(double pct); // high-level
}

/** "Refined Abstraction" */
class CircleShape: Shape {
    this(double x, double y, double radius, DrawingAPI drawingAPI) {
        this.x = x;
        this.y = y;
        this.radius = radius;
        this.drawingAPI = drawingAPI;
    }

    // low-level i.e. Implementation specific
    void draw() {
        drawingAPI.drawCircle(x, y, radius);
    }
    // high-level i.e. Abstraction specific
    void resizeByPercentage(double pct) {
        radius *= pct;
    }
private:
    double x, y, radius;
    DrawingAPI drawingAPI;
}

int main(string[] argv) {
    auto api1 = new DrawingAPI1();
    auto api2 = new DrawingAPI2();

    auto c1 = new CircleShape(1, 2, 3, api1);
    auto c2 = new CircleShape(5, 7, 11, api2);

    Shape[4] shapes;
    shapes[0] = c1;
    shapes[1] = c2;

    shapes[0].resizeByPercentage(2.5);
    shapes[0].draw();
    shapes[1].resizeByPercentage(2.5);
    shapes[1].draw();

    return 0;
}
Implementation in Perl

This example in Perl uses the MooseX::Declare module.

# Implementor
role Drawing::API {
    requires 'draw_circle';
}

# Concrete Implementor 1
class Drawing::API::1 with Drawing::API {
    method draw_circle(Num $x, Num $y, Num $r) {
        printf "API1.circle at %f:%f radius %f\n", $x, $y, $r;
    }
}

# Concrete Implementor 2
class Drawing::API::2 with Drawing::API {
    method draw_circle(Num $x, Num $y, Num $r) {
        printf "API2.circle at %f:%f radius %f\n", $x, $y, $r;
    }
}

# Abstraction
role Shape {
    requires qw( draw resize );
}

# Refined Abstraction
class Shape::Circle with Shape {
    has $_  => ( is => 'rw', isa  => 'Any' ) for qw( x y r );
    has api => ( is => 'ro', does => 'Drawing::API' );

    method draw() {
        $self->api->draw_circle( $self->x, $self->y, $self->r );
    }

    method resize(Num $percentage) {
        $self->{r} *= $percentage;
    }
}

my @shapes = (
    Shape::Circle->new( x=>1, y=>2, r=>3,  api => Drawing::API::1->new ),
    Shape::Circle->new( x=>5, y=>7, r=>11, api => Drawing::API::2->new ),
)

$_->resize( 2.5 ) and $_->draw for @shapes;
Implementation in Delphi

The following Delphi program illustrates the "shape" example given above and will output:

API1.circle at 1:2 7.5
API2.circle at 5:7 27.5
program Bridge;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

type

  ///implementator
  TDrawingAPI = class abstract
    procedure DrawCircle(x, y, radius: double); virtual; abstract;
  end;

  //ConcreteImplementator 1
  TDrawingAPI1 = class(TDrawingAPI)
    procedure DrawCircle(x, y, radius: double); override;
  end;

 //ConcreteImplementator 2
  TDrawingAPI2 = class(TDrawingAPI)
    procedure DrawCircle(x, y, radius: double);override;
  end;

  //Abstraction
  TShape = class abstract
    procedure Draw();virtual; abstract;// low-level (i.e. Implementation-specific)
    procedure ResizeByPercentage(pct: double);virtual; abstract;// high-level (i.e. Abstraction-specific)
  end;

  //Refined Abstraction
  TCircleShape = class(TShape)
    strict private
      x, y, radius: double;
      drawingAPI: TDrawingAPI;
    public
      constructor Create(x, y, radius: double; drawingAPI: TDrawingAPI);
      procedure Draw;override;
      procedure ResizeByPercentage(pct: double);override;
  end;

{ TDeawingAPI1 }

procedure TDrawingAPI1.DrawCircle(x, y, radius: double);
begin
  WriteLn('API1.circle at '+FloatToStr(x)+' : '+FloatToStr(y)+' radius '+FloatToStr(radius));
end;

{ TDeawingAPI }

procedure TDrawingAPI2.DrawCircle(x, y, radius: double);
begin
  WriteLn('API2.circle at '+FloatToStr(x)+' : '+FloatToStr(y)+' radius '+FloatToStr(radius));
end;

{ TCircleShape }

constructor TCircleShape.Create(x, y, radius: double; drawingAPI: TDrawingAPI);
begin
  self.x := x;
  self.y := y;
  self.radius := radius;
  self.drawingAPI := drawingAPI;
end;

procedure TCircleShape.Draw;
begin
  drawingAPI.DrawCircle(x, y, radius);
end;

procedure TCircleShape.ResizeByPercentage(pct: double);
begin
  radius := radius * pct;
end;

var shapes: array of TShape;
    shape: TShape;
begin
  try
    { TODO -oUser -cConsole Main : Insert code here }
    SetLength(shapes, 2);
    shapes[0] := TCircleShape.Create(1, 2, 3, TDrawingAPI1.Create);
    shapes[1] := TCircleShape.Create(5, 7, 11, TDrawingAPI2.Create);

    for shape in shapes do
    begin
       shape.ResizeByPercentage(2.5);
       shape.Draw;
    end;

    WriteLn(#13#10+'Press any key to continue..');
    ReadLn;

    shapes[0].Free;
    shapes[1].Free;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.


Clipboard

To do:
Add more illustrations.


Adapter Computer Science Design Patterns
Bridge
Builder


You have questions about this page?
Ask it here:


Create a new page on this book: