Паттерн «Компоновщик»
Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy. Урок 80 из 91.
Composite Pattern — это структурный шаблон проектирования, который позволяет объединять объекты в древовидные структуры, а затем работать с этими структурами так, как если бы они были отдельными объектами. Он идеально подходит для представления иерархий «часть — целое», в которых вы хотите единообразно обрабатывать отдельные объекты и группы объектов.
Рассмотрим файловую систему. folder can contain файлы и другие folder.
При вычислении общего размера нужно вызвать один и тот же метод и для файлов, и для папок, а папки должны рекурсивно суммировать своё содержимое. Паттерн Composite упрощает эту задачу, предоставляя и листьям (файлам), и composites (папкам) один и тот же интерфейс.
<?php
interface FileSystemItem {
public function getName(): string;
public function getSize(): int;
}
class File implements FileSystemItem {
public function __construct(
private string $name,
private int $size
) {}
public function getName(): string {
return $this->name;
}
public function getSize(): int {
return $this->size;
}
}
class Folder implements FileSystemItem {
private array $items = [];
public function __construct(private string $name) {}
public function getName(): string {
return $this->name;
}
public function add(FileSystemItem $item): void {
$this->items[] = $item;
}
public function getSize(): int {
$total = 0;
foreach ($this->items as $item) {
$total += $item->getSize();
}
return $total;
}
}
Теперь вы можете создавать древовидные структуры и единообразно работать с ними:
<?php
$docs = new Folder("Documents");
$docs->add(new File("resume.pdf", 250));
$docs->add(new File("cover.docx", 50));
$images = new Folder("Images");
$images->add(new File("photo.jpg", 1500));
$root = new Folder("Root");
$root->add($docs);
$root->add($images);
$root->add(new File("readme.txt", 10));
echo $root->getSize();
Вывод:
1810Клиентский код вызывает getSize(), не заботясь о том, работает ли он с одним file или со структурой глубоко вложенных folder. Паттерн обрабатывает рекурсию внутри себя, благодаря чему со сложными иерархиями легко работать.
Задание
ЛегкоДавайте создадим систему организационной структуры с использованием шаблона Composite. В компаниях иерархическая структура, где department может содержать employees и другие sub-departments: идеальная древовидная структура, в которой нужно единообразно вычислять итоговые значения для всей организации.
Вы организуете код в четырех файлах:
OrganizationUnit.php: Определите интерфейсOrganizationUnit, который будут реализовывать как отдельные employees, так и departments. В нем должны быть два метода:getName(): stringиgetSalary(): int. Этот общий интерфейс позволяет одинаково обрабатывать отдельных employees и целые departments при вычислении затрат.Employee.php: Подключите интерфейс OrganizationUnit и создайтеEmployeeclass, который его реализует. Employee — это конечный узел в дереве организации. У него есть имя и зарплата, но он cannot содержать другие units. Используйте продвижение параметров конструктора, чтобы принять имя и зарплату, и реализуйте оба метода интерфейса.Department.php: Подключите интерфейс OrganizationUnit и создайтеDepartmentclass, который его реализует. Department — это composite, который can содержать both employees и другие departments. Ваш department должен:- Принимать имя в своем конструкторе
- Реализовать
getName(), чтобы возвращать имя department - Иметь метод
add(OrganizationUnit $unit): voidдля добавления employees или sub-departments - Реализовать
getSalary(), чтобы возвращать общую зарплату всех содержащихся в нем units (рекурсивно суммируя все элементы внутри)
main.php: Подключите файлы Employee и Department. Вы получите один входной параметр: JSON-строку, представляющую структуру организации.JSON будет иметь следующий формат:
{"Engineering": [{"name": "Alice", "salary": 80000}, {"name": "Bob", "salary": 75000}], "Sales": [{"name": "Carol", "salary": 65000}]}Каждый ключ — это название department, а его значение — array employees с их именами и зарплатами.
Создайте корневой
Departmentс именем"Company". Для каждого department в JSON создайтеDepartment, добавьте его employees, а затем добавьте этот department в Company. Наконец, выведите общую зарплату компании в следующем формате:Total Salary: [amount]
Преимущество шаблона Composite заключается в том, что вызов getSalary() для department Company автоматически вычисляет сумму по всем вложенным departments и employees: клиентскому коду не нужно знать внутреннюю структуру.
Попробуйте сами
<?php
require_once 'Employee.php';
require_once 'Department.php';
// Прочитать входные данные JSON
$input = trim(fgets(STDIN));
$data = (array)json_decode($input, true);
// TODO: Создать корневой Department с названием "Company"
// TODO: Пройти циклом по каждому department в данных JSON
// Для каждого department:
// - Создать новый объект Department
// - Добавить каждого employee в этот department
// - Добавить department в Company
// TODO: Вывести общую зарплату в формате: Total Salary: [amount]
?>В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово $thisМетодыСвойстваКонструктор (__construct)Деструктор (__destruct)Повторение — простой калькулятор4Наследование
Основы наследованияКлючевое слово parent::Переопределение методовКлючевое слово finalАбстрактные классыПовторение — иерархия сотрудников7Инкапсуляция
Public, Protected, PrivateМодификаторы доступа подробноГеттеры и сеттерыСокрытие информацииПродвижение конструктора (8.0)Повторение — система учёта студентов10Продвинутые концепции ООП
Композиция и наследованиеВнедрение зависимостейАнонимные классыПеречисления (PHP 8.1)Файберы (PHP 8.1)Глубокое погружение в клонирование объектовГенераторы и итераторы13Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Репозиторий»2Пространства имён и автозагрузка
Введение в пространства имёнКлючевое слово useСтандарт автозагрузки PSR-4Автозагрузчик ComposerПовторение — структурированный проект5Интерфейсы и контракты
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовИнтерфейс и абстрактный классАннотация типов с интерфейсамиПовторение — калькулятор фигур8Магические методы
Введение в магические методы__toString и __debugInfo__get, __set, __isset, __unset__call и __callStatic__clone и клонирование объектов__serialize и __unserializeПовторение — пользовательская коллекция3Свойства классов
Свойства экземпляра и статические свойстваКонстанты в классахСтатические методы и свойстваПриватные и защищённые свойстваСвойства только для чтения (PHP 8.1)Повторение — менеджер банковского счёта6Полиморфизм
Повторение переопределения методовПолиморфизм через интерфейсыАннотации типов и объединённые типыПозднее статическое связываниеПовторение — обработчик платежей9Трейты
Введение в трейтыИспользование нескольких трейтовРазрешение конфликтов трейтовАбстрактные методы в трейтахТрейты и наследование12Паттерны проектирования, часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн StrategyПотренируйтесь самостоятельно: Онлайн-компилятор PHP