Wednesday, February 10, 2010

Design patterns : Bridge Design Pattern

Bridge pattern decouples the abstraction from its implementation so that both can vary independently. It is quite similar to the adapter pattern. An adapter pattern makes two unrelated, existing classes work together, when the two participants were not thought to be aware of each other during design. A bridge pattern separates concerns and is chosen at the design level before the creation of participating classes.

There is an abstraction that the client sees. There is an implementor which provides the interface for actual implementation. A refined abstraction which provides extension to the abstraction's functionalities. And a ConcreteImplementor which is an implementation of the implementor.

PHP Code :

abstract class BridgeBook
{
  private $bbAuthor;
  private $bbTitle;
  private $bbImp;
  function __construct($author_in, $title_in, $choice_in)
  {
    $this->bbAuthor = $author_in;
    $this->bbTitle  = $title_in;
    if ('STARS' == $choice_in)
    {
      $this->bbImp = new BridgeBookStarsImp();
    } else
    {
      $this->bbImp = new BridgeBookCapsImp();
    }
  }
  function showAuthor()
  {
    return $this->bbImp->showAuthor($this->bbAuthor);
  }
  function showTitle()
  {
    return $this->bbImp->showTitle($this->bbTitle);
  }
}

class BridgeBookAuthorTitle extends BridgeBook
{
  function showAuthorTitle()
  {
    return $this->showAuthor() . "'s " . $this->showTitle();
  }
}

class BridgeBookTitleAuthor extends BridgeBook
{
  function showTitleAuthor()
  {
    return $this->showTitle() . ' by ' . $this->showAuthor();
  }
}

abstract class BridgeBookImp
{
  abstract function showAuthor($author);
  abstract function showTitle($title);
}

class BridgeBookCapsImp extends BridgeBookImp
{
  function showAuthor($author_in)
  {
    return strtoupper($author_in);
  }
  function showTitle($title_in)
  {
    return strtoupper($title_in);
  }
}

class BridgeBookStarsImp extends BridgeBookImp
{
  function showAuthor($author_in)
  {
    return Str_replace(" ","*",$author_in);
  }
  function showTitle($title_in)
  {
    return Str_replace(" ","*",$title_in);
  }
}

echo('BEGIN'."\n");

echo('test 1 - author title with caps');
$book = new BridgeBookAuthorTitle('Larry Truett','PHP for Cats','CAPS');
echo($book->showAuthorTitle());
echo("\n");

echo('test 2 - author title with stars');
$book = new BridgeBookAuthorTitle('Larry Truett','PHP for Cats','STARS');
echo($book->showAuthorTitle());
echo("\n");

echo('test 3 - title author with caps');
$book = new BridgeBookTitleAuthor('Larry Truett','PHP for Cats','CAPS');
echo($book->showTitleAuthor());
echo("\n");

echo('test 4 - title author with stars');
$book = new BridgeBookTitleAuthor('Larry Truett','PHP for Cats','STARS');
echo($book->showTitleAuthor());
echo("\n");

echo('END'."\n");

Output : 
BEGIN
test 1 - author title with capsLARRY TRUETT's PHP FOR CATS
test 2 - author title with starsLarry*Truett's PHP*for*Cats
test 3 - title author with capsPHP FOR CATS by LARRY TRUETT
test 4 - title author with starsPHP*for*Cats by Larry*Truett
END



Java Code :

/** "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" */
interface Shape 
{
  public void draw();                             // low-level
  public void resizeByPercentage(double pct);     // high-level
}

/** "Refined Abstraction" */
class CircleShape implements Shape 
{
  private double x, y, radius;
  private DrawingAPI drawingAPI;
  public CircleShape(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
  public void draw() 
  {
    drawingAPI.drawCircle(x, y, radius);
  }   
  // high-level i.e. Abstraction specific
  public void resizeByPercentage(double pct) 
  {
    radius *= pct;
  }
}

/** "Client" */
public class Bridge
{
  public static void main(String[] args) 
  {
    Shape[] shapes = new Shape[2];
    shapes[0] = new CircleShape(1, 2, 3, new DrawingAPI1());
    shapes[1] = new CircleShape(5, 7, 11, new DrawingAPI2());

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

Output :

API1.circle at 1.000000:2.000000 radius 7.500000
API2.circle at 5.000000:7.000000 radius 27.500000

No comments: