组合模式(Composite)

目的

一组对象与该对象的单个实例的处理方式一致。

示例

  • 一个表单类实例在处理其表单所有元素的方法与处理该表单自身实例方法相同,在调用方法 render() 时,会随之遍历它的所有子元素并对他们调用 render() 方法
  • Zend_Config: 一个配置选项树,每个选项自身就是一个 Zend_Config 对象

UML 图

file

代码

相关代码参见 GitHub

RenderableInterface.php

  1. <?php
  2. namespace DesignPatterns\Structural\Composite;
  3. interface RenderableInterface
  4. {
  5. public function render(): string;
  6. }

Form.php

  1. <?php
  2. namespace DesignPatterns\Structural\Composite;
  3. /**
  4. * 该组合内的节点必须派生于该组件契约。为了构建成一个组件树,
  5. * 此为强制性操作。
  6. */
  7. class Form implements RenderableInterface
  8. {
  9. /**
  10. * @var RenderableInterface[]
  11. */
  12. private $elements;
  13. /**
  14. * 遍历所有元素,并对他们调用 render() 方法,然后返回表单的完整
  15. * 的解析表达。
  16. *
  17. * 从外部上看,我们不会看到遍历过程,该表单的操作过程与单一对
  18. * 象实例一样
  19. *
  20. * @return string
  21. */
  22. public function render(): string
  23. {
  24. $formCode = '<form>';
  25. foreach ($this->elements as $element) {
  26. $formCode .= $element->render();
  27. }
  28. $formCode .= '</form>';
  29. return $formCode;
  30. }
  31. /**
  32. * @param RenderableInterface $element
  33. */
  34. public function addElement(RenderableInterface $element)
  35. {
  36. $this->elements[] = $element;
  37. }
  38. }

InputElement.php

  1. <?php
  2. namespace DesignPatterns\Structural\Composite;
  3. class InputElement implements RenderableInterface
  4. {
  5. public function render(): string
  6. {
  7. return '<input type="text" />';
  8. }
  9. }

TextElement.php

  1. <?php
  2. namespace DesignPatterns\Structural\Composite;
  3. class TextElement implements RenderableInterface
  4. {
  5. /**
  6. * @var string
  7. */
  8. private $text;
  9. public function __construct(string $text)
  10. {
  11. $this->text = $text;
  12. }
  13. public function render(): string
  14. {
  15. return $this->text;
  16. }
  17. }

测试

Tests/CompositeTest.php

  1. <?php
  2. namespace DesignPatterns\Structural\Composite\Tests;
  3. use DesignPatterns\Structural\Composite;
  4. use PHPUnit\Framework\TestCase;
  5. class CompositeTest extends TestCase
  6. {
  7. public function testRender()
  8. {
  9. $form = new Composite\Form();
  10. $form->addElement(new Composite\TextElement('Email:'));
  11. $form->addElement(new Composite\InputElement());
  12. $embed = new Composite\Form();
  13. $embed->addElement(new Composite\TextElement('Password:'));
  14. $embed->addElement(new Composite\InputElement());
  15. $form->addElement($embed);
  16. // 此代码仅作示例。在实际场景中,现在的网页浏览器根本不支持
  17. // 多表单嵌套,牢记该点非常重要
  18. $this->assertEquals(
  19. '<form>Email:<input type="text" /><form>Password:<input type="text" /></form></form>',
  20. $form->render()
  21. );
  22. }
  23. }