PHP Bridge Design Pattern

armeniaTwo Interfaces

The intent of the Bridge pattern is to decouple an abstraction from its implementation so that the two can vary independently. (GoF 151) If you don’t think about that much, it sounds like a good idea to keep an application from grinding its gears when a change is made in either the abstraction or implementation. The Freemans (Head First Design Patterns, pp. 612-613) have a great example—a universal remote control. The remote control is the abstraction and a TV set is the implementor. Concrete implementors are the different brands of TVs. As new technology arises, the remote control can be updated with new gizmos. Likewise, the TV sets can also be updated and different brands will have their own unique features. A good Bridge design will allow each to be changed without breaking the other. So far so good.

An Abstraction Has-A Implementor

About six years ago, I started looking at different Bridge design pattern examples. I ran into examples that were examples of something but did not seem to be Bridge patterns. (One was nothing more than a single interface with two different implementations. The coder explained, See, that’s a bridge. I sat there with question marks floating above my head like some cartoon character.) The GoF intent statement and Freeman example were quite clear. However, the Abstraction interface and the Implementor interface, either of which could be an abstract class or interface structure, were bridged by the abstraction having-a implementor. To fully understand a Bridge pattern, you need to understand the relationship known as a bridge between the Abstraction and Implementor participants. First, though, see what the results are by pressing the Play button and Download the files:
PlayDownload

For an overview, take a look at Figure 1. The top half is the structure laid out by The Gang of Four—the generic class diagram for a Bridge pattern. The bottom half is the PHP implementation that addresses a common issue: Variation in the content of a Web page and the structure of a Web page. By having two interfaces, either can change independently of the other. The example shows a Web page with variable content that is displayed in desktop and mobile devices.

Figure 1: Bridge Class diagrams

Figure 1: Bridge Class diagrams


One of the important features of the Bridge pattern turns out to be quite subtle and easily overlooked.

The key method (Operation()) in the Abstraction participant is a concrete one.

In the example for this post, the IPage abstract class includes the buildPage() method. The buildPage() method uses a concrete implementation of the Implementor participant, (IDevice). You can see how the method is implemented in the IPage abstract class:

<?php
abstract class IPage 
{
    //Low-level
    abstract function doDevice(IDevice $deviceNow);
    //High-level
    protected function buildPage()
    {
        echo $this->deviceSelected->buildDevice($this->header,$this->graphic,$this->body);
    }
    //Properties
    protected $header, $graphic, $body, $deviceSelected;
}
?>

As you can see, the abstract function doDevice() expects an IDevice object as a parameter. The $deviceSelected property is one that will be assigned a concrete Implementor (IDevice). So, in the sense that both an abstract reference using code hinting and a concrete use of an implemented IDevice are part of the IPage abstract class, it can be said to “have a” Implementor (IDevice). (Click below to see the rest of the Bridge pattern.)

A Refined Abstraction

Nowhere else in the GoF book is there mention of a refined abstraction outside of the Bridge pattern. Generally, once an interface has abstracted methods, the job of those classes that implement the interface is to provide concrete operations. However, the refined abstraction extends the interface defined by the Abstraction (GoF, 154). So a refined abstraction is just an extension of another Interface (or Abstract class). Therein lies the final answer to the question about the implied aggregation. As you saw in the previous section, the Bridge design pattern is named after the bridge held up by aggregation between the Abstraction interface and the Implementor interface. However, we may not see that aggregation until the abstraction is refined in implementation by another class. The concrete implementation is done by the implementation of the Implementor class. In this example, the StandardPage class is the RefinedAbstraction.

<?php
class StandardPage extends IPage 
{    
    public function doDevice(IDevice $deviceNow)
    {
        $this->deviceSelected=$deviceNow;
        $this->setPage();
        $this->buildPage();
    }
 
    /*The setPage() method could be part of the client, but then
    the deDevice() method would require additional parameters. */
 
    private function setPage()
    {
        $this->header="Future Bridge";
        $this->graphic="airbridge.gif";
        $this->body="Hop on, and we'll be over this bridge in no time!";
    }
}
?>

At this point, the arrangement of the pattern clears up. The abstraction is separated from the implementation by refining the abstraction rather than implementing it by providing a concrete class. The implementation interface is implemented with concrete classes. However, the abstract class is the entry way for the client, which has-a implementation.

The setPage() method is relatively concrete for a refined abstraction, but I included it to cut down on parameters for the client to fill in. An alternative implementation would be to add more parameters to the doDevice() method and let the client take care of the concrete content.

Orthogonal Design

In the roughest sense, orthogonal refers to two (or more) elements that can vary independently of one another. For example, a Bridge may the Abstraction take care of the shape of an object and the Implementor the color. In that sense shape and color are orthogonal. Either could change and have no effect on the other.

In searching for a better characterization of orthogonal design, I came across an article, Design Pattern Orthogonal Component, that provides an example used with a state machine. In the state machine, the alarm clock is divided into two orthogonal regions: timekeeping and alarm. According to the article, dividing the state machine into two orthogonal regions is considered fairly expensive because each region requires a separate state-variable and extra work in dispatching events (e.g., ringing an alarm.)

As an alternative to orthogonal regions, the article suggests orthogonal components. In order to connect up the alarm with the alarm clock the article shows how to use aggregation to accomplish that linkage. It points out,

Concurrency virtually always arises within objects by aggregation; that is, multiple states of the components can contribute to a single state of the composite object.

Figure 2 shows how the page and device that formats the page are linked through aggregation. As such, the “bridge” in the Bridge design pattern is an orthogonal component. Because the IPage and IDevice are in an aggregate relationship, their life spans are identical. However, that does not mean that they cannot vary independently. Instead, it means that each can change independently without disrupting the other. At the same time the change will be reflected in the orthogonal relationship. For example, the structure (IDevice) of a Web page can change from desktop to smartphone, and the content (IPage) from C# to PHP without disrupting either and yet affecting both. The desktop page that used to discuss C# is now configured to display on iPhones and Androids and show a PHP discussion.

Figure 2:  Orthogonal  component in Bridge

Figure 2: Orthogonal component in Bridge

The Implementators

Now that the concept of the bridge is established, the “other end” of the bridge from the Abstractor is the Implementator, which in this example is IDesign interface. The interface itself is a single method:

<?php
interface IDevice
{  
    function buildDevice($head,$image,$text); 
}
?>

The three parameters reflect the three properties in IPage. If that seems to be a restrictively tight bind, instead of having three properties as parameters, you could use a single array. That would be more flexible, but a little messier as an initial example of the Bridge design pattern. All that’s left is the implementation of the concrete implementors:

//Desktop
<?php
class Desktop implements IDevice
{
    private $page;
    private $header;
    private $graphic;
    private $bodyText;
 
    public $pageNow;
    function buildDevice($head,$image,$text)
    {
        $this->header=$head;
        $this->graphic=$image;
        $this->bodyText=$text;
        //Begin heredoc string
        $this->pageNow=<<<DESKTOP
        <!DOCTYPE html>
        <html>
        <head>
        <link rel="stylesheet" type="text/css" href="bridge.css">
            <title>Desktop Bridge</title>
        </head>
        <body>
        <header><h2>$this->header</h2></header>
        <img src="desktoppix/$this->graphic" alt="Air" width="800" height="500">
        <p>$this->bodyText</p>
        </body>
        </html>
DESKTOP;
    return $this->pageNow;
    }
}
?>
 
//Tablet
<?php
class Tablet implements IDevice
{
    private $page;
    private $header;
    private $graphic;
    private $bodyText;
 
    public $pageNow;
    function buildDevice($head,$image,$text)
    {
        $this->header=$head;
        $this->graphic=$image;
        $this->bodyText=$text;
        //Begin heredoc string
        $this->pageNow=<<<TABLET
        <!DOCTYPE html>
        <html>
        <head>
        <link rel="stylesheet" type="text/css" href="bridge.css">
            <title>Tablet Bridge</title>
        </head>
        <body>
        <header><h2>$this->header</h2></header>
        <img src="tabletpix/$this->graphic" alt="Air" width="600" height="375">
        <p>$this->bodyText</p>
        </body>
        </html>
TABLET;
    return $this->pageNow;
    }
}
?>
 
//Phone
<?php
class Phone implements IDevice
{
    private $page;
    private $header;
    private $graphic;
    private $bodyText;
 
    public $pageNow;
    function buildDevice($head,$image,$text)
    {
        $this->header=$head;
        $this->graphic=$image;
        $this->bodyText=$text;
        //Begin heredoc string
        $this->pageNow=<<<PHONE
        <html>
        <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Phone Bridge</title>
        <link rel="stylesheet" href="themes/Bridge.min.css" />
        <link rel="stylesheet" href="themes/jquery.mobile.icons.min.css" />
        <link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.3/jquery.mobile.structure-1.4.3.min.css" />
        <script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
        <script src="http://code.jquery.com/mobile/1.4.3/jquery.mobile-1.4.3.min.js"></script>
        </head>
        <body>
        <div data-role="page" data-theme="a">
          <div data-role="header" data-position="inline" data-theme="a">
            <h2>$this->header</h2>
          </div>
          <div role="main" class="ui-content" data-theme="a"> <img src="phonepix/$this->graphic" alt="Air" width="300" height="188">
            <p>$this->bodyText</p>
          </div>
        </div>
        </body>
        </html>
PHONE;
    return $this->pageNow;
    }
}
?>

Figure 3: Phone implementation

Figure 3: Phone implementation

The Desktop and Tablet implementations of IDevice are almost identical, but the Table uses a slightly smaller graphic. However, in looking at the Phone implementation, a whole slew of jQuery links are included, and it looks slightly different than the page for both the Desktop and Table implementations. Figure 3 shows what is configured in a smartphone (iPhone pictured) using the Phone class and Figure 4, a Mini iPad using the Tablet class.
Figure 4: Table implementation

Figure 4: Table implementation

Here Comes the Client

In this particular implementation of a PHP Bridge pattern, I wanted to give the client as little to do as possible. The idea is to focus on the Bridge structure with the client simply making a request. However, the client’s request is largely usurped by the StandardPage’s method, setPage(). As noted above, if you want, you can get rid of the setPage() method and add it’s properties as parameters in the doDevice() method. If you make those changes, the BridgeClient will need to pass values to the bridge component for processing. That should be fairly trivial. (An even better idea would be to add input to the Bridge.html document that would be processed by the client to add more flexibility for what appears on the Web pages.)

<?php
error_reporting(E_ALL | E_STRICT);
ini_set("display_errors", 1);
function __autoload($class_name) 
{
    include $class_name . '.php';
}
class BridgeClient
{
    private static $pageDevice;
    private static $device;
    //client request
    public static function request()
    {
      self::$device=$_POST['device'];
      self::$pageDevice=new StandardPage();
      self::$pageDevice->doDevice(new self::$device());
    } 
}
BridgeClient::request();
?>

In case you were wondering what the faded gray box with “Client” and “BridgeClient” is for in Figure 2, we have to go to the appendix of the GoF book (pp 364-365)—it indicates that the client is not a participant in the class but is useful to show which participants interact with client. In this case the client is a collaborator, and it interacts with the abstraction which forwards the client’s request to the implementor object.

The significant point here is that the client interacts with an abstract object, both the interface and the refined abstraction of the interface. Intuitively, we might assume it interacts directly with the implementor object, but the little gray box shows us otherwise. That’s why these diagrams can be so useful even though at times I think that a mad (computer) scientist concocted them to thwart understanding.

Share

Copyright © 2014 William Sanders. All Rights Reserved.

0 Responses to “PHP Bridge Design Pattern”


  • No Comments

Leave a Reply