Шаблоны проектирования в PHP : Команда

Задумывались ли вы, что сейчас люди используют больше 4 миллиардов мобильных устройств? В Австралии население составляет примерно 11 миллионов человек, а количество сотовых телефонов — 22 миллиона — то есть примерно 2 устройства на человека.

Очевидно, что люди все больше и больше пользуются мобильными устройствами. Их повсеместность обуславливает превосходство СМС сообщений над email. СМС имеет ряд преимуществ: такие сообщения коротки, мгновенны, а самое лучшее — довольно мало спама.

А причем же здесь шаблон проектирования Команда?

Давай рассмотрим такой сценарий. У компании есть свой сайт, который ежедневно разыгрывает разные вещи. У неё так же есть база данных, содержащая более 250 000 зарегистрированных пользователей. Каждому из них ежедневно надо отсылать кодовое слово, которое они должны либо ввести либо кликнуть по ссылке, чтобы принять участие в розыгрыше. Большинство пользователей используют email для связи, но всё больше становится сторонников СМС. Вот и проблема — как отсылать сообщения по двум разным каналам обоим группам пользователей?

По логике, следует разделить пользователей на две группы: получатели email и получатели СМС. И отсылать им сообщения по отдельности используя соответствующий тип связи. Используя шаблон Команда (Command) вы сможете отсылать сообщения обоим группам пользователей в одном процессе.

Очередь сообщений с помощью шаблона проектирования Команда (Command)

Этот шаблон проектирования описывает то, как инкапсулировать сообщение в качестве объекта, чтобы его можно было ставить в очередь и сохранять информацию о пользователях с разными типами связи. Для примера, рассмотрим простую реализацию очереди сообщений. Вот описание класса MessageQueue:

<?php
class MessageQueue
{
    private $queue;
    public function __construct()
    {
        $this->queue = [];
    }
    public function addMessage(IMessage $msg)
    {
        $this->queue[] = $msg;
    }
    public function execute()
    {
        $sendCount = 0;
        foreach ($this->queue as $msg) {
            if ($msg->send()) {
                $sendCount++;
            }
        }
        return $sendCount;
    }
}

Эта очередь использует два метода — addMessage(), который ставит сообщения в очередь и execute() — обработчик сообщений в очереди. В нашем примере addMessage() просто добавляет сообщения в массив $queue, а execute() обрабатывает все сообщения в $queue и вызывает метод send() для каждого сообщения.

Шаблон проектирования Команда (Command) ставит каждое сообщение в очередь для дальнейшей обработки. Основная логика отправки email и СМС будет выполнена в методе send(). MessageQueue совсем необязательно знать как обрабатывать запросы, так как эти обязательства делегируются на запрашивающий объект. Класс с методом send() должен реализовать интерфейс IMessage.

<?php
interface IMessage
{
    public function send();
}

Каждый объект сообщения реализует интерфейс IMessage и использует свою реализацию метода send().

<?php
class DailyAlertEmail implements IMessage
{
    // ...
    public function send() {
        // код который будет отсылать данные на имейл
        // ...
        echo "Отправка сообщения по имейлу";
    }
}
class DailyAlertSMS implements IMessage
{
    // ...
    public function send() {
        // код который будет отправлять по SMS
        // ...
        echo "Отправка сообщения через SMS";
    }
}

Сообщение DailyAlertEmail использует метод send() для отправки кодового слова на email, а DailyAlertSMS— отсылает СМС сообщения. Далее, чтобы отправить и СМС, и email вам потребуется из БД получить предпочитаемый способ свзяи с клиентом (т.е в коллекции полученных клиентов в каждом объекте храниться, как именно он хочет получать уведомления), инициализировать подходящий объект IMessage, добавить его в очередь и выполнить метод execute(). Хотелось бы отметить, что создание соответствующего объекта типа IMessage для каждого пользователя — отличный пример использования шаблона проектирования Фабрика.

<?php
// создание новой очереди сообщений
$msgQueue = new MessageQueue();
$result = $db->query("SELECT * FROM customers");
while ($customer = $result->fetch(PDO::FETCH_ASSOC)) {
    // Фабрика создаёт объект DailyAlertSMS или DailyAlertEmail
    $msg = MessageFactory::build($customer, $codeword);
    // добавить объект сообщения в очередь
    $msgQueue->addMessage($msg);
}
// отправить всем заказчикам
$msgQueue->execute();

При помощи шаблона проектирования Команда (Command) вы легко извлекаете всех клиентов из базы данных, инициализируете соответствующий объект IMessage, и независимо от типа связи обрабатываете всех клиентов за раз, вместо того, чтобы сначала получать из БД пользователей для СМС сообщений, а потом для email.

Помните, это базовый пример. В настоящем приложении следовало бы создавать пакеты сообщений (например, пачками по 500) и отсылать их частично в течение всего дня, идеально, если отправка будет происходить в качестве фонового процесса. Всего несколько изменений позволит вам преобразовать этот код в очередь отложенных задач, отсылать сообщения при помощи cron и следить за прогрессом через базу данных.

Заключение

Как вы сами видите, шаблон проектирования Команда отлично подходит для следующих ситуаций:

  • Вы хотите различать объекты по выполняемому действию.
  • Вам требуется указывать, ставить в очередь и выполнять запросы в разное время.
  • В случае когда набор данных требуется инкапсулировать в одно действие (н-р транзакция).

В данном примере я показал как реализовать шаблон проектирования Command для очереди сообщений, где запросы можно ставить в очередь для последовательной обработки. В тоже время отделяя основную логику команд от логики самой очереди.