У меня есть один вопрос: мне нужно настроить мой DI / IoC-контейнер для каждого объекта в приложении или просто настроить фабрики?
Теперь у меня есть что-то вроде этого:
'serviceFactory' => function() use ($container) {
return new \Application\Core\Factory\ServiceFactory($container->get('entityFactory'), $container->get('repositoryFactory'), $container->get('cache'), $container->get('file'), $container->get('image'));
},
'repositoryFactory' => function() use ($container) {
return new \Application\Core\Factory\RepositoryFactory($container->get('database'), $container->get('queryBuilder'), $container->get('mapper'), $container->get('language'));
},
'entityFactory' => function() use ($container) {
return new \Application\Core\Factory\EntityFactory($container->get('language'));
},
Но затем приложение загружает ненужные объекты в другие ненужные объекты.
Пример: в BlogService я не использую файл класс или Образ учебный класс. В ThumbnailService Я делаю, но я не использую сущность или же вместилище внутри него.
Итак, нужно ли мне импортировать ленивую загрузку внутри моего DI / IoC-контейнера или мне нужно записывать каждое соединение Entity / Repository / Service / Controller следующим образом:
'blogController' => function() use ($container) {
return new \Application\Controller\BlogController($container->get('blogService'));
},
'blogService' => function() use ($container) {
return new \Application\Service\BlogService($container->get('blog'), $container->get('categoryEntity'), $container->get('blogRepository'), $container->get('cache'));
},
'blogRepository' => function() use ($container) {
return new \Application\Model\Repository\BlogRepository($container->get('database'), $container->get('queryBuilder'), $container->get('mapper'), $container->get('language'));
},
'blog' => function() use ($container) {
return new \Application\Model\Entity\Blog($container->get('language'));
},
'thumbnailService' => function() use ($container) {
return new \Application\Service\ThumbnailService($container->get('image'), $container->get('file'));
},
Но таким образом я мог бы написать все 50-100 сущностей, репозитории, сервисы и контроллеры, может быть, чрезмерная оптимизация?
Какие-либо предложения ?
Нет, обычно вам нужно регистрировать только те типы, которые могут различаться, т.е. конкретные реализации абстракции. Если вы не используете абстракции, тогда DI-контейнер не так уж полезен.
Контейнер DI должен позволять регистрировать типы с использованием соглашений, например, регистрировать все классы с именем, нужным в «Сервисе», в качестве сервисов. Таким образом, вы просто определяете некоторые «правила», и регистрация выполняется автоматически, независимо от того, сколько сервисов у вас в приложении. По крайней мере, так обстоят дела в .net, но написание вручную регистрации 50-100 классов — это запах кода.
Обновить
Это должно выглядеть так
interface ISomeService {}
class MyService implements ISomeService { }
class OtherService
{
private $svc;
function __construct(ISomeService $svc)
{
$this->svc=$svc;
}
function doStuff()
{
//do stuff using $this->svc
}
}
//DiContainer
//here you you should register things (I don't know a container for php, I'll write what I'm using in C#, but you'll get the idea)
container.RegisterTypes(myAssembly)
.Where(type=>type.Name.EndsWith("Service"))
.AsSelf()
.AsImplementedInterfaces();
Приведенный выше код указывает контейнеру сканировать все классы с именем, оканчивающимся на «Service» (мое соглашение), и автоматически регистрировать их для использования, когда запрашивается либо этот конкретный тип, либо реализация интерфейсов, реализованных этим типом.
В нашем примере это означает, что когда OtherService запрашивается из контейнера, контейнер знает, как использовать MyService тип как конкретный тип для ISomeService . Контейнер просматривает ваш конструктор, чтобы определить, какие зависимости нужны объекту.
Чтобы правильно использовать DI-контейнер, ваша инфраструктура (mvc) должна знать об этом, поскольку она не предназначена для непосредственного использования вашим приложением. Есть случаи, когда шаблон Service Locator действителен, но обычно фреймворк должен использовать контейнер для создания экземпляров услуг, которые вам нужны.
Вы конфигурируете контейнер, когда приложение загружается (как часть или интегрируется с вашей инфраструктурой mvc) и все. Далее вы просто определяете интерфейсы и классы (сервисы, репозитории, обработчики событий и т. Д.). Единственное, что вам нужно сделать, — это придумать некоторые соглашения, позволяющие вашему контейнеру автоматически регистрировать классы на их основе (например, называть ваши сервисы «Сервис»).
DI-контейнеры впервые появились на языках со статической типизацией (java, C #), где их было бы целесообразнее использовать, по сравнению с динамическим языком, где вы не ограничены типом (поэтому я использую хинтинг типов в php). Они все еще полезны, но их немного сложнее понять.
Других решений пока нет …