While the basic premise behind this article is still right, PHP 5.4 introduced Traits, which are suitable for implementing this design pattern. I recommend using them instead.
There is a strong case for using design patterns in PHP applications. They will help you create code that is readable, easy to maintain and flexible to expand upon.
In programming, design patterns are essentially clever ways of solving common problems. By using these standardized and well documented ways of solving problems, other developers will have an easier time reading your code.
Say that you are making a website for a newspaper. This newspaper has, like newspapers usually do, a number of articles displayed on their site. You represent them with as objects of class Article. Each article has certain methods, like getDate(), getAuthor(), getTitle().
Over time though, things may get complicated. Your editor may decide to collect information about all politicians in local elections, and display their biographical information next to the articles realted to that person. At another point, you will need to show information about a football team mentioned in the article.
One way to solve this is to add methods to your class Article, such as getRelatedPolitican() or getFootballTeam(). Overtime, your Articles will grow bigger and bigger: the class will be harder to maintain and objects will have more unnecessary overhead.
Enter decorator pattern. This pattern allows you to extend functionality of objects without modifying their class. The idea is to, when needed, pass a new object to the object which you want to extend. So, instead of implementing getRelatedPolitican() and getFootballTeam() in the Article class, you create two decorators, let’s call them class DecoratorPolitican and class DecoratorTeam and pass them to your Article object for additional functionality.
Let’s see this in action. First of all, I will define a class which represents an article:
decorators[] = 'DecoratorPolitician'; } public function getDate() { return "11/2/2010"; } public function getAuthor() { return "Article Author"; } public function getTitle() { return "Article Title"; } public function __call($method, $args) { foreach ($this->decorators as $decorator) { if (is_callable(array($decorator, $method))) { $dec_object = new $decorator($this); return call_user_func(array($dec_object, $method)); } } } } ?>
Now, let me create a class for a decorator:
article = $article; } public function getPoliticianName() { return $this->getFirstName() . ' ' . $this->getLastName(); } private function getFirstName() { return "Foo"; } private function getLastName() { return "Bar"; } } ?>
Let’s go step by step through class Article:
public function __construct() { $this->decorators[] = 'DecoratorPolitician'; }
In the class constructor we added a decorator to the array holding all possible decorators for Article objects. These are only strings, and the objects will getcreated on demand.
The class has several public methods, like getAuthor(), getDate() and getTitle() – these will always be used, so it’s smart to put them in the Article class itself.
Now, the interesting bit, which separates this decorator implemention from others on the web and makes it PHP specific:
public function __call($method, $args) { foreach ($this->decorators as $decorator) { if (is_callable(array($decorator, $method))) { $dec_object = new $decorator($this); return call_user_func(array($dec_object, $method)); } } }
Magic method __call() will be invoked whenever an unaccessible method is called. So, if you create a new object, trying to access its public methods, there will be no change. But if you try to access methods that it doesn’t have, __call() will be invoked. We’ll use this to our advantage: if any of the decorators has the required method, we will invoke it instead.
Class DecoratorPolitician is fairly simple:
public function __construct(Article $article) { $this->article = $article; }
It takes an object of the type Article as a parameter. This bounds the specific article to it’s decorator. This is very useful in real world scenarios: you’ll need to access properties of the Article object. E.g. to find a related politician, you would need to have article’s ID, category, or some other information.
Method getPoliticianName() is used to actually return the result to Article. All other methods there are for show: if you have some complex computing to do, it will be well isolated within that one decorator.
Pretty neat, isn’t it? Give it a try:
$article = new Article(); echo $article->getTitle() . PHP_EOL; echo $article->getDate() . PHP_EOL; echo $article->getPoliticianName() . PHP_EOL;
This will have an output of:
New article created
Article Title
11/2/2010
New decorator created
Foo Bar