您的位置 首页 php

14 PHP 设计模式系列「装饰器模式(Decorator)」

1、模式定义

装饰器模式能够从一个对象的外部动态地给对象添加功能。

通常给对象添加功能,要么直接修改对象添加相应的功能,要么派生对应的子类来扩展,抑或是使用对象组合的方式。显然,直接修改对应的类这种方式并不可取。在面向对象的设计中,我们也应该尽量使用对象组合,而不是对象继承来扩展和复用功能。装饰器模式就是基于对象组合的方式,可以很灵活的给对象添加所需要的功能。装饰器模式的本质就是动态组合。动态是手段,组合才是目的。

常见的使用示例:Web服务层 —— 为 REST 服务提供 JSON 和 XML 装饰器。

2、UML类图

3、示例代码

RendererInterface.php

<?php
namespace DesignPatterns\Structural\Decorator;
/**
 * RendererInterface接口
 */interface RendererInterface
{
 /**
 * render data
 *
 * @return mixed
 */ public function renderData();
}
 

Webservice.php

<?php
namespace DesignPatterns\Structural\Decorator;
/**
 * Webservice类
 */class Web service  implements RendererInterface
{
 /**
 * @var mixed
 */  protected  $data;
 /**
 * @param mixed $data
 */ public function __construct($data)
 {
 $this->data = $data;
 }
 /**
 * @return string
 */ public function renderData()
 {
 return $this->data;
 }
}
 

Decorator.php

<?php
namespace DesignPatterns\Structural\Decorator;
/**
 * 装饰器必须实现 RendererInterface 接口, 这是装饰器模式的主要特点,
 * 否则的话就不是装饰器而只是个包裹类
 *//**
 * Decorator类
 */abstract class Decorator implements RendererInterface
{
 /**
 * @var RendererInterface
 */ protected $wrapped;
 /**
 * 必须类型声明装饰组件以便在子类中可以调用renderData()方法
 *
 * @param RendererInterface $wrappable
 */ public function __construct(RendererInterface $wrappable)
 {
 $this->wrapped = $wrappable;
 }
}
 

RenderInXml.php

<?php
namespace DesignPatterns\Structural\Decorator;
/**
 * RenderInXml类
 */class RenderInXml extends Decorator
{
 /**
 * render data as XML
 *
 * @return mixed|string
 */ public function renderData()
 {
 $ output  = $this->wrapped->renderData();
 // do some fancy conversion to xml from array ...
 $doc = new \DOMDocument();
  foreach  ($output as $key => $val) {
 $doc->appendChild($doc->createElement($key, $val));
 }
 return $doc->saveXML();
 }
}
 

RenderInJson.php

<?php
namespace DesignPatterns\Structural\Decorator;
/**
 * RenderInJson类
 */class RenderInJson extends Decorator
{
 /**
 * render data as JSON
 *
 * @return mixed|string
 */ public function renderData()
 {
 $output = $this->wrapped->renderData();
 return json_encode($output);
 }
}
 

4、测试代码

Tests/DecoratorTest.php

<?php
namespace DesignPatterns\Structural\Decorator\Tests;
use DesignPatterns\Structural\Decorator;
/**
 * DecoratorTest 用于测试装饰器模式
 */class DecoratorTest extends \PHPUnit_Framework_TestCase
{
 protected $service;
 protected function setUp()
 {
 $this->service = new Decorator\Webservice(array('foo' => 'bar'));
 }
 public function testJsonDecorator()
 {
 // Wrap service with a JSON decorator for renderers
 $service = new Decorator\RenderInJson($this->service);
 // Our Renderer will now output JSON instead of an array
 $this->assertEquals('{"foo":"bar"}', $service->renderData());
 }
 public function testXmlDecorator()
 {
 // Wrap service with a XML decorator for renderers
 $service = new Decorator\RenderInXml($this->service);
 // Our Renderer will now output XML instead of an array
 $xml = '<?xml version="1.0"?><foo>bar</foo>';
 $this->assertXmlStringEqualsXmlString($xml, $service->renderData());
 }
 /**
 * The first key-point of this pattern :
 */ public function testDecoratorMustImplementsRenderer()
 {
 $className = 'DesignPatterns\Structural\Decorator\Decorator';
 $interfaceName = 'DesignPatterns\Structural\Decorator\RendererInterface';
 $this->assertTrue(is_subclass_of($className, $interfaceName));
 }
 /**
 * Second key-point of this pattern : the decorator is type-hinted
 *
 * @expectedException \PHPUnit_Framework_Error
 */ public function testDecoratorTypeHinted()
 {
 if (version_compare(PHP_VERSION, '7', '>=')) {
 throw new \PHPUnit_Framework_Error('Skip test for PHP 7', 0, __FILE__, __LINE__);
 }
 $this->getMockForAbstractClass('DesignPatterns\Structural\Decorator\Decorator', array(new \stdClass()));
 }
 /**
 * Second key-point of this pattern : the decorator is type-hinted
 *
 * @requires PHP 7
 * @expectedException TypeError
 */ public function testDecoratorTypeHintedForPhp7()
 {
 $this->getMockForAbstractClass('DesignPatterns\Structural\Decorator\Decorator', array(new \stdClass()));
 }
 /**
 * The decorator implements and wraps the same interface
 */ public function testDecoratorOnlyAcceptRenderer()
 {
 $mock = $this->getMock('DesignPatterns\Structural\Decorator\RendererInterface');
 $dec = $this->getMockForAbstractClass('DesignPatterns\Structural\Decorator\Decorator', array($mock));
 $this->assertNotNull($dec);
 }
}
 

文章来源:智云一二三科技

文章标题:14 PHP 设计模式系列「装饰器模式(Decorator)」

文章地址:https://www.zhihuclub.com/150633.shtml

关于作者: 智云科技

热门文章

网站地图