Паттерн Команда
Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy — урок 75 из 91.
Паттерн Команда (Command Pattern) — это поведенческий паттерн проектирования, который превращает запрос в самостоятельный объект, содержащий всю информацию о запросе. Такая трансформация позволяет передавать запросы в качестве аргументов методов, откладывать или ставить выполнение запроса в очередь, а также поддерживать операции отмены.
Представьте себе ресторан.
Когда вы заказываете еду, официант записывает ваш заказ на листке (команду), относит его на кухню, и повар его выполняет. Официанту не нужно знать, как готовить; он просто передает команду.
Это отделяет отправителя запроса от исполнителя.
Вот базовая реализация:
<?php
interface Command {
public function execute(): string;
}class Light {
public function on(): string { return "Light is ON"; }
public function off(): string { return "Light is OFF"; }
}class LightOnCommand implements Command {
public function __construct(private Light $light) {}
public function execute(): string {
return $this->light->on();
}
}class LightOffCommand implements Command {
public function __construct(private Light $light) {}
public function execute(): string {
return $this->light->off();
}
}
Инициатор (invoker) запускает команды, не зная, что они делают:
<?php
class RemoteControl {
public function press(Command $command): string {
return $command->execute();
}
}$light = new Light();
$remote = new RemoteControl();
echo $remote->press(new LightOnCommand($light)) . "\n";
echo $remote->press(new LightOffCommand($light));
Вывод:
Light is ON
Light is OFFПаттерн Команда (Command Pattern) отлично подходит в тех случаях, когда вам нужно ставить операции в очередь, реализовывать функциональность отмены или логировать действия. Каждая команда самодостаточна, что позволяет легко добавлять новые команды, не изменяя существующий код.
Задание
ЛегкоДавайте создадим командную систему текстового редактора, используя паттерн Команда (Command Pattern). Текстовым редакторам часто требуется поддерживать такие операции, как написание текста, удаление текста и отмена действий — каждая операция становится автономным объектом команды, который может быть выполнен через единый интерфейс.
Вы организуете свой код в четырех файлах:
Command.php— Определите интерфейсCommandс единственным методомexecute(): string. Это устанавливает контракт, которому должны следовать все команды редактора.TextEditor.php— Создайте классTextEditor, который представляет собой получателя (receiver) — объект, который фактически выполняет работу. Ваш редактор должен:- Хранить текущее текстовое содержимое (начните с пустой строки)
- Иметь метод
write(string $text): string, который добавляет текст к содержимому и возвращает"Written: [text]" - Иметь метод
clear(): string, который очищает содержимое и возвращает"Content cleared" - Иметь метод
getContent(): string, который возвращает текущее содержимое
Commands.php— Подключите файлы Command и TextEditor. Создайте два класса команд, реализующих интерфейсCommand:WriteCommand— ПринимаетTextEditorи текстовую строку в своем конструкторе. Его методexecute()вызывает метод редактораwrite()и возвращает результат.ClearCommand— ПринимаетTextEditorв своем конструкторе. Его методexecute()вызывает метод редактораclear()и возвращает результат.
main.php— Подключите файл Commands. Вы получите два входных значения: тип команды ("write"или"clear") и текстовую строку (используется только для команд записи).Создайте экземпляр
TextEditor. В зависимости от типа команды создайте соответствующий объект команды. Затем создайте функцию-инициатор (invoker) или простой механизм, который вызываетexecute()для команды и выводит результат.На новой строке выведите текущее содержимое редактора в следующем формате:
Content: [content](если оно пустое, просто выведитеContent:без ничего после пробела).
Эта задача демонстрирует, как паттерн Команда инкапсулирует каждую операцию в виде объекта. Инициатору не нужно знать, выполняет ли он команду записи или очистки — он просто вызывает execute() и позволяет команде обработать детали.
Шпаргалка
Паттерн Команда (Command Pattern) — это поведенческий паттерн проектирования, который инкапсулирует запрос в виде отдельного объекта, содержащего всю информацию о запросе. Это позволяет передавать запросы в качестве аргументов методов, ставить операции в очередь и поддерживать отмену действий.
Паттерн отделяет отправителя (invoker) от исполнителя (receiver), подобно тому как официант передает заказы повару, не зная, как именно готовится блюдо.
Базовая структура
Определите интерфейс Command с методом execute():
interface Command {
public function execute(): string;
}Создайте класс-получатель (receiver), который выполняет фактическую работу:
class Light {
public function on(): string { return "Light is ON"; }
public function off(): string { return "Light is OFF"; }
}Реализуйте конкретные классы команд, которые инкапсулируют определенные действия:
class LightOnCommand implements Command {
public function __construct(private Light $light) {}
public function execute(): string {
return $this->light->on();
}
}
class LightOffCommand implements Command {
public function __construct(private Light $light) {}
public function execute(): string {
return $this->light->off();
}
}Инициатор (Invoker)
Инициатор запускает команды, не зная деталей их реализации:
class RemoteControl {
public function press(Command $command): string {
return $command->execute();
}
}
$light = new Light();
$remote = new RemoteControl();
echo $remote->press(new LightOnCommand($light)) . "\n";
echo $remote->press(new LightOffCommand($light));Преимущества
Паттерн Команда идеально подходит для постановки операций в очередь, реализации функционала отмены или логирования действий. Каждая команда самодостаточна, что позволяет легко добавлять новые команды без изменения существующего кода.
Попробуйте сами
<?php
require_once 'Commands.php';
// Чтение входных данных
$commandType = trim(fgets(STDIN)); // "write" или "clear"
$text = trim(fgets(STDIN)); // текст для команды write
// TODO: Создать экземпляр TextEditor
// TODO: На основе типа команды создать соответствующий объект команды
// - Если "write", создать WriteCommand с редактором и текстом
// - Если "clear", создать ClearCommand с редактором
// TODO: Создать механизм инициатора (invoker), который вызывает execute() для команды
// и выводит результат
// TODO: Вывести текущее содержимое редактора в формате: "Content: [content]"
// (если пусто, просто вывести "Content: " без текста после пробела)
?>В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово $thisМетодыСвойстваКонструктор (__construct)Деструктор (__destruct)Итоги — Простой калькулятор4Наследование
Основы наследованияКлючевое слово parent::Переопределение методовКлючевое слово finalАбстрактные классыИтоги — Иерархия сотрудников7Инкапсуляция
Public, Protected, PrivateПодробный разбор модификаторов доступаГеттеры и сеттерыСокрытие данныхConstructor Promotion (8.0)Итоги — Система учета студентов10Продвинутые концепции ООП
Композиция против наследованияВнедрение зависимостейАнонимные классыПеречисления (PHP 8.1)Файберы (PHP 8.1)Глубокое погружение в клонирование объектовГенераторы и итераторы13Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий2Пространства имен и автозагрузка
Введение в пространства именКлючевое слово useСтандарт автозагрузки PSR-4Автозагрузчик ComposerИтоги: структурированный проект5Интерфейсы и контракты
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовИнтерфейс против абстрактного классаType Hinting с интерфейсамиИтоги — Калькулятор фигур8Магические методы
Введение в магические методы__toString и __debugInfo__get, __set, __isset, __unset__call и __callStatic__clone и клонирование объектов__serialize и __unserializeИтоги — Кастомная коллекция11Система типов и обработка ошибок
Объявление типовNullable-типыТипы объединения и пересеченияКлассы исключенийИерархия пользовательских исключенийTry, Catch, FinallyИтоги — Валидатор форм14Проект: Управление библиотекой
Обзор проектаКлассы Book и User3Свойства классов
Свойства экземпляра и статические свойстваКонстанты в классахСтатические методы и свойстваПриватные и защищенные свойстваСвойства Readonly (PHP 8.1)Итоги — Менеджер банковского счета6Полиморфизм
Снова о переопределении методовПолиморфизм через интерфейсыType Hinting и Union TypesПозднее статическое связываниеИтоги — Payment Processor9Трейты
Введение в трейтыИспользование нескольких трейтовРазрешение конфликтов трейтовАбстрактные методы в трейтахТрейты и наследование12Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy