Компоновщик (англ. Composite pattern) — структурный шаблон проектирования, объединяющий объекты в древовидную
структуру для представления иерархии от частного к целому. Компоновщик позволяет клиентам обращаться
к отдельным объектам и к группам объектов одинаково.
Очень простой шаблон. Будем создавать HTML документ для отправки нашему менеджеру. У нас есть один интерфейс для всех компонентов HTML документа. Это даёт нам возможность обращаться ко всем компонентам одним и тем же способом. Для наглядности реализуем интерфейс двумя абстрактными классами:
- Composite - родительский компонент, у которого есть наследники;
- Leaf - конечный элемент. Наследников иметь не может.
В тесте(CompositeTest) приведён пример постройки HTML документа.
-
Abstraction
-
Composite.php
<?php namespace DesignPatterns\Structural\Composite\Abstraction; use DesignPatterns\Structural\Composite\Helper\NewLineHelper; /** * Class Composite * @package DesignPatterns\Structural\Composite\Abstraction */ abstract class Composite implements CompositeInterface { use NewLineHelper; /** * @var CompositeInterface[] */ protected $elements = []; /** * @param CompositeInterface $element * * @return void */ public function addElement(CompositeInterface $element) { $this->elements[] = $element; } /** * @return string */ abstract public function render(): string; }
-
CompositeInterface.php
<?php namespace DesignPatterns\Structural\Composite\Abstraction; /** * Interface CompositeInterface * @package DesignPatterns\Structural\Composite\Abstraction */ interface CompositeInterface { /** * @param CompositeInterface $element * * @return void */ public function addElement(CompositeInterface $element); /** * @return string */ public function render(): string; }
-
Leaf.php
<?php namespace DesignPatterns\Structural\Composite\Abstraction; use DesignPatterns\Structural\Composite\Helper\NewLineHelper; /** * Class Leaf * @package DesignPatterns\Structural\Composite\Abstraction */ abstract class Leaf implements CompositeInterface { use NewLineHelper; /** * @var string */ protected $content; /** * Leaf constructor. * * @param string $content */ public function __construct(string $content) { $this->content = $content; } /** * @param CompositeInterface $element * * @return void */ public function addElement(CompositeInterface $element) { } /** * @return string */ abstract public function render(): string; }
-
-
Helper
-
NewLineHelper.php
<?php namespace DesignPatterns\Structural\Composite\Helper; /** * Class NewLineHelper * @package DesignPatterns\Structural\Composite\Helper */ trait NewLineHelper { /** * @param string $str * * @return string */ protected function newLineStringPrepare(string $str): string { return $str . PHP_EOL; } }
-
-
Html
-
Body.php
<?php namespace DesignPatterns\Structural\Composite\Html; use DesignPatterns\Structural\Composite\Abstraction\Composite; /** * Class Body * @package DesignPatterns\Structural\Composite\Html */ class Body extends Composite { /** * @var string */ protected $document; /** * @return string */ public function render(): string { $this->document = $this->newLineStringPrepare('<body>'); foreach ($this->elements as $element) { $this->document .= $element->render(); } $this->document .= $this->newLineStringPrepare('</body>'); return $this->document; } }
-
Div.php
<?php namespace DesignPatterns\Structural\Composite\Html; use DesignPatterns\Structural\Composite\Abstraction\Composite; /** * Class Div * @package DesignPatterns\Structural\Composite\Html */ class Div extends Composite { /** * @var string */ protected $document; /** * @return string */ public function render(): string { $this->document = $this->newLineStringPrepare('<div>'); foreach ($this->elements as $element) { $this->document .= $element->render(); } $this->document .= $this->newLineStringPrepare('</div>'); return $this->document; } }
-
H4.php
<?php namespace DesignPatterns\Structural\Composite\Html; use DesignPatterns\Structural\Composite\Abstraction\Leaf; /** * Class H4 * @package DesignPatterns\Structural\Composite\Html */ class H4 extends Leaf { /** * @return string */ public function render(): string { return $this->newLineStringPrepare('<h4>' . $this->content . '</h4>'); } }
-
Header.php
<?php namespace DesignPatterns\Structural\Composite\Html; use DesignPatterns\Structural\Composite\Abstraction\Composite; use DesignPatterns\Structural\Composite\Helper\NewLineHelper; /** * Class Header * @package DesignPatterns\Structural\Composite\Html */ class Header extends Composite { use NewLineHelper; /** * @var string */ protected $document; /** * @return string */ public function render(): string { $this->document = $this->newLineStringPrepare('<header>'); foreach ($this->elements as $element) { $this->document .= $element->render(); } $this->document .= $this->newLineStringPrepare('</header>'); return $this->document; } }
-
Html.php
<?php namespace DesignPatterns\Structural\Composite\Html; use DesignPatterns\Structural\Composite\Abstraction\Composite; /** * Class Html * @package DesignPatterns\Structural\Composite\Html */ class Html extends Composite { /** * @var string */ protected $document; /** * @return string */ public function render(): string { $this->document = $this->newLineStringPrepare('<html>'); foreach ($this->elements as $element) { $this->document .= $element->render(); } $this->document .= $this->newLineStringPrepare('</html>'); return $this->document; } }
-
Paragraph.php
<?php namespace DesignPatterns\Structural\Composite\Html; use DesignPatterns\Structural\Composite\Abstraction\Leaf; /** * Class Paragraph * @package DesignPatterns\Structural\Composite\Html */ class Paragraph extends Leaf { /** * @return string */ public function render(): string { return $this->newLineStringPrepare('<p>' . $this->content . '</p>'); } }
-
Title.php
<?php namespace DesignPatterns\Structural\Composite\Html; use DesignPatterns\Structural\Composite\Abstraction\Leaf; /** * Class Title * @package DesignPatterns\Structural\Composite\Html */ class Title extends Leaf { /** * @return string */ public function render(): string { return $this->newLineStringPrepare('<title>' . $this->content . '</title>'); } }
Test case:
CompositeTest.php
<?php namespace DesignPatterns\Tests\Structural\Composite; use DesignPatterns\Structural\Composite\Html\Body; use DesignPatterns\Structural\Composite\Html\Div; use DesignPatterns\Structural\Composite\Html\H4; use DesignPatterns\Structural\Composite\Html\Header; use DesignPatterns\Structural\Composite\Html\Html; use DesignPatterns\Structural\Composite\Html\Paragraph; use DesignPatterns\Structural\Composite\Html\Title; use PHPUnit_Framework_TestCase; class CompositeTest extends PHPUnit_Framework_TestCase { public function testRender() { $result = '<html>' . PHP_EOL . '<header>' . PHP_EOL . '<title>Custom Page</title>' . PHP_EOL . '</header>' . PHP_EOL . '<body>' . PHP_EOL . '<div>' . PHP_EOL . '<h4>Custom header</h4>' . PHP_EOL . '<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus convallis velit massa, faucibus egestas mauris vestibulum id. Vivamus ut justo.</p>' . PHP_EOL . '</div>' . PHP_EOL . '</body>' . PHP_EOL . '</html>' . PHP_EOL; $htmlDocument = new Html(); $header = new Header(); $title = new Title('Custom Page'); $header->addElement($title); $htmlDocument->addElement($header); $body = new Body(); $div = new Div(); $h4 = new H4('Custom header'); $paragraph = new Paragraph( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus convallis velit massa, faucibus egestas mauris vestibulum id. Vivamus ut justo.' ); $div->addElement($h4); $div->addElement($paragraph); $body->addElement($div); $htmlDocument->addElement($body); $this->assertEquals($result, $htmlDocument->render()); } }
-