Как архитектурно лучше решить такую проблему?
У меня есть класс логгера, которые можно конфигурировать разными способами, например писать в разные хранилища, писать разными форматами и т.д. (можно представить себе, что это Monolog)
В проекте, очень часто нужно писать в файловых лог, и каждый раз создавать объект заново и конфигурировать его - не хорошо.
Например:
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
$logger = new Logger('my_logger');
$logger->pushHandler(new StreamHandler(__DIR__ . '/app.log', Logger::WARNING));
$logger->pushHandler(new StreamHandler(__DIR__ . '/app.log', Logger::DEBUG, $bubble = true));
$logger->pushProcessor(function ($record) {
$record['extra']['cookies'] = $_COOKIE;
return $record;
});
как минимум - это дублирование кода + неудобно.
Значит нужно вынести создание объекта а отдельный метод. Но куда ? Если создать метод в самом логгере - это не его зона ответственности, завтра, может унжен будет еще один метод, для логирования в rabbitmq.
Как быть ? Создать отдельный класс-фабрику ? Но разве идея фабрики не в том, что она может создавать объекты одного интерфейса ? а тут у нас фабрика все время возвращает один и тот же объект, только в разных конфигурациях ?
Какие вообще есть решения ?
Ответы (1 шт):
используйте dependency injection
// 1 делаем наш логгер синглтоном
// в каждом фреймворке это делается по-своему(ну или реально паттерном)
// пример yii
// common/config/main.php
'singletons' => [
LoggerInterface::class => [
'class' => YourLoggerClass::class
'param1' => 'val1',
'param2' => 'val2',
// ......
],
],
// 2 протягиваем его в класс через DI механизм фремворка
// 2.1 через конструктор(более классический вариант)
public function __construct(LoggerInterface $logger){
$this->logger = $logger;
}
// 2.2 через контейнер(в каждом фрейме по своему)
public function doSth()
{
/** @var LoggerInterface $logger */
$logger = Yii::$container->get(LoggerInterface::class);
// код .....
// код .....
// код .....
}