Компоновщик (англ. 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 ());
}
}