装饰模式(Decorator)

目的

为类实例动态增加新的方法。

例子

  • Zend Framework: Zend_Form_Element 实例的装饰者
  • Web Service Layer: 用于 REST 服务的 JSON 和 XML 装饰者 (当然,在这个例子中理应只有一个是被允许的)

UML 图

file

代码

你也可以在 GitHub 上查看代码

RenderableInterface.php

  1. <?php
  2. namespace DesignPatterns\Structural\Decorator;
  3. /**
  4. * 创建渲染接口。
  5. * 这里的装饰方法 renderData() 返回的是字符串格式数据。
  6. */
  7. interface RenderableInterface
  8. {
  9. public function renderData(): string;
  10. }

Webservice.php

  1. <?php
  2. namespace DesignPatterns\Structural\Decorator;
  3. /**
  4. * 创建 Webservice 服务类实现 RenderableInterface。
  5. * 该类将在后面为装饰者实现数据的输入。
  6. */
  7. class Webservice implements RenderableInterface
  8. {
  9. /**
  10. * @var string
  11. */
  12. private $data;
  13. /**
  14. * 传入字符串格式数据。
  15. */
  16. public function __construct(string $data)
  17. {
  18. $this->data = $data;
  19. }
  20. /**
  21. * 实现 RenderableInterface 渲染接口中的 renderData() 方法。
  22. * 返回传入的数据。
  23. */
  24. public function renderData(): string
  25. {
  26. return $this->data;
  27. }
  28. }

RendererDecorator.php

  1. <?php
  2. namespace DesignPatterns\Structural\Decorator;
  3. /**
  4. * 装饰者必须实现渲染接口类 RenderableInterface 契约,这是该设计
  5. * 模式的关键点。否则,这将不是一个装饰者而只是一个自欺欺人的包
  6. * 装。
  7. *
  8. * 创建抽象类 RendererDecorator (渲染器装饰者)实现渲染接口。
  9. */
  10. abstract class RendererDecorator implements RenderableInterface
  11. {
  12. /**
  13. * @var RenderableInterface
  14. * 定义渲染接口变量。
  15. */
  16. protected $wrapped;
  17. /**
  18. * @param RenderableInterface $renderer
  19. * 传入渲染接口类对象 $renderer。
  20. */
  21. public function __construct(RenderableInterface $renderer)
  22. {
  23. $this->wrapped = $renderer;
  24. }
  25. }

XmlRenderer.php

  1. <?php
  2. namespace DesignPatterns\Structural\Decorator;
  3. /**
  4. * 创建 Xml 修饰者并继承抽象类 RendererDecorator 。
  5. */
  6. class XmlRenderer extends RendererDecorator
  7. {
  8. /**
  9. * 对传入的渲染接口对象进行处理,生成 DOM 数据文件。
  10. */
  11. public function renderData(): string
  12. {
  13. $doc = new \DOMDocument();
  14. $data = $this->wrapped->renderData();
  15. $doc->appendChild($doc->createElement('content', $data));
  16. return $doc->saveXML();
  17. }
  18. }

JsonRenderer.php

  1. <?php
  2. namespace DesignPatterns\Structural\Decorator;
  3. /**
  4. * 创建 Json 修饰者并继承抽象类 RendererDecorator 。
  5. */
  6. class JsonRenderer extends RendererDecorator
  7. {
  8. /**
  9. * 对传入的渲染接口对象进行处理,生成 JSON 数据。
  10. */
  11. public function renderData(): string
  12. {
  13. return json_encode($this->wrapped->renderData());
  14. }
  15. }

测试

Tests/DecoratorTest.php

  1. <?php
  2. namespace DesignPatterns\Structural\Decorator\Tests;
  3. use DesignPatterns\Structural\Decorator;
  4. use PHPUnit\Framework\TestCase;
  5. /**
  6. * 创建自动化测试单元 DecoratorTest 。
  7. */
  8. class DecoratorTest extends TestCase
  9. {
  10. /**
  11. * @var Decorator\Webservice
  12. */
  13. private $service;
  14. /**
  15. * 传入字符串 'foobar' 。
  16. */
  17. protected function setUp()
  18. {
  19. $this->service = new Decorator\Webservice('foobar');
  20. }
  21. /**
  22. * 测试 JSON 装饰者。
  23. * 这里的 assertEquals 是为了判断返回的结果是否符合预期。
  24. */
  25. public function testJsonDecorator()
  26. {
  27. $service = new Decorator\JsonRenderer($this->service);
  28. $this->assertEquals('"foobar"', $service->renderData());
  29. }
  30. /**
  31. * 测试 Xml 装饰者。
  32. */
  33. public function testXmlDecorator()
  34. {
  35. $service = new Decorator\XmlRenderer($this->service);
  36. $this->assertXmlStringEqualsXmlString('<?xml version="1.0"?><content>foobar</content>', $service->renderData());
  37. }
  38. }