Попробуем разобраться с шаблонами проектирования программного обеспечения.
Чаще всего под шаблонами проектирования понимают некую часть программы, которая должна выполнить часто повторяющуюся задачу. Для таких частых, стандартных задач были написаны алгоритмы и, даже, специальная книга "Design Patterns".
Вот об этих шаблонах или паттернах, описанных в книге, и поговорим.
В статье на Википедии предлагают следующую классификацию шаблонов:
Мне кажется, что самый простой и популярный шаблон, который можно разобрать в первой статье - это Порождающий шаблон "Фабричный метод". Основная идея его в том, что наша программа будет создавать разные классы в зависимости от некоторых условий. Причем, во время разработки программы, мы не будем знать какой класс нам будет необходим. И программа должна будет определить и создать класс сама. Да, объяснить без примеров это не просто, поэтому посмотрим на код.
Фреймворк
Для того, чтобы написать программу использующую паттерны, я решил создать небольшой проект и попробовать внедрить шаблоны так, чтобы практическая польза от них была как-то заметна. Проект - это элементарный телеграм бот. В который можно написать любое сообщение и получить ответ от бота. Для реализации бота я использовал простой php фреймворк с сайта code.mu на котором есть много полезных уроков и заданий.
Код проекта можно найти на гитхабе, ведь редактор текста на Пикабу не позволяет форматировать код для удобного чтения.
А бот можно найти в телеграме, написать ему сообщение и проверить работу нашего Фабричного метода.
Код проекта
Идея программы очень простая - вы пишите сообщение телеграм боту. Бот отправляет callback сообщение на бекэнд нашего приложения. И в зависимости от текста этого сообщения, код на бекэнде определяет какое сообщение отправить боту обратно, чтобы вывести его в телеграм клиенте.
Сообщение от телеграма приходит на роут, который мы указали при создании бота в BotFather. А во фреймворке создали этот роут в "\project\config\routes.php"
use \Core\Route;
return [
new Route('/telegram/:var1/', 'telegram', 'index'), // роут для telegram bot
];
Роут находит Telegram Controller и выполняет метод index. В этом методе мы выполняем ряд проверок. И если сообщение пришло от нашего телеграм бота, передаем сообщение в Фабричный метод, чтобы создать нужный объект и получить обратное сообщение для бота.
$ms = new MessageFactory($params);
$msObject = $ms->create();
В классе MessageFactory есть два метода. В конструкторе мы разбираем параметры сообщения и пытаемся получить текст сообщения от телеграма и id чата от которого сообщение пришло.
А в методе create мы проверяем существует ли класс для конкретного сообщения и если существует создаем объект этого класса и возвращаем его в контроллер. Если же класса нет, значит мы должны создать дефолтный класс.
public function __construct(array $params)
{
$this->command = preg_replace('/\//', '_', $params['message']['text']??'sdsfsdf');
$this->params['chatId'] = $params['message']['chat']['id']??0;
}
public function create()
{
$className = ucfirst($this->command);
$messageFile = $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . "project/classes/messagefactory/messages/Message$className.php";
$messageClass = $this->namespace."Message$className";
if(file_exists($messageFile) && class_exists($messageClass)){
return new $messageClass($this->params);
} else {
return new MessageDefault($this->params);
}
}
Для каждого конкретного сообщения, которое мы хотим обработать на бекэнде по уникальному сценарию, мы должны создать класс реализации. Например, класс "\project\classes\messagefactory\messages\MessageTest.php"
Этот класс расширяет абстрактный класс MessageAbstract и реализует интерфейс IMessage. class MessageTest extends MessageAbstract implements IMessage {}
У него есть только конструктор, в котором мы вызываем конструктор родителя и заполняем свойство $message = 'Test Message'. Соответственно, если мы напишем в телеграм бот сообщение 'test', то наш Фактори метод создаст класс MessageTest, в свойстве $message у него будет строка 'Test Message' и контроллер отправит эту строку обратно в телеграм бот.
Что если
Что если бы мы не использовали все эти модные паттерны, классы и ооп. Конечно, такую программу можно реализовать и без ооп, возможно это даже проще. Но тогда описать обработку каждого сообщения придется в блоках if/else или switch/case. На первом этапе это будет не трудно. Просто напишем нужную строку для каждого case. Но, что если логика реакции на каждое сообщение будет сложной. Если при сообщении '/start' мы захотим зарегистрировать пользователя в системе и добавить его в базу данных. А при дефолтном сообщении создать еще несколько вариантов сценариев. Тогда наш контроллер будет огромного размера и поддерживать такую программу станет слишком сложно.
С другой стороны, если создать фабрику сообщений, то нам нужно только добавить отдельный файл сообщения в соответствующую директорию и все остальное сделает наш Фактори метод.
Вместо заключения. Это первый шаблон проектирования ПО который мы рассмотрели. Впереди еще много работы и полезных штук, поэтому... Спасибо всем, кто смог дочитать этот пост до конца несмотря на ужасное оформление, примеры на php и общую безграмотность. Отдельное спасибо всем, кто заходит и подписывается на ютуб канал. Я начинаю готовить видео на тему Шаблонов проектирования. Может быть примеры кода на видео будут более понятны.
Что рассмотрим в следующий раз?