PHP Decorator Design Pattern: Accessorizing Your Classes

decorator design pattern

Decorating an Object

Adding Just the Flourishes You Need

How would your like your concrete classes to be like an unadorned Christmas tree? When you need an ornament, you put it on. You can put on several of the same type, all different types and when they’re no longer needed, you can take them off. Your central object (class) is unchanged and you’re not processing stuff you’re not using. When you need it; you just pop it on like an ornament on a tree. Further, you can decorate different components with the same ornaments.

When would you use such a pattern? Consider setting up an order form. Each order is an object, and you decorate your order with other objects the user wants to buy. When I buy a computer, I accessorize it with with added memory, a Webcam, a USB hub and anything else that I think I need. However, the store doesn’t have to have a separate computer for every possible combination that users may want to buy. They can just have a few and let the user decorate them anyway she wants. Further, the object you want does not have to drag every option with it—just the ones the buyer wants. You can play the test case and download all of the files using the following two buttons:

The Structure

To kick things off, let’s take a look at the class diagram that the Gang of Four devised. It is one of the most interesting because an abstract class is subclassed from another abstract class (among other things) as can be seen in Figure 1:

Figure 1:Decorator Class Diagram

In looking at what varies, we find that the variation is responsibilities of an object without sub-classing—in other words, use delegation. However, you can see that both the Decorator class and the Concrete Components and Decorators are all sub-classed from Abstract classes. Isn’t that inheritance instead of composition? Of all of the things that can vary in a program structure, those assigned to the Decorator still need further clarification. As you will see in the next section, the Decorator seems to even contradict its own element of variation because it double-subclasses. However, once everything is straightened out, you will find the Decorator example we use to be very simple to implement and even use in a real live work situation.

An Abstract Class as a Subclass

One of the most unusual elements of the Decorator pattern is the fact that the abstract class, Decorator, is a subclass of the base abstract class, Component. The concrete component classes derived directly from the Component class act in a very similar way as the concrete decorator classes. This is because they have an almost identical interface. However, the concrete decorator classes wrap a Component reference inherited from the Decorator.

Developers have employed different ways to implement the Decorator design pattern, but I prefer to use abstract classes for both the component and decorator.The wrapper is the abstract Decorator class. First, in our design all of the decorators wrap concrete components using the IComponent Type Hinting. This helps establish the correct interface. You will see:

private  $computer;
public function __construct(IComponent $computer) 
{
	$this->computer = $computer;
}

That’s possible because the abstract Decorator class extends the abstract component class named IComponent. Even though the object is instantiated as a concrete Component, the same interface is inherited in the concrete decorators because their parent class (Decorator) inherits the interface of the IComponent abstract class.

Gamma, Helm, Johnson and Vlissides point out,

The decorator conforms to the interface of the component it decorates so that its presence is transparent to the component’s clients. (p. 175)

That sounds something like recursion—that’s because it is recursion. The purpose, though, is to add the decorator’s features to the component without changing the component. The decorations occur at run-time giving the design a good deal of flexibility through delegation and loose coupling.

Note: I use the name IComponent for the name of the abstract class. That’s because it reminds us that the class is abstract and all we want from it is its interface. I could have used an Interface, especially since I used abstract methods; however, I like the idea of using an abstract class because they afford a bit more flexibility. A common convention is to name interfaces with the initial capital “I” but the same practice has been applied to abstract classes as well.

Second, the design suggests that the concrete decorators be instantiated by wrapping the component instance. That would mean that the constructors would have to override the parent class constructor function. Well, PHP does not have override statements, but with an abstract class, there’s nothing to override. I don’t suppose there’s anything wrong with that, but I saw nothing to be gained from it. In their work, the Freemans (Head First Design Patterns) similarly did not include the wrapper in the abstract Decorator, even though Java does have an abstract class modifier. In any event, the decorator wraps the component and that allows the component and decorator elements to share much of the design’s structure.

Using the Decorator in an Online Computer Store

To demonstrate how to use the Decorator design pattern, we’ll start off with a simple set of classes that represent components to be decorated (computer) and their decorators (accessories.) The choices of computers will be limited to a Dell and an Apple Macintosh, and they will share the same set of decorators: a terabyte of added hard drive, a USB hub, and a big monitor.

Abstract Classes

The abstract classes are made up of IComponent and Decorator. The Decorator wraps the IComponent with an aggregation relationship. In order for this kind of wrapping, the two must share the same interface, and so the component classes and decorator classes too share the same interfaces albeit with different implementations. The Decorator extends the IComponent, and so all of the Decorator child classes (concrete decorators) do as well. So first, we have the IComponent abstract class with two methods. (An interface may have served as well as an abstract class, but I was interested to see if we could build a PHP Decorator example using abstract classes since the original example by GoF used them for the component and decorator interface. Also, I had not seen other PHP examples of a Decorator that used abstract classes.)

<?php
abstract class IComponent
{
	abstract public function getComputer();
	abstract public function price();
}
?>

Because the Decorator acts as a wrapper for the IComponent it conforms to the IComponent’s interface, making the decoration transparent. As you can see, there’s not much we need to do with the Decorator other than extend the IComponent. The getDescription() method represents the potential of other methods. (If you comment it out, it won’t affect the design.)

<?php
abstract class Decorator extends IComponent
{
	public function getDescription() { }
}
?>

The Concrete Components: Wrap Me Up!

The concrete components are the “wrappees” (elements that get wrapped) and as such they don’t have to do much. In this example, they have a description and a price. The description differentiates them from other objects (computers) and price returns what they’ll set you back.

///Concrete Components
<?php
class Dell extends IComponent 
{
	private $description;
	public function __construct() 
	{
		$this->description="Dell Computer: ";
	}
 
	public function getDescription()
	{
		return $this->description;
	}
	public function price() 
	{
		return 599;
	}
}
?>
///Concrete Component
<?php
class Apple extends IComponent
{
	private $computer;
	public function __construct()
	{
		$this->computer="Apple Macintosh: ";
	}
 
	public function getComputer()
	{
		return $this->computer;
	}
	public function price()
	{
		return 799;
	}
}
?>

As you can see we have two concrete components: one for an Apple and one for a Dell computer. (You HP folks can relax; it’s easy to add concrete components.)

The Concrete Decorators: Boa-Constructors

The actual wrappers are the concrete decorators. Like the concrete components they have a description and a price. However, they also wrap a component and they return not only their own price but also the value of everything that they wrap. Concrete decorators wrap concrete component instances in their constructors. (Think of “boa-constructors” wrapping components!)

An important feature of the concrete decorators is that they include an IComponent as type hinting. This is truly programming to the interface. Since the decorators are also IComponent types, when a wrapped component is wrapped in a decorator, there’s no conflict. They have the same interface. (Please note that the price() method has both a “+” and a “.” operator. The former is for adding the value of the decorator’s value and the component’s value, and the latter is for concatenation.

///Concrete Decorators
 
///Terabyte Decorator
<?php
class Terabyte extends Decorator 
{
	private  $computer;
	public function __construct(IComponent $computer) 
	{
		$this->computer = $computer;
	}
	public function getComputer() 
	{
		return $this->computer->getComputer() . "<br/>&nbsp;&nbsp; Terabyte ";
	}
	public function price() 
	{
		return 129 + $this->computer->price();
	}
}
?>
///Hub Decorator
<?php
class Hub extends IComponent 
{
	private  $computer;
	public function __construct(IComponent $computer) 
	{
		$this->computer = $computer;
	}
	public function getComputer() 
	{
		return $this->computer->getComputer() . "<br/>&nbsp;&nbsp; USB Hub ";
	}
	public function price() 
	{
		return 82 + $this->computer->price();
	}
}
?>
 
///Big Monitor Decorator
<?php
class BigMonitor extends IComponent 
{
	private  $computer;
	public function __construct(IComponent $computer) 
	{
		$this->computer = $computer;
	}
	public function getComputer() 
	{
		return $this->computer->getComputer() . "<br/>&nbsp;&nbsp; Big Monitor ";
	}
	public function price() 
	{
		return 325 + $this->computer->price();
	}
}
?>

The best features of the concrete decorators is the ability to change the price or description without disrupting the rest of the program. Adding more concrete decorations is just as easy—all without having to re-write large portions of the program.

The Client

The client class makes its request by first determining which component the user has selected—an “apple” or “dell.” Then, if an accessory has been requested, it then wraps the component in one or more decorators. At most, the Client deals with a single component and three decorators. Each value is added to the price—the component’s base price and the each decorator’s price as it is added.

<?php
ini_set("display_errors","2");
ERROR_REPORTING(E_ALL);
include_once('IComponent.php');
include_once('Decorator.php');
include_once('Dell.php');
include_once('Apple.php');
include_once('Terabyte.php');
include_once('BigMonitor.php');
include_once('Hub.php');
 
class Client
{
	private $computerNow;
	private $accessory;
	private $n;
 
	public function __construct()
	{	
		$computer=$_POST['query'];
 
		if($computer=="apple")
		{
			$this->computerNow = new Apple();
		}
		else
		{
			$this->computerNow = new Dell();
		}
 
	if(!empty($_POST['accessories']))
		{
			$this->accessory=$_POST['accessories'];
			$this->n=count($this->accessory);
			$this->accessorize();
		}
 
		print $this->computerNow->getComputer() . "<br/>&nbsp;&nbsp;<strong>Total= $" . $this->computerNow->price() . "</strong><p/>";	
	}
 
	private function accessorize()
	{
		for($a=0; $a < $this->n; $a++)
		{
			switch($this->accessory[$a])
			{
				case "bigmonitor":
					$this->computerNow =new BigMonitor($this->computerNow);
					break;
				case "terabyte":
					$this->computerNow =new Terabyte($this->computerNow);
					break;
				case "hub":
					$this->computerNow =new Hub($this->computerNow);					
			}
		} 
	}
}
$goClient=new Client()
?>

The operations within the private function to add the accessories could have been done within the constructor function. Placing it off on its own seemed like one way to distinguish the decorators from the components and perhaps easier to have update with added accessories.

The User Interface

The UI is an HTML program that calls the Client (php) class and passes what the user enters in radio buttons and check boxes. This one is very simple and does not even have such elemental validation. First, nothing beyond the basics (the structure) of the Decorator design pattern is revealed making it easier to see how to use the Decorator. Second, you can easily fix it up to your own style and taste—after all, it’s just HTML.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<style type="text/css">
body
	{
		font-family:Verdana, Arial;
		color:#151919;
		background-color:#E5B744;
		font-size;11pt;
	}
h1	
	{
		color:#733218;
	}
h2
	{
		color:#8C7C40;
	}
 
</style>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Choose Computer</title>
</head>
<body>
<h1>Decorator Computer Store</h1>
<h2>Select a Compuer:</h2>
<form action="Client.php" method="post">
<input type="radio" name="query" value="dell" />
Dell Computer<br/>
<input type="radio" name="query" value="apple" />
Apple Macintosh<p/>
 
<h2>Select Components</h2>
 
<input type="checkbox" name=accessories[] value="bigmonitor" />
Big Monitor<br/>
<input type="checkbox" name=accessories[] value="terabyte" />
Terabyte External Storage<br/>
<input type="checkbox" name=accessories[] value="hub" />
USB Hub<p/>
<input type="submit" name="sendNow" value="Place Order" />
</form>
</body>
</html>

If you add new components or concrete decorations, all you need to do is add the corresponding input tags.

Putting the Decorator to Work

This example of the Decorator is best used in terms making changes. Adding decorators is easy, and they are only used when needed. Likewise, adding components is simply a matter of creating a new concrete component class with the base description and price.

You may be thinking that this looks just like the Strategy pattern. However, there are important differences. The decorator only changes the component from the outside. Thus, the component doesn’t have to know anything about the decorator. However, in the Strategy pattern the component knows about the possible extensions (algorithms) and must maintain a reference to them.

You can explore the Decorator from several different perspectives and try it out with applications where you need to have different component features without having to subclass them. I think that you can appreciate the flexibility of the Decorator most when it comes time to add new features to fundamental components without the need to change them.

Share

No related posts.

Related posts brought to you by Yet Another Related Posts Plugin.

3 Responses to “PHP Decorator Design Pattern: Accessorizing Your Classes”


  • Hi,
    I’ve enjoyed reading your article. Please forgive me if I’m wrong, I’m not an experienced programmer but wouldn’t the line in the client constructor: print $this->computerNow->getComputer() return null for a new Dell() object?
    Shane

  • Looks like maybe a bad copy paste on the concrete component classes. It would seem that the first example is setting the description member variable correctly but fails to implement IComponent by not having a getComputer method. It does have a getDescription method.

    The second example has a correct getComputer method but is setting the computer member variable to what should be it’s description.

  • And by the way, awesome blog.

Leave a Reply