Posted on 22-01-2010
Filed Under (PHP) by Romi

After discussing creational patterns, which handle object creation throughout the application, it’s time to take a look at how objects are composed in order to create flexible larger structures. Structural patterns deal with the way objects connect with each other to form new objects, or modify object capabilities and they offer flexible solutions to common architectural issues when it comes to application design.

The first pattern we’ll be looking at is called Decorator (aka Wrapper) and it’s used mainly for adding features to individual objects, as opposed to an entire class of objects since this approach isn’t considered to be flexible. The classical decorator example describes a situation when trying to add a border and scroll bars to a text window, which is a very intuitive one, so I’ll start from there. Generally speaking, a text container has variable dimensions since it can have content of variable lengths. A border decorator adds a width and height to it, plus the scroll bars, should the content not fit. A border decorator adds a frame to an element. Traditionally, the way to accomplish this was to instantiate a decorator object by calling another decorator which finally calls the object to be decorated, so you would end up with a series of nested objects when creating a decorated element, and later when rendering it. Here is what that code would have looked like:

$window = new WindowBorderDecorator(
    new WindowScrollbarDecorator(
        new WindowDecorator(
            new Window()
        )
    )
);

This creates the problem of dynamically removing a decorator, because if we remove one, all nested items will be removed, and in turn, the main item (in this case the window) will be removed also. A more recent approach to managing decorators involves adding a list, for storing decorators, to the class which needs them. This approach removes the problem mentioned above, and allows adding and removing decorators dynamically at run-time. As a trade-off, this requires the output generated by the object’s render function to be passed through all decorators and finally to return the modified (or “decorated”) content. Not really a trade-off…but still. Let’s consider another simple example: adding decorators to input elements. And we’ll have two types of decorators, Border and Color (background). The first one adds a border with a certain color and thickness, and the second one adds a background color to the element. Our element class will look a bit different from the last time, since it needs to be able to support decorators, but first we need to see what its interface looks like:

interface IElement
{
    public function renderElement();
}

Each element needs to be rendered, so the interface consists of only one function. Next we want to make this item able to support decorators. We’ll have to define another interface for specifying that. Since I didn’t have any other idea on how to name it, i called it IDecorable:

interface IDecorable
{
    public function addDecorator( IDecorator &$decorator );
    public function removeDecorator( IDecorator &$decorator );
}

The interface for an element which supports decorators consists of just two methods for adding and removing decorators dynamically. They will come in handy later on, but now it’s time to see what the Decorator class looks like.

interface IDecorator
{
    public function render( $data );
}

Just a simple function for wrapping the data and then render it. $data is the content passed to a decorator, which could be the HTML code of the element, or the result after applying other decorators on the element beforehand. I added an AbstractDecorator class in order to store all common properties of a decorator, in this case, each of them has a padding, so we can see them better, but it’s a good idea to have an abstract class in order to define only the differing details of each concrete decorator class:

class AbstractDecorator implements IDecorator
{
    protected $_padding = 15;
 
    public function render( $data ) {}
}

We define the empty method render here, since the abstract decorator implements IDecorator, even though the concrete sub-classes will define it later. I added a $_padding variable in order to allow all subclasses to inherit it. Now we see how the elements are connecting to decorators, we have to add a concrete one, in order to add a decorator to an element. Let’s discuss the BorderDecorator. Basically, all it does, is to enclose an element in a div tag so we can add some CSS properties to it, like border obviously. Besides the padding property, it also has a $_borderWidth property so we can vary the border thickness of the decorator, we could have added a color variable, but for now it’s enough:

class BorderDecorator extends AbstractDecorator
{
    private $_borderWidth = 2;
 
    public function __construct( $borderWidth = NULL, $padding = NULL )
    {
        if( isset( $borderWidth ) )
            $this->_borderWidth = $borderWidth;
        if( isset( $padding ) )
            $this->_padding    = $padding;
    }
 
    public function render( $data )
    {
        return
        '<div style="float:left; border:'.$this->_borderWidth.'px solid #0066ff; padding:'.$this->_padding.'px;">'.
        "\r\n".$data.
        '</div>'."\r\n";
    }
}

You can see how the render method wraps the div around the passed content. As another example, we can consider the ColorDecorator class which acts like the BorderDecorator, except it adds a different CSS property to the wrapping div. You should start to see how the decorators nest data to compose the final object, if not it will all be clear when we look at the concrete element class, and specifically its render method:

class ConcreteElement implements IElement,IDecorable
{
    private $_decorators;
 
    public function __construct( $type, $name, $value = "" )
    {
        $this->_type    = $type;
        $this->_name    = $name;
        $this->_value    = $value;
        $this->_decorators = array();
    }
 
    public function renderElement()
    {
        $result = '<input type="'.$this->_type.'" name="'.$this->_name.'" value="'.$this->_value.'" />';
        foreach( $this->_decorators as $decorator )
            $result = $decorator->render( $result );
        echo $result;
    }
 
    public function addDecorator(IDecorator &$decorator)
    {
        array_push( $this->_decorators, $decorator );
        unset( $decorator );
        return $this;
    }
 
    public function removeDecorator(IDecorator &$decorator)
    {
        foreach( $this->_decorators as $key => $dec )
        if( $dec == $decorator )
        {
            unset( $this->_decorators[$key] );
            break;
        }
        unset( $decorator );
        return $this;
    }
}

Here we actually define the render method and decorator operations of an element. We can now add and remove decorators to and from any ConcreteElement object by pushing them in the $_decorators list, or by removing them from that same list. The only thing left is to create an object and add some decorators to it. Since we return $this in the addDecorator() and removeDecorator() methods we can chain them, so we can add and remove decorators on one line:

$element = new ConcreteElement("text", "myInput", "Default");
$element->addDecorator( new BorderDecorator( 5 ) )
               ->addDecorator( new ColorDecorator( "#aaffdd" ) )
               ->addDecorator( new BorderDecorator( 3 ) )
               ->addDecorator( new ColorDecorator( "#aaeeff", 20 ) );

Since we add a decorator by passing a newly created element, we need to unset it. Now we just call $element->renderElement() where it is suited in the application, and we can watch the magic unfold. You can find here the application for this post, and here a link to download the source.

(0) Comments    Read More   
Post a Comment
Name:
Email:
Website:
Comments: