From b8381e5a60c195694afdfed28cb39f381459906c Mon Sep 17 00:00:00 2001 From: Matt Iversen Date: Wed, 8 May 2013 21:30:09 +1000 Subject: [PATCH] Proper example of Decorator pattern Previous example was simply implementing a strategy pattern. A key problem is that WebService made clear that it did no actual output of its own - it handed it off to 'decorators'. A decorator should always wrap a class that already has its own complete, self-contained functionality. In this way, they only enhance / modify it. An instance that is non-decorated should work fully by itself. Note the distinguishing difference between this and sub-classing - here the enhancement is added dynamically, at runtime, and to one instance. With sub-classing, the enhancement is added at compile / parse time, and is applied to all instances / uses of the subclass. --- Decorator/Decorator.php | 76 ++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 43 deletions(-) diff --git a/Decorator/Decorator.php b/Decorator/Decorator.php index 1c3d098..856b181 100644 --- a/Decorator/Decorator.php +++ b/Decorator/Decorator.php @@ -14,71 +14,61 @@ namespace DesignPatterns; * course) * */ -class Webservice + +interface Renderer +{ + public function renderData(); +} + +class Webservice implements Renderer { protected $_data; - /** - * an array to hold all added decorators, often there would be defaults set in this - * array, e.g. the service could be defaulted to use JSON and only for special APIs - * use XML - * - * @var array - */ - protected $_decorators = array(); - public function __construct($data) { $this->_data = $data; } - /** - * - * - * @param WebserviceDecorator $decorator - * @return void - */ - public function addDecorator(WebserviceDecorator $decorator) - { - $this->_decorators[] = $decorator; - } - - /** - * @return string - */ public function renderData() { - $response = ''; - foreach ($this->_decorators as $decorator) { - $response = $decorator->renderData($this->_data); - } - - return $response; + return $this->_data; } } -interface WebserviceDecorator +abstract class Decorator { - public function renderData($data); -} - -class JsonDecorator implements WebserviceDecorator -{ - public function renderData($data) + protected $_wrapped; + + public function __construct($wrappable) { - return json_encode($data); + $this->_wrapped = $wrappable; } } -class XmlDecorator implements WebserviceDecorator +class RenderInJson extends Decorator implements Renderer { - public function renderData($data) + public function renderData() { - // do some fancy conversion to xml from array ... - return simplexml_load_string($data); + $output = $this->_wrapped->renderData(); + return json_encode($output); } } +class RenderInXml extends Decorator implements Renderer +{ + public function renderData() + { + $output = $this->_wrapped->renderData(); + // do some fany conversion to xml from array ... + return simplexml_load_string($output); + } +} + +// Create a normal service $service = new Webservice(array('foo' => 'bar')); -$service->addDecorator(new JsonDecorator()); + +// Wrap service with a JSON decorator for renderers +$service = new RenderInJson($service); +// Our Renderer will now output JSON instead of an array + echo $service->renderData();