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

29 января 2020
Шаблон предлагает связать инициатора действия и его выполнителя с помощью отдельного класса-команды. Тем самым логика работы с выполнителем действия перемещается из инициатора или клиентского кода внутрь класса-команды. Это позволяет упаковать действие в объект и хранить его, добавлять и извлекать из очереди. 

Шаблон состоит из следующих элементов:
  • Команда (Command) — класс с публичным методом (как правило, метод называется execute()), который напрямую работает с Выполнителем действия;
  • Инициатор действия (Invoker) — класс или клиентский код, который инициирует выполнение действия с помощью Команды и её метода execute();
  • Выполнитель действия (Receiver) — класс, который непосредственно производит выполнение действия.

Пример задачи: сделать поочерёдную перезагрузку серверных служб (почтовый сервис, база данных, мемкэш).

Код реализации паттерна на языке PHP. Скачать исходники (ZIP, 3 Кб)

commands.classes.php

Содержит классы Команд с объявленным методом execute(), внутри которого выполнение действия (перезагрузка службы) передаётся Выполнителю.

Классы внутри файла:
  • Command — абстрактный класс с объявленным методом execute(). Является родительским методом для других классов-команд;
  • CommandMailServiceReset — класс-команда по перезагрузке почтового сервера. Здесь метод execute() уже определён. Внутри идёт обращение к Выполнителю действия, которым является класс MailService, находящийся в файле mail_service.class.php;
  • CommandDBReset — класс-команда по перезагрузке базы данных. Здесь метод execute() тоже определён. Внутри идёт обращение к Выполнителю действия, которым является класс DB, находящийся в файле db.class.php;
  • CommandMemcachedReset — класс-команда по перезагрузке системы кэширования Memcached. Метод execute() определён. Внутри идёт обращение к Выполнителю действия, которым является класс CustomMemcached, находящийся в файле custom_memcached.class.php.
<?

/**
 * Абстрактный класс команды
 */
abstract class Command
{
    /**
     * Выполняет команду (действие)
    * @return void
     */
    abstract public function execute();
}

/**
 * Команда по перезагрузке почтового сервера
 */
class CommandMailServiceReset extends Command
{
    public function execute()
    {
        require_once(__DIR__.'/mail_service.class.php');
        $mailService = new MailService();
        $mailService->reset();
    }
}

/**
 * Команда по перезагрузке базы данных
 */
class CommandDBReset extends Command
{
    public function execute()
    {
        require_once(__DIR__.'/db.class.php');
        $db = new DB();
        $db->reset();
    }
}

/**
 * Команда по перезагрузке кэш-сервиса Memcached
 */
class CommandMemcachedReset extends Command
{
    public function execute()
    {
        require_once(__DIR__.'/custom_memcached.class.php');
        $customMemcached = new CustomMemcached();
        $customMemcached->reset();
    }
}

?>

mail_service.class.php

Класс для работы с почтой. Содержит метод reset() для перезагрузки почтового сервиса.
<?
/**
 * Почтовый сервис
 */
class MailService
{
    /**
     * Запускает работу почтового сервиса
     */
    public function start()
    {
        echo 'Почтовый сервис запущен';
        echo '<br>';
    }

    /**
     * Перезапускает работу почтового сервиса
     */
    public function reset()
    {
        echo 'Почтовый сервис перезапущен';
        echo '<br>';
    }
}

?>

db.class.php

Класс для работы базой данных. Содержит метод reset() для перезагрузки БД.
<?
/**
 * Класс для работьы с БД
 */
class DB
{
    /**
     * Запускает работу сервера БД
     */
    public function start()
    {
        echo 'БД запущена';
        echo '<br>';
    }

    /**
     * Перезапускает работу сервера БД
     */
    public function reset()
    {
        echo 'БД перезапущена';
        echo '<br>';
    }
}

?>

custom_memcached.class.php

Класс для работы с системой кэширования Memcached. Содержит метод reset() для перезагрузки службы Memcached.
<?
/**
 * Класс для работьы с кэш-системой Memcached
 */
class CustomMemcached
{
    /**
     * Запускает работу сервера Memcached
     */
    public function start()
    {
        echo 'Memcached запущен';
        echo '<br>';
    }

    /**
     * Перезапускает работу сервера Memcached
     */
    public function reset()
    {
        echo 'Memcached перезапущен';
        echo '<br>';
    }
}

?>

process_queue.class.php

Содержит класс ProcessQueue, реализующий очередь процессов. Класс используется для добавления команд в очередь с помощью метода addCommand($command). Обход очереди и исполнение процессов осуществляется методом run(). В рамках шаблона класс ProcessQueue является Инициатором действий.
<?
//Подключение классов команд
require_once(__DIR__.'/commands.classes.php');

/**
 * Класс "Очередь процессов"
 */
class ProcessQueue
{
    /**
     * Очередь, хранящая объекты команд
     * 
     * @var array
     */
    protected $queue;

    /**
     * Добавляет команду в очередь
     * @param command $command - объект команды
     * @return bool - в случае успешного добавления команды в очередь метод вернёт 'true', в противном случае вернёт 'false'
     */
    public function addCommand($command)
    {
        if ($command instanceof Command) {
            $this->queue[] = $command;
            return true;
        } else {
            return false;
        }
    }

    /**
     * Запускает выполнение команд и процессов из очереди
    * @return void
     */
    public function run()
    {
        foreach ($this->queue as $z => $process) {
            //Выполняем процесс
            $process->execute();

            //Удаляем процесс из очереди
            unset($this->queue[$z]);
        }
    }
}
?>

test.php

Тестовый скрипт, в котором для перезагрузки серверных служб создаются объекты команд $commandMailServiceReset, $commandDBReset и $commandMemcachedReset. Затем объекты добавляются в очередь процессов $processQueue и далее происходит вызов метода run(), который поочерёдно обходит команды из очереди, вызывая у каждой из них метод execute().
<?
//Подключение классов команд
require_once(__DIR__.'/commands.classes.php');

//Подключение класса "Очередь процесов"
require_once(__DIR__.'/process_queue.class.php');

//Создаём объект-команду для перезагрузки почтового сервера
$commandMailServiceReset = new CommandMailServiceReset();

//Создаём объект-команду для перезагрузки БД
$commandDBReset = new CommandDBReset();

//Создаём объект-команду для перезагрузки Memcached
$commandMemcachedReset = new CommandMemcachedReset();

//Добавляем команды в очередь процессов
$processQueue = new ProcessQueue();
$processQueue->addCommand($commandMailServiceReset);
$processQueue->addCommand($commandDBReset);
$processQueue->addCommand($commandMemcachedReset);
$processQueue->run();

?>

Результат работы скрипта test.php
Почтовый сервис перезапущен
БД перезапущена
Memcached перезапущен