Шаблон «Компоновщик» предполагает, что при разработке «контейнерных» объектов, которые собирают и упорядочивают «объекты содержимого», вы упрощаете операции, если предоставляете контейнерам и объектам содержимого общий набор методов. И тем самым поддерживаете максимум возможных методов при том, что вызывающему неважно, переданы отдельный объект контента или целый контейнер.
Реализация на Phyton
Шаблон «Компоновщик» предполагает, что при разработке «контейнерных» объектов, которые собирают и упорядочивают «объекты содержимого», вы упрощаете операции, если предоставляете контейнерам и объектам содержимого общий набор методов. И тем самым поддерживаете максимум возможных методов при том, что вызывающему неважно, переданы отдельный объект контента или целый контейнер.
Преимущества симметрии, которую создаёт этот паттерн между контейнерами и их содержимым, увеличиваются, только если симметрия делает объекты взаимозаменяемыми. Но здесь некоторые статически типизированные языки встречают препятствие.
В языках со строгой типизацией объекты двух классов взаимозаменяемые только при наследовании от одного родительского класса, который реализует общие методы, или при наследовании одного класса непосредственно от другого.
В других статических языках ограничение мягче. Нет строгой необходимости в том, чтобы контейнер и его содержимое делились реализацией. Пока оба соответствуют «интерфейсу», который объявляет конкретные общие методы, объекты вызываются симметрично.
Так как это программирование на Python, оба ограничения испаряются! Пишите код в предпочтительном для себя диапазоне безопасности и краткости. Хотите, пойдите классическим путём и добавьте общий суперкласс:
class Widget(object): def children(self): return [] class Frame(Widget): def __init__(self, child_widgets): self.child_widgets = child_widgets def children(self): return self.child_widgets class Label(Widget): def __init__(self, text): self.text = text
Или задайте объектам один и тот же интерфейс. И положитесь на тесты, которые помогут поддерживать симметрию между контейнерами и содержимым. (Где для простейших скриптов ваш «тест» может быть фактом выполнения кода.)
class Frame(object): def __init__(self, child_widgets): self.child_widgets = child_widgets def children(self): return self.child_widgets class Label(object): def __init__(self, text): self.text = text def children(self): return []
Или выберите другой подход из спектра дизайна между этими двумя крайностями. Вот что поддерживает Python:
abc
.zope.interface
.MyPy
.Поскольку Python предлагает такой спектр подходов, не стоит определять паттерн «Компоновщик» классически, то есть как один конкретный механизм (суперкласс) для создания или гарантирования симметрии. Вместо этого определите его как создание симметрии любыми средствами в иерархии объектов.
Реализация на 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();
Еще один пример с использованием типажей:
interface IComponent { function display(); } trait TComponent { public $name; public function __construct($name) { $this->name = $name; } public function display() { print $this->name.' '.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();