ОП, или кто тоже разбирается. подскажите вот в чем:Есть приложение, просто роутер который вызывает обработчики.В обработчике/контроллере я хочу что то сделать, допустим записать лог в файл.Пусть у меня нет сервис локатора, или DI контейнера.Как я реализую подобное:допустим метод класса обрабатывает запрос...public function anyMethod(){//создаю фабрику сервисов Логгирования//которая принимает аргументом конкретный сервис ( MonologLoggerService ), который имплементирует общий для всех логгинг-сервисов интерфейс, что бы методы в контроллерах были одни и те же, и я смог легко поменять один сервис логгирования на другой.$serviceFabric = new ServiceFabric ( new MonologLoggerService );$serviceLogger = $serviceFabric->getInstanseLogger();$serviceLogger->warning('что нибудь пишу в лог');}}Если абстрагироваться от контейнеров и сервис локатора - такой подход адекватен?Это фактически мои первые потуги в рамках ООП. Получается в моем варианте фабрика - это не совсем то что должно быть?Фабрика возвращать должна один и тот же экземпляр одного конкретного класса - просто с разными настройками?Или фабрика может возвращать экземпляры разных классов , объединенных общим интерфейсом или абстрактным классом?
>>1965889Если ты решил спроектировать что-то, то ты должен ответить на вопрос: зачем нужен тот или иной класс? Не проще ли без него? Вот например, зачем ты сделал фабрику логгеров? Что тебе мешает сразу создать логгер через new MonlogLoggerService? > Если абстрагироваться от контейнеров и сервис локатора - такой подход адекватен?Я не понимаю, зачем ты сделал фабрику.> Фабрика возвращать должна один и тот же экземпляр одного конкретного класса - просто с разными настройками?> Или фабрика может возвращать экземпляры разных классов , объединенных общим интерфейсом или абстрактным классом?Бывает и так, и так. Фабрику обычно добавляют для решения таких проблем: - чтобы не копипастить в несколько мест сложный код создания объекта- в библиотеках, чтобы пользователь бибилиотеки мог повлиять на процесс создания объекта, подменив в ней фабрику на своюНу и вопрос: чем тебе не подходит DI? То есть, просто передавать логгер в конструктор твоего класса? Зачем ты начал изобретать какие-то странные решения?
>>1966063>>Вот например, зачем ты сделал фабрику логгеров? Что тебе мешает сразу создать логгер через new MonlogLoggerService?Изначально вообще у был некий общий сервис логгеров, а в нем в конструкторе был захардкоден вызов конкретного допустим MonlogLoggerService. Идея была в том что бы я в общем сервисе логинов мог менять конкретный логгер, а так как в клиентском коде везде вызывается общий сервис - то можно было поменять логер в одном месте. Фактически как в контейнере хранить сервис.Но прочел что не комильфо в конструкторе определять зависимости и родилась идея передавать конкретный сервис в конструктор.Более того - если в ходе выполнения программы мне нужно что бы в одном случае один сервис логгинга вызывался, а в другом другой - то вариант захардокоденного вызова конкретного сервиса не даст это сделать в принципе.>>Бывает и так, и так. Фабрику обычно добавляют для решения таких проблем:>>Ну и вопрос: чем тебе не подходит DI? Я в первую очередь решал такую для себя проблему - получить возможность быстрой замены конкретной реализации какого то сервиса во всем клиентском коде в приложении. И я специально не пользуюсь DI - контейнерами, или банальным сервис-локатором. Хочу лучше прочувствовать что дает не правильный подход. Мне как то проще сделать криво, столкнуться со всеми минусами кривого решения, и тогда я по настоящему понимаю реальные плюсы правильного подхода. >>Не видя кода ServiceFabric, нельзя сказать, зависит он от MonologLoggerService или нет. На первый взгляд, он зависит, так как требует его передачи в конструктор.А как же в таком случае должен выглядить полностью не зависимый ServiceFabric?А как поправить мой пример кода так что бы стало более правильно?И вот вопрос тогда, вот в DI контейнере я определил сервис, LogService::class. И в клиентском коде я получаю этот объект из контейнера. А после я решил что в контейнере LogService::class должен быть иной логгер, не монолог. Я просто руками в контейнере меняю Монолог на что то другое, и все?Или есть иной вариант.Сорр если туплю, я Зандстру начал читать , разделы про ООП, пока что все немного в кучу. У него сервис локатор это класс, который через статические методы получает и возвращает какие то объекты - паттерн Регистр примерно так.
>>1966437То есть, ты хотел иметь несколько логгеров и легко заменять один на другой. Это правильно реализуется так:- определяем интерфейс логгера LoggerInterface- делаем несколько логгеров, реализующих этот интерфейсДалее есть несколько вариантов, как это использовать:1) с помощью DIВ конструкторе объявляем зависимость от LoggerInterface, сохраняем его в $this->logger, и далее в коде используем его: $this->logger->warning("Yes");Разумеется, вместо ручного прокидывания логгера в конструктор можно использовать DI контейнер, который сделает это за тебя.Получается, наш класс готов работать с любым логгером, который ему дадут в конструктор.2) с помощью ServiceLocatorДелаем в ServiceLocator метод getLogger(): LoggerInterface и используем его: $this->locator->getLogger()->warning("Yes");Здесь для подмены логгера надо лишь заменить единственную функцию getLogger().Про минусы сервис локатора (и про плюсы DI) можно прочесть у меня в уроке про DI: https://github.com/codedokode/pasta/blob/master/arch/di.md3) с помощью Registry - создаем логгер, кладем в Registry, а в других местах берем его оттуда.Заметь, что ни один из этих подходов не требует писать фабрику. Мне кажется, она в данной ситуации не нужна.-----Подход с интерфейсом работает, если ты пишешь сам код логгеров. Если ты используешь сторонние библиотеки, ты не можешь заставить их соответствовать твоему интерфейсу. В таком случае ты пишешь интерфейс LoggerInterface и адаптеры (вроде MonologAdapter), которые соответствуют интерфейсу и которые передают сообщения сторонней библиотеке.-----Теперь про минусы твоего кода: - ты копипастишь код создания фабрики много раз (каждый раз, когда тебе нужен логгер), а копипаста это плохо (если придется что-то менять, то придется менять в куче мест). Нарушаешь DRY = Dont Repeat Yourself.- у тебя, чтобы поменять тип логгера, надо его менять в куче мест- у тебя каждый раз с помощью new создается новый объект фабрики и новый объект логгера. Это может вызвать проблемы, например, если логгер открывает соединеие с БД, то каждый новый логгер будет открывать новое соединение и ты упрешься в лимит на число соединений. Правильнее использовать один объект логгера везде, а не создавать каждый раз новый. Или другой пример: ты хочешь не печатать в лог сообщение, если оно совпадает с предыдущим. Ты не сможешь это сделать, так как каждый раз ты создаешь новый объект логгера, который ничего не знает о предыдущем логгере и предыдущем сообщении.> И вот вопрос тогда, вот в DI контейнере я определил сервис, LogService::class. И в клиентском коде я получаю этот объект из контейнера. А после я решил что в контейнере LogService::class должен быть иной логгер, не монолог. Я просто руками в контейнере меняю Монолог на что то другое, и все?Поменять так просто ты не можешь, так как ты в куче месте в коде прописал LogService и код ждет именно этот класс.Надо делать так: ты объявляешь логгер в контейнере под именем LoggerInterface, и везде в коде просишь LoggerInterface::class. А в контейнере задаешь, какой именно класс будет использован. ----На самом деле, тут есть еще второй вариант, без интерфейсов. Ты можешь сделать класс-посредник, LoggerService, вся суть которого в том, что он передает сообщения конкретной бибилиотеке (например, Monolog). Везде ты внедряешь объект этого класса в качестве логгера. Тогда для замены логгера тебе будет достаточно поменять код в LoggerService.