В своем проекте я использую популярную библиотеку для логирования - Monolog, которая имеет достаточно много вариантов использования.
Для своего проекта, на текущем этапе, мне нужно иметь 2 вида логирования:
1. Логирования в файл
2. Отправка логов в очередь rabbitmq
В разных частях приложения нужен один из этих логгеров, или одновременно оба.
Сам экземпляр логгера конфигурируется (конфигурация уровней логирования, источников, форматов и т.д.) достаточно сложно, и чтобы каждый раз не конфигурировать его, логично инкапсулировать создание объекта в отдельный класс.
Т.е. нам нужно решить две задачи:
1. Упростить интерфейс логгера, и его конфигурирование. Т.е. это явно паттерн Фасад.
2. Нужные разные типы логгеров. Сейчас нужно два типа, потом будет больше. Тут скорее всего нужен паттерн Фабрики.
Какой именно паттерн подходит для моей задачи не пойму, можно ли их комбинировать ? или они могут исключать друг друга ?
В голову приходит два варианта решения:
1. Использовать паттерн Фасад: для каждого типа логгера свой Фасад. Оба Фасада будут иметь единый интерфейс (с методом добавления в лог).
2. Использовать паттер Фабричный метод, только как именно его задействовать не пойму.
Подскажите пожалуйста, какой вариант предпочтительнее ?
И если можно пример второго варианта, как на практике это делается ?
один объект должен делать одну вещь, значит фасад не подходит. Усложнять логгер ни к чему, пусть логирует только в файл или только в рэбит. Используйте два логгера, вот и все. Это проще, это более явно. Я бы сделал так:
// Some code
$logger = Logger::instance();
$rabbitLogger = RabbitLogger::instance();
Вообще-то в монологе есть каналы для этого https://github.com/Seldaek/monolog/blob/master/doc...
А так же собственно, кастомные хендлеры/процессоры. В коде ты используешь стандартный логгер интерфейс, а что куда писать и слать разруливается кофигурацией монолога через di. А если его нет, то стоит задуматься именно о нём, особенно если в перспективе еще какие-то другие форматы и способы логгирования понадобятся - всё будет разруливаться в едином месте через доп. хендлеры/форматтеры и каналы при конфигурации логгера.
по поводу di, у меня тоже была мысль, но не до конца понимаю, как его правильно использовать.
в нормальных mvc фреймворках есть единая точка входа в приложения, насколько я понимаю, обычно именно там и инициализируют di контейнер.
1. у меня нет единой точки входа в приложения (особенность нашей cms), их может быть много
2. я не понимаю какая область видимости должна быть в di-контейнера (например тот же простой Pimple).
Наверное, нужно инициализацию контейнера вынести в отдельный фал, например di_init.php,
и уже в нем инициализировать контейнер.
И дальше в нужных точках входа, подключить файл, и использовать контейнер.
да, если точек входа несколько, то нужно подключить файл с инициализацией контейра в каждый из них.
Про область видимости - не совсем понятен вопрос. Ваша cms использует composer, psr-4 или psr-0 ?
Если там много построенного на функциях а не на классах, то веротяно придётся объявлять как global
А так, я больше предпочитаю https://github.com/thephpleague/container или symfony которые умеют autowire и lazy load, pimple насколько я помню не умеет. В любом случае трудно что-то порекомендовать не имея представления о проекте
Т.е. никаких global, и singleton не нужно выдумывать ?
И насколько корректно в одном файле с инициализацией контейнера, добавлять в контейнер зависимости разных подсистем ?
Получится так, что буду добавлять в контейнер все зависимости, но при использовании в одной из точек входа, часть зависимостей просто не нужна, т.к. они из другой подсистемы, или в других модулях нужны..
topuserman да, так должно работать, только в вашем примере больше похоже, что вы его как сервис-локатор хотите использовать.
По поводу синглтонства - это непосредственно доку конкретной реализации контейнера нужно смотреть - многие поддерживают возможность задать shared или не shared мод в зависимости от которого будет возвращаться каждый раз новый инстанс класса, или тот же самый как будто это синглтон, без необходимости делать вызываемый класс синглтоном
О том что целесообразнее в вашем случае мне сказать трудно. Возможно в вашем случае будут какие-то общие зависимости для всей системы и какая-то часть только для модулей. Если cms подразумевает какую-то модульность, то вероятно стоит предусмотреть интерфейс чтоб каждый модуль предоставлял конфиг со своими зависимостями и ядро системы при использовании модуля регистрировало его зависимости.
Но в целом не стоит бояться того, что придётся регистрировать слишком много зависимостей или избыточные - если зависимость не вызывается, инстанс для нее создаваться не будет
В данном случае, вы запутались в паттернах, потому что на самом деле они вам и не нужны. Просто сделайте, как считаете нужным, без переусложнения. Если окажется, что работать с получившейся обвязкой неудобно — отрефакторите.
Как я уже говорил, чтобы оценить архитектуру нужно иметь базовые навыки ее создания, а у тебя их нету. Поэтому ты даже не понимаешь того, о чем это книга. Что такое паттерны программирования, и какую проблему они пытались решить, но не решили.
Паттерны это сборник примеров того, как можно сделать. Не образец для подражания, не набор готовых блоков.
Если обратиться к реальной архитектуре, в смысле домов, то мы увидим что из готовых блоков можно построить хрущевку, в 3 этажа или в 5, в 5 подъездов или в 10. Но ничего кроме хрущевки нельзя. Ни мост, ни любое другое нормальное здание. Только тупую панельку.
Вот тоже и с паттернами. Паттерны не подменяют и не заменяют умение проектировать. Да, они задумывались для этого, но эта попытка провалилась. Даже если ты из лего замок строишь, нужно понимать что ты делаешь, а не бездумно блоки совать.
Одно из решений, которые я предположил - это упрощение.
Инкапсулировать инстанцирование логгера и его конфигурацию в отдельный класс. И так для каждого типа логгера.
Оба предположительных решения описаны в вопросе.
Вы можете это называть паттерном, или чем-то иным, не важно.
Я описал какие у меня есть исходные данные и как будет в будущем расширяться.