Фабричный метод (Factory Method) — шаблон проектирования
16 января 2020
Шаблон определяет в родительском классе общую схему создания объекта и даёт дочерним классам право самостоятельно решать, объект какого класса им нужно создавать. Тем самым, в родительском классе задаётся абстрактная логика, а в дочерних классах реализуется конкретная логика.
В шаблоне имеются две группы классов – фабрики (создатели) и продукты (результат работы фабрик/создателей). У каждой группы есть слой абстракции, который реализуется в виде интерфейсов и абстрактных классов. Фабрики создают объекты продуктов с помощью метода, который так и называется – фабричный метод.
Далее приводится реализация паттерна на языке PHP (скачать исходники — ZIP, 2 Кб)
Результат работы скрипта test.php:
В шаблоне имеются две группы классов – фабрики (создатели) и продукты (результат работы фабрик/создателей). У каждой группы есть слой абстракции, который реализуется в виде интерфейсов и абстрактных классов. Фабрики создают объекты продуктов с помощью метода, который так и называется – фабричный метод.
Пример задачи: запись логов на разные носители (вывод на экран, запись в файл на локальном диске, сохранение в базу данных).
Далее приводится реализация паттерна на языке PHP (скачать исходники — ZIP, 2 Кб)
log_classes.php
Cодержит классы для работы с логами. Объекты данных классов будут являться продуктами фабрик. Перечень классов:- Log – абстрактный класс, который описывает абстрактную логику для работы с логами. Класс объявляет метод write для записи лога, а так же объявляет и определяет метод outNotificatonSuccess для вывода уведомления об успешном действии по записи лога (метод чисто демонстративный);
- Log2Console, Log2DB, Log2File – классы, которые на основе абстрактного класса Log реализуют конечную логику по работе с логами. В частности, каждый класс по-своему определяет метод write (у Log2Console лог выводится в консоль, у Log2DB лог пишется в БД, а у Log2File лог пишется в файл).
<?
/**
* Абстрактный класс для работы с логами
*/
abstract class Log
{
/**
* Записывает сообщение с логом
* @param string $message - текст с сообщением лога
* @return bool - вернёт true в случае успешной записи лога и false в противном случае
*/
abstract public function write($message);
/**
* Выводит уведомление об успешном выполнении действия
*/
public function outNotificatonSuccess()
{
echo 'Действие успешно выполнено ('.get_class($this).')'."\n";
}
}
/**
* Класс для вывода лога на экран
*/
class Log2Console extends Log
{
/**
* Выводит на экран сообщение с логом
*
* @param string $message - текст с сообщением лога
* @return bool
*/
public function write($message)
{
echo $message."\n";
$this->outNotificatonSuccess();
return true;
}
}
/**
* Класс для записи лога в БД
*/
class Log2DB extends Log
{
/**
* Записывает сообщение с логом
*
* @param string $message - текст с сообщением лога
* @return bool - вернёт true в случае успешной записи лога и false в противном случае
*/
public function write($message)
{
//Добавление записи в логом в таблицу базы данных
//...
//Вывод уведомления об успешном выполнении действия
$this->outNotificatonSuccess();
}
}
/**
* Класс для записи лога в файл
*/
class Log2File extends Log
{
/**
* @var string $filePath - путь к файлу, в конец которого будет записана строка с сообщением лога
*/
private $filePath = '';
/**
* Задаёт путь к файлу, в который будут писаться логи
* @param string $filePath - путь к файлу, в конец которого будет записана строка с сообщением лога
*/
public function setFilePath($filePath)
{
$this->filePath = $filePath;
}
/**
* Записывает сообщение с логом в конец файла
*
* @param string $message - текст с сообщением лога
* @return bool - вернёт true в случае успешной записи лога и false в противном случае
*/
public function write($message)
{
if ($this->filePath == '') {
throw new Exception('Ошибка. Не задан путь к файлу, в который будут писаться логи');
}
//Открытие файла и последуюющая запись строки с логом в конец файла
$fh = fopen($this->filePath, 'a');
if ($fh) {
fwrite($fh, "\n".$message);
fclose($fh);
$this->outNotificatonSuccess();
return true;
} else {
return false;
}
}
}
?>
log_creators.php
Файл с классами фабрик, содержит следующие элементы:- FactoryLog – интерфейс фабрик. В нашем случае интерфейс объявляет только один фабричный метод create;
- CreatorLog2Console – класс фабрики, которая определяет (реализует) фабричный метод create и создаёт продукт класса Log2Console;
- CreatorLog2DB – класс фабрики, которая определяет (реализует) фабричный метод create и создаёт продукт класса Log2DB;
- CreatorLog2File – класс фабрики, которая определяет (реализует) фабричный метод create и создаёт продукт класса Log2File;
<?
//Подключаем классы для работы с логами
require_once(__DIR__.'/log_classes.php');
/**
* Интерфейс для работы с создателями логов
*/
interface FactoryLog
{
/**
* Создаёт и возвращат объект для работы с логами
* @return Log
*/
public function create();
}
/**
* Класс для создания объекта LogConsole по работе с логами, выводящимися на экран
*/
class CreatorLog2Console implements FactoryLog
{
/**
* Создаёт и возвращает объект лога
* @return Log2Console
*/
public function create()
{
return new Log2Console();
}
}
/**
* Класс для создания объекта LogDB по работе с логами, сохраняющимися в базу данных
*/
class CreatorLog2DB implements FactoryLog
{
/**
* Создаёт и возвращает объект лога
* @return Log2DB
*/
public function create()
{
return new Log2DB();
}
}
/**
* Класс для создания объекта LogDB по работе с логами, сохраняющимися в файл
*/
class CreatorLog2File implements FactoryLog
{
/**
* Создаёт и возвращает объект лога
* @return Log2File
*/
public function create()
{
return new Log2File();
}
}
?>
test.php
Скрипт с примером использования логов. Тут фабрики CreatorLog2Console, CreatorLog2DB и CreatorLog2File с помощью фабричного метода create создают продукты (объекты) по работе с логами и далее в каждом объекте вызывается метод write для записи лога. Обратите внимание, что у объекта класса CreatorLog2File дополнительно вызывается метод setFilePath для указания файла, в который будет записываться лог.<?
//Подключаем классы для создания объектов по работе с логами
require_once(__DIR__.'/log_creators.php');
$log = CreatorLog2Console::create();
$log->write('Тестовая запись лога 1');
$log = CreatorLog2DB::create();
$log->write('Тестовая запись лога 2');
$log = CreatorLog2File::create();
$log->setFilePath(__DIR__.'/journal.txt');
$log->write('Тестовая запись лога 3');
?>
Результат работы скрипта test.php:
Тестовая запись лога 1
Действие успешно выполнено (Log2Console)
Действие успешно выполнено (Log2DB)
Действие успешно выполнено (Log2File)