Компоновщик (шаблон проектирования)

13.03.2021

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

Цель

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

Описание

UML-диаграмма шаблона:

Примеры реализации

Пример на Java

Исходный текст на языке Java import java.util.List; import java.util.ArrayList; /** "Component" */ interface Graphic { //Prints the graphic. public void print(); } /** "Composite" */ class CompositeGraphic implements Graphic { //Collection of child graphics. private List<Graphic> mChildGraphics = new ArrayList<Graphic>(); //Prints the graphic. public void print() { for (Graphic graphic : mChildGraphics) { graphic.print(); } } //Adds the graphic to the composition. public void add(Graphic graphic) { mChildGraphics.add(graphic); } //Removes the graphic from the composition. public void remove(Graphic graphic) { mChildGraphics.remove(graphic); } } /** "Leaf" */ class Ellipse implements Graphic { //Prints the graphic. public void print() { System.out.println("Ellipse"); } } /** Client */ public class Program { public static void main(String[] args) { //Initialize four ellipses Ellipse ellipse1 = new Ellipse(); Ellipse ellipse2 = new Ellipse(); Ellipse ellipse3 = new Ellipse(); Ellipse ellipse4 = new Ellipse(); //Initialize three composite graphics CompositeGraphic graphic = new CompositeGraphic(); CompositeGraphic graphic1 = new CompositeGraphic(); CompositeGraphic graphic2 = new CompositeGraphic(); //Composes the graphics graphic1.add(ellipse1); graphic1.add(ellipse2); graphic1.add(ellipse3); graphic2.add(ellipse4); graphic.add(graphic1); graphic.add(graphic2); //Prints the complete graphic (four times the string "Ellipse"). graphic.print(); } }

Пример на C#

Исходный текст на языке C# class MainApp { static void Main() { // Create a tree structure Composite root = new Composite("root"); root.Add(new Leaf("Leaf A")); root.Add(new Leaf("Leaf B")); Composite 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 leaf = new Leaf("Leaf D"); root.Add(leaf); root.Remove(leaf); // Recursively display tree root.Display(1); // Wait for user Console.Read(); } } /// <summary> /// Component - компонент /// </summary> /// <li> /// <lu>объявляет интерфейс для компонуемых объектов;</lu> /// <lu>предоставляет подходящую реализацию операций по умолчанию, /// общую для всех классов;</lu> /// <lu>объявляет интерфейс для доступа к потомкам и управлению ими;</lu> /// <lu>определяет интерфейс доступа к родителю компонента в рекурсивной структуре /// и при необходимости реализует его. Описанная возможность необязательна;</lu> /// </li> abstract class Component { protected string name; // Constructor public Component(string name) { this.name = name; } public abstract void Display(int depth); } /// <summary> /// Composite - составной объект /// </summary> /// <li> /// <lu>определяет поведение компонентов, у которых есть потомки;</lu> /// <lu>хранит компоненты-потомоки;</lu> /// <lu>реализует относящиеся к управлению потомками операции и интерфейсе /// класса <see cref="Component"/></lu> /// </li> class Composite : Component { private List<Component> children = new List<Component>(); // Constructor public Composite(string name) : base(name) { } public void Add(Component component) { children.Add(component); } public void Remove(Component component) { children.Remove(component); } public override void Display(int depth) { Console.WriteLine(new String('-', depth) + name); // Recursively display child nodes foreach (Component component in children) { component.Display(depth + 2); } } } /// <summary> /// Leaf - лист /// </summary> /// <remarks> /// <li> /// <lu>представляет листовой узел композиции и не имеет потомков;</lu> /// <lu>определяет поведение примитивных объектов в композиции;</lu> /// </li> /// </remarks> class Leaf : Component { // Constructor public Leaf(string name) : base(name) { } public override void Display(int depth) { Console.WriteLine(new String('-', depth) + name); } }

Пример на C++

Исходный текст на языке C++ #include <iostream> #include <list> #include <algorithm> #include <memory> class IText{ public: typedef std::shared_ptr<IText> SPtr; virtual void draw() = 0; virtual void add(const SPtr&) { throw std::runtime_error("IText: Can't add to a leaf"); } virtual void remove(const SPtr&){ throw std::runtime_error("IText: Can't remove from a leaf"); } }; class CompositeText: public IText{ public: void add(const SPtr& sptr){ children_.push_back(sptr); } void remove(const SPtr& sptr){ children_.remove(sptr); } void replace(const SPtr& oldValue, const SPtr& newValue){ std::replace(children_.begin(), children_.end(), oldValue, newValue); } virtual void draw(){ for(SPtr& sptr : children_){ sptr->draw(); } } private: std::list<SPtr> children_; }; class Letter: public IText{ public: Letter(char c):c_(c) {} virtual void draw(){ std::cout<<c_; } private: char c_; }; int main(){ CompositeText sentence; IText::SPtr lSpace(new Letter(' ')); IText::SPtr lExcl(new Letter('!')); IText::SPtr lComma(new Letter(',')); IText::SPtr lNewLine(new Letter(' ')); IText::SPtr lH(new Letter('H')); // letter 'H' IText::SPtr le(new Letter('e')); // letter 'e' IText::SPtr ll(new Letter('l')); // letter 'l' IText::SPtr lo(new Letter('o')); // letter 'o' IText::SPtr lW(new Letter('W')); // letter 'W' IText::SPtr lr(new Letter('r')); // letter 'r' IText::SPtr ld(new Letter('d')); // letter 'd' IText::SPtr li(new Letter('i')); // letter 'i' IText::SPtr wHello(new CompositeText); wHello->add(lH); wHello->add(le); wHello->add(ll); wHello->add(ll); wHello->add(lo); IText::SPtr wWorld(new CompositeText); // word "World" wWorld->add(lW); wWorld->add(lo); wWorld->add(lr); wWorld->add(ll); wWorld->add(ld); sentence.add(wHello); sentence.add(lComma); sentence.add(lSpace); sentence.add(wWorld); sentence.add(lExcl); sentence.add(lNewLine); sentence.draw(); // prints "Hello, World! " IText::SPtr wHi(new CompositeText); // word "Hi" wHi->add(lH); wHi->add(li); sentence.replace(wHello, wHi); sentence.draw(); // prints "Hi, World! " sentence.remove(wWorld); sentence.remove(lSpace); sentence.remove(lComma); sentence.draw(); // prints "Hi! " return 0; }

Пример на D

Исходный текст на языке D import std.stdio; abstract class TInfo { protected: string name; public: void Info(); } class TFile: TInfo { protected: uint size; public: this(const string theName, uint theSize) { name = theName; size = theSize; } void Info() { writefln("%s %d", name, size); } } class TDir: TInfo { protected: TInfo[] info; public: this(const string theName) { name = theName; } void Info() { writefln("[%s]", name); foreach (f; info) { f.Info(); } } void Add(TInfo theInfo) { info ~= theInfo; } } void main() { TDir first = new TDir("first"); first.Add(new TFile("a.txt", 100)); first.Add(new TFile("b.txt", 200)); first.Add(new TFile("c.txt", 300)); TDir second = new TDir("second"); second.Add(new TFile("d.txt", 400)); second.Add(new TFile("e.txt", 500)); TDir root = new TDir("root"); root.Add(first); root.Add(second); root.Info(); }

Пример на Python

Исходный текст на языке Python from abc import ABCMeta, abstractmethod class Unit(metaclass=ABCMeta): """ Абстрактный компонент, в данном случае это - отряд (отряд может состоять из одного солдата или более) """ @abstractmethod def print(self) -> None: """ Вывод данных о компоненте """ pass class Archer(Unit): """ Лучник """ def print(self) -> None: print('лучник', end=' ') class Knight(Unit): """ Рыцарь """ def print(self) -> None: print('рыцарь', end=' ') class Swordsman(Unit): """ Мечник """ def print(self) -> None: print('мечник', end=' ') class Squad(Unit): """ Компоновщик - отряд, состоящий более чем из одного человека. Также может включать в себя другие отряды-компоновщики. """ def __init__(self): self._units = [] def print(self) -> None: print("Отряд {} (".format(self.__hash__()), end=' ') for u in self._units: u.print() print(')') def add(self, unit: Unit) -> None: """ Добавление нового отряда :param unit: отряд (может быть как базовым, так и компоновщиком) """ self._units.append(unit) unit.print() print('присоединился к отряду {}'.format(self.__hash__())) print() def remove(self, unit: Unit) -> None: """ Удаление отряда из текущего компоновщика :param unit: объект отряда """ for u in self._units: if u == unit: self._units.remove(u) u.print() print('покинул отряд {}'.format(self.__hash__())) print() break else: unit.print() print('в отряде {} не найден'.format(self.__hash__())) print() if __name__ == '__main__': print('OUTPUT:') squad = Squad() squad.add(Knight()) squad.add(Knight()) squad.add(Archer()) swordsman = Swordsman() squad.add(swordsman) squad.remove(swordsman) squad.print() squad_big = Squad() squad_big.add(Swordsman()) squad_big.add(Swordsman()) squad_big.add(squad) squad_big.print() ''' OUTPUT: рыцарь присоединился к отряду -9223363262492103834 рыцарь присоединился к отряду -9223363262492103834 лучник присоединился к отряду -9223363262492103834 мечник присоединился к отряду -9223363262492103834 мечник покинул отряд -9223363262492103834 Отряд -9223363262492103834 ( рыцарь рыцарь лучник ) мечник присоединился к отряду 8774362671992 мечник присоединился к отряду 8774362671992 Отряд -9223363262492103834 ( рыцарь рыцарь лучник ) присоединился к отряду 8774362671992 Отряд 8774362671992 ( мечник мечник Отряд -9223363262492103834 ( рыцарь рыцарь лучник ) ) '''

Пример на PHP5

Исходный текст на языке PHP5 <?php abstract class Component { protected $name; public function __construct($name) { $this->name = $name; } 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 display() { print_r($this->name); } } // 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(); ?>

Пример компоновщика с внешним итератором на PHP5

Исходный текст на языке PHP5 /** * Паттерн-компоновщик с внешним итератором * Итератор использует рекурсию для перебора дерева элементов */ namespace compositeIterator{ /** * Клиент использует интерфейс AComponent для работы с объектами. * Интерфейс AComponent определяет интерфейс для всех компонентов: как комбинаций, так и листовых узлов. * AComponent может реализовать поведение по умолчанию для add() remove() getChild() и других операций */ abstract class AComponent { public $customPropertyName; public $customPropertyDescription; /** * @param AComponent $component */ public function add($component) { throw new Exception("Unsupported operation"); } /** * @param AComponent $component */ public function remove($component) { throw new Exception("Unsupported operation"); } /** * @param int $int */ public function getChild($int) { throw new Exception("Unsupported operation"); } /** * @return IPhpLikeIterator */ abstract function createIterator(); public function operation1() { throw new Exception("Unsupported operation"); } } /** * Leaf наследует методы add() remove() getChild( которые могут не иметь смысла для листового узла. * Хотя листовой узел можно считать узлом с нулём дочерних объектов * * Leaf определяет поведение элементов комбинации. Для этого он реализует операции, поддерживаемые интерфейсом Composite. */ class Leaf extends AComponent { public function __construct($name, $description = '') { $this->customPropertyName = $name; $this->customPropertyDescription = $description; } public function createIterator() { return new NullIterator(); } public function operation1() { echo (" I'am leaf {$this->customPropertyName}, i don't want to do operation 1. {$this->customPropertyDescription}"); } } class NullIterator implements IPhpLikeIterator { public function valid() { return (false); } public function next() { return (false); } public function current() { return (null); } public function remove() { throw new CException('unsupported operation'); } } /** * Интерфейс Composite определяет поведение компонентов, имеющих дочерние компоненты, и обеспечивает хранение последних. * * Composite также реализует операции, относящиеся к Leaf. Некоторые из них не могут не иметь смысла для комбинаций; в таких случаях генерируется исключение. */ class Composite extends AComponent { private $_iterator = null; /** * @var ArrayObject AComponent[] $components для хранения потомков типа AComponent */ public $components = null; public function __construct($name, $description = '') { $this->customPropertyName = $name; $this->customPropertyDescription = $description; } /** * @param AComponent $component */ public function add($component) { if (is_null($this->components)) { $this->components = new ArrayObject; } $this->components->append($component); } public function remove($component) { foreach ($this->components as $i => $c) { if ($c === $component) { unset($this->components[$i]); } } } public function getChild($int) { return ($this->components[$int]); } public function operation1() { echo " $this->customPropertyName $this->customPropertyDescription"; echo " --------------------------------"; $iterator = $this->components->getIterator(); while ($iterator->valid()) { $component = $iterator->current(); $component->operation1(); $iterator->next(); } } /** * @return CompositeIterator */ public function createIterator() { if (is_null($this->_iterator)) { $this->_iterator = new CompositeIterator($this->components->getIterator()); } return ($this->_iterator); } } /** * Рекурсивный итератор компоновщика */ class CompositeIterator implements IPhpLikeIterator { public $stack = array(); /** * @param ArrayIterator $componentsIterator */ public function __construct($componentsIterator) { //$this->stack= new ArrayObject; $this->stack[] = $componentsIterator; } public function remove() { throw new CException('unsupported operation'); } public function valid() { if (empty($this->stack)) { return (false); } else { /** @var $componentsIterator ArrayIterator */ // берём первый элемент $componentsIterator = array_shift(array_values($this->stack)); if ($componentsIterator->valid()) { return (true); } else { array_shift($this->stack); return ($this->valid()); } } } public function next() { /** @var $componentsIterator ArrayIterator */ $componentsIterator = current($this->stack); $component = $componentsIterator->current(); if ($component instanceof Composite) { array_push($this->stack, $component->createIterator()); } $componentsIterator->next(); //return($component); } public function current() { if ($this->valid()) { /** @var $componentsIterator ArrayIterator */ // берём первый элемент $componentsIterator = array_shift(array_values($this->stack)); return ($componentsIterator->current()); } else { return (null); } } } /** * Интерфейс Iterator должен быть реализован всеми итераторами. * Данный интерфейс является частью интерфейса стандартного php итератора. * Конкретный Iterator отвечает за управление текущей позицией перебора в конкретной коллекции. */ interface IPhpLikeIterator { /** * @abstract * @return boolean есть ли текущий элемент */ public function valid(); /** * @abstract * @return mixed перевести курсор дальше */ public function next(); /** * @abstract * @return mixed получить текущий элемент */ public function current(); /** * удалить текущий элемент коллекции * @abstract * @return void */ public function remove(); } class Client { /** * @var AComponent */ public $topItem; public function __construct($topItem) { $this->topItem = $topItem; } public function printOperation1() { $this->topItem->operation1(); } public function printOperation2() { echo " "; $iterator = $this->topItem->createIterator(); while ($iterator->valid()) { /** @var $component AComponent */ $component = $iterator->current(); if (strstr($component->customPropertyName, 'leaf1')) { echo (" I'm Client, I found leaf {$component->customPropertyName}, I'll just leave it here (for my 'first-leafs' tea collection). {$component->customPropertyDescription}"); } $iterator->next(); } } } class Test { public static function go() { $a = new Composite("c1"); $b = new Composite("c2"); $c = new Composite("c3"); $topItem = new Composite("top item"); $topItem->add($a); $topItem->add($b); $topItem->add($c); $a->add(new Leaf("c1-leaf1")); $a->add(new Leaf("c1-leaf2")); $b->add(new Leaf("c2-leaf1")); $b->add(new Leaf("c2-leaf2")); $b->add(new Leaf("c2-leaf3")); $c->add(new Leaf("c3-leaf1")); $c->add(new Leaf("c3-leaf2")); $client = new Client($topItem); $client->printOperation1(); $client->printOperation2(); } } Test::go(); }

Пример на PHP5.4

Исходный текст на языке PHP5.4 <?php interface IComponent { function display(); } trait TComponent { public $name; public function __construct($name) { $this->name = $name; } public function display() { print $this->name.'<br>'.PHP_EOL; } } trait TComposite { use TComponent{ TComponent::display as displaySelf; } protected $children = array(); public function add(IComponent $item) { $this->children[$item->name] = $item; } public function remove(IComponent $item) { unset($this->children[$item->name]); } public function display() { $this->displaySelf(); foreach ($this->children as $child) { $child->display(); } } } class Composite implements IComponent { use TComposite; } class Leaf implements IComponent { use TComponent; } $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")); $leaf = new Leaf("Leaf D"); $root->add($leaf); $root->remove($leaf); $root->display();

Пример на CoffeeScript

Исходный текст на языке CoffeeScript

Пример болванки простенького физического движка

# Component class PObject collide : (pObj) -> addChild : (pObj) -> rmChild : (index) -> getChild : (index) -> # Leaf class PShape extends PObject collide : (pObj) -> # ... # Composite class PCollection extends PObject constructor : -> @children = [] collide : (pObj) -> child.collide(pObj) for child in @children return @ addChild : (pObj) -> @children.push(pObj) if pObj instanceof PObject return @ rmChild : (index) -> @children.splice(index, 1) return @ getChild : (index) -> @children[index]

Пример на VB.NET

Исходный текст на языке VB.NET Class Program Shared Sub Main() ' Create a tree structure Dim root As Component = New Composite("root") root.Add(New Leaf("Leaf A")) root.Add(New Leaf("Leaf B")) Dim comp As Component = 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 Dim leaf As New Leaf("Leaf D") root.Add(leaf) root.Remove(leaf) ' Recursively display tree root.Display(1) ' Wait for user Console.Read() End Sub End Class ''' <summary> ''' Component - компонент ''' </summary> ''' <li> ''' <lu>объявляет интерфейс для компонуемых объектов;</lu> ''' <lu>предоставляет подходящую реализацию операций по умолчанию, ''' общую для всех классов;</lu> ''' <lu>объявляет интерфейс для доступа к потомкам и управлению ими;</lu> ''' <lu>определяет интерфейс доступа к родителю компонента в рекурсивной структуре ''' и при необходимости реализует его. Описанная возможность необязательна;</lu> ''' </li> MustInherit Class Component Protected name As String ' Constructor Public Sub New(ByVal name As String) Me.name = name End Sub Public MustOverride Sub Add(ByVal c As Component) Public MustOverride Sub Remove(ByVal c As Component) Public MustOverride Sub Display(ByVal depth As Integer) End Class ''' <summary> ''' Composite - составной объект ''' </summary> ''' <li> ''' <lu>определяет поведеление компонентов, у которых есть потомки;</lu> ''' <lu>хранит компоненты-потомоки;</lu> ''' <lu>реализует относящиеся к управлению потомками операции и интерфейсе ''' класса <see cref="Component"/></lu> ''' </li> Class Composite Inherits Component Private children As New ArrayList() ' Constructor Public Sub New(ByVal name As String) MyBase.New(name) End Sub Public Overrides Sub Add(ByVal component As Component) children.Add(component) End Sub Public Overrides Sub Remove(ByVal component As Component) children.Remove(component) End Sub Public Overrides Sub Display(ByVal depth As Integer) Console.WriteLine(New String("-"c, depth) & name) ' Recursively display child nodes For Each component As Component In children component.Display(depth + 2) Next End Sub End Class ''' <summary> ''' Leaf - лист ''' </summary> ''' <remarks> ''' <li> ''' <lu>представляет листовой узел композиции и не имеет потомков;</lu> ''' <lu>определяет поведение примитивных объектов в композиции;</lu> ''' </li> ''' </remarks> Class Leaf Inherits Component ' Constructor Public Sub New(ByVal name As String) MyBase.New(name) End Sub Public Overrides Sub Add(ByVal c As Component) Console.WriteLine("Cannot add to a leaf") End Sub Public Overrides Sub Remove(ByVal c As Component) Console.WriteLine("Cannot remove from a leaf") End Sub Public Overrides Sub Display(ByVal depth As Integer) Console.WriteLine(New String("-"c, depth) & name) End Sub End Class

Пример на Delphi

Исходный текст на языке Delphi program CompositePattern; {$APPTYPE CONSOLE} uses SysUtils, Contnrs; type TCustomLetter = class public procedure Draw; virtual; abstract; end; type TLetter = class(TCustomLetter) private FLetter: Char; public constructor Create(aLetter: Char); procedure Draw; override; end; constructor TLetter.Create(aLetter: Char); begin FLetter := aLetter; end; procedure TLetter.Draw; begin Write(FLetter); end; type TWord = class(TCustomLetter) private FWord: String; public constructor Create(aWord: String); procedure Draw; override; end; constructor TWord.Create(aWord: String); begin FWord := aWord; end; procedure TWord.Draw; begin Write(FWord); end; type TText = class(TCustomLetter) private FList: TObjectList; public constructor Create; destructor Destroy; override; procedure Add(aCustomLetter: TCustomLetter); procedure Draw; override; end; constructor TText.Create; begin inherited; FList := TObjectList.Create; end; destructor TText.Destroy; begin FList.Free; inherited; end; procedure TText.Add(aCustomLetter: TCustomLetter); begin FList.Add(aCustomLetter); end; procedure TText.Draw; var vI: Integer; begin for vI := 0 to Pred(FList.Count) do TLetter(FList[vI]).Draw; end; var vRootText, vSubText: TText; begin vRootText := TText.Create; vSubText := TText.Create; try vSubText.Add(TLetter.Create('!')); vSubText.Add(TLetter.Create('!')); vSubText.Add(TLetter.Create('!')); vSubText.Add(TWord.Create(' =)')); vRootText.Add(TLetter.Create('H')); vRootText.Add(TLetter.Create('E')); vRootText.Add(TLetter.Create('L')); vRootText.Add(TLetter.Create('L')); vRootText.Add(TLetter.Create('O')); vRootText.Add(TLetter.Create(' ')); vRootText.Add(TWord.Create('World')); vRootText.Add(vSubText); vRootText.Draw; finally vRootText.Destroy; end; ReadLn; end.

Пример на JavaScript

Исходный текст на языке JavaScript function Component() { this.name = ''; this.value = 0; this.execute = function () { }; } function Leaf(name, value) { this.name = name; this.value = value; this.execute = function () { return this.value; }; } Leaf.prototype = Object.create(Component.prototype); Leaf.prototype.constructor = Leaf; function Composite(name) { var self = this; var children = []; this.name = name; this.add = function (component) { children.push(component); }; this.remove = function (componentName) { var newChildren = []; children.forEach(function (component) { if (component.name !== componentName) { newChildren.push(component); } }); children = newChildren; }; this.execute = function () { children.forEach(function (component) { self.value = (self.value || 0) + component.execute(); }); return self.value; }; } Composite.prototype = Object.create(Component.prototype); Composite.prototype.constructor = Composite; // Применение var kitchen = new Composite('Кухня'); kitchen.add( new Leaf('Верхняя секция', 5200) ); kitchen.add( new Leaf('Верхняя двойная секция', 10000) ); kitchen.add( new Leaf('Нижняя секция', 4500) ); kitchen.add( new Leaf('Нижняя угловая секция', 7800) ); var equipment = new Composite('Техника'); equipment.add( new Leaf('Газовая плита', 26400) ); equipment.add( new Leaf('Холодильник', 32300) ); equipment.add( new Leaf('Посудомойка', 21600) ); kitchen.add(equipment); console.log('Итого: ' + kitchen.execute() + ' руб.');

Пример на Swift

Исходный текст на языке Swift protocol Item { var id: UInt32 { get } var name: String { get } func desctiption() -> String } class Button: Item { var id: UInt32 = arc4random() var name: String = "Button" func desctiption() -> String { return "ID: (id) | (name)" } } class Label: Item { var id: UInt32 = arc4random() var name: String = "Label" func desctiption() -> String { return "ID: (id) | (name)" } } class View: Item { var components: [Item] = [] var id: UInt32 = arc4random() var name: String = "View" func desctiption() -> String { return components.reduce("", {"($0) ($1.desctiption())"}) } func add(item: Item) { components.append(item) } func remove(item: Item) { if let index = components.firstIndex(where: {$0.id == item.id}) { components.remove(at: index) } } } // Use Composite let button = Button() print(button.desctiption()) let view = View() view.add(item: Button()) view.add(item: Label()) print(view.desctiption())