Composite

Composite (компоновщик) — это структурный паттерн проектирования, который позволяет сгруппировать множество объектов в древовидную структуру, а затем работать с ней так, как будто это единичный объект.

Проблема

Паттерн Компоновщик имеет смысл только тогда, когда основная модель вашей программы может быть структурирована в виде дерева.

Например, есть два объекта: Продукт и Коробка. Коробка может содержать несколько Продуктов и других Коробок поменьше. Те, в свою очередь, тоже содержат либо Продукты, либо Коробки и так далее.

Теперь предположим, ваши Продукты и Коробки могут быть частью заказов. Каждый заказ может содержать как простые Продукты без упаковки, так и составные Коробки. Ваша задача состоит в том, чтобы узнать цену всего заказа.

Если решать задачу в лоб, то вам потребуется открыть все коробки заказа, перебрать все продукты и посчитать их суммарную стоимость. Но это слишком хлопотно, так как типы коробок и их содержимое могут быть вам неизвестны. Кроме того, наперёд неизвестно и количество уровней вложенности коробок, поэтому перебрать коробки простым циклом не выйдет.

Решение

Компоновщик предлагает рассматривать Продукт и Коробку через единый интерфейс с общим методом получения стоимости.

Продукт просто вернёт свою цену. Коробка спросит цену каждого предмета внутри себя и вернёт сумму результатов. Если одним из внутренних предметов окажется коробка поменьше, она тоже будет перебирать своё содержимое, и так далее, пока не будут посчитаны все составные части.

Composite - не слишком пригодня для сохранения в БД, зато отлично подходят для сохранения в XML.

abstract class Component
{
	protected $name;
	public function __construct($name)
	{
		$this->name = $name;
	}
	public abstract function add(Component $c);
	public abstract function remove(Component $c);
	public abstract function display();
}

class Composite extends Component
{
	private $children = array();
	public function add(Component $component)
	{
		$this->children[$component->name] = $component;
	}
	public function remove(Component $component)
	{
		unset($this->children[$component->name]);
	}
	public function display()
	{
		foreach($this->children as $child){
            $child->display();
		}
	}
}

class Leaf extends Component
{
	public function add(Component $c)
	{
		print ("Cannot add to a leaf") ;
	}
	public function remove(Component $c)
	{
		print("Cannot remove from a leaf");
	}
	public function display()
	{
		echo $this->name . PHP_EOL;
	}
}

// Create a tree structure
$root = new Composite("root");

$root->add(new Leaf("Leaf A"));
$root->add(new Leaf("Leaf B"));

$comp = new Composite("Composite X");

$comp->add(new Leaf("Leaf XA"));
$comp->add(new Leaf("Leaf XB"));
$root->add($comp);
$root->add(new Leaf("Leaf C"));

// Add and remove a leaf
$leaf = new Leaf("Leaf D");
$root->add($leaf);
$root->remove($leaf);

// Recursively display tree
$root->display();