Предположим, у меня есть группа классов, экземпляры которых должна создавать фабрика. Эта фабрика должна ввести параметр в экземпляр класса.
<?php
class ClassA
{
public function __constructor(
\My\Logger $logger,
\Dependency\For\ClassA $dependencyA2,
\One\More\Dependency\For\ClassA $dependencyA3,
...
string $parameterInjectedByTheFactory
) {
$this->logger = $logger;
$this->dependencyA2 = $dependencyA2;
...
$this->parameterInjectedByTheFactory = $parameterInjectedByTheFactory;
}
}
а также
<?php
class ClassB
{
public function __constructor(
\My\Logger $logger,
\Dependency\For\ClassB $dependencyB2,
\One\More\Dependency\For\ClassB $dependencyB3,
...
string $parameterInjectedByTheFactory
) {
$this->logger = $logger;
$this->dependencyB2 = $dependencyB2;
...
$this->parameterInjectedByTheFactory = $parameterInjectedByTheFactory;
}
}
Я считаю, что следующий класс неверен. Он не должен знать о конкретных зависимостях для ClassA и ClassB, не так ли?
class Factory
{
public function __constructor(
\My\Logger $logger
) {
$this->logger = $logger;
}
public function make($class, $myParameter)
{
switch($class)
{
case 'ClassA':
return new $class(
$this->logger,
new \Dependency\For\ClassA,
...
$myParameter
)
case 'ClassB':
return new $class(
$this->logger,
new \Dependency\For\ClassB,
...
$myParameter
)
}
}
}
Я хочу воспользоваться автоматическим подключением DI для создания экземпляров classA и classB, но внедрять контейнер в фабрику - это плохая практика.
Более реальный пример. У меня есть EndpointManager с методом get($endpointCode), который ищет $ endpointCode в массиве. Эта запись массива содержит информацию для построения конечной точки:
<?php
[
'endpoint1' => [
'class' => '\My\Class\For\Dealing\With\MySQL',
'connectionData' => 'mysql:user@localhost',
],
'endpoint2' => [
'class' => '\My\Class\For\Dealing\With\REST',
'connectionData' => [
'url': 'https://localhost/api/rest/',
...
],
],
]
EndpointManager должен создать экземпляр объекта класса, определенного в записи 'class', и передать ему connectionData. Но конструкторы для \My\Class\For\Dealing\With\MySQL и \My\Class\For\Dealing\With\REST разнородны, каждый из них имеет свой собственный потенциально большой набор зависимостей. Есть ли способ использовать DI для создания экземпляров этих объектов.
Какой лучший подход в этой ситуации?
@xmike Хороший ответ и очень интересные ссылки. Ты открыл мне глаза. Я введу контейнер, чтобы иметь дело с созданием этих объектов. Спасибо!






Вы можете делегировать создание конкретного класса анонимной функции, например
<?php
$dependencyManager>add('endpoint1', function ($di) {
return new \My\Class\For\Dealing\With\MySQL('mysql:user@localhost', .., ...);
});
$dependencyManager>add('endpoint2', function ($di) {
return new \My\Class\For\Dealing\With\REST('https://localhost/api/rest/', .., ..., ...);
});
Чтобы получить класс экземпляра:
<?php
$endpoint1Instance = $dependencyManager->get('endpoint1');
$endpoint2Instance = $dependencyManager->get('endpoint2');
где методы add() и get() могут быть объявлены, как показано ниже:
<?php
class DependencyManager
{
private $dependencies = [];
...
public function add($key, callable $factory)
{
$this->dependencies[$key]['instance'] = null;
$this->dependencies[$key]['factory'] = $factory;
}
public function get($key)
{
if (isset($this->dependencies[$key]) {
$instance = $this->dependencies[$key]['instance'];
if (is_null($instance)) {
$instance = $this->dependencies[$key]['factory']($this);
$this->dependencies[$key]['instance'] = $instance;
}
return $instance;
} else {
throw new Exception('not found');
}
}
...
}
Вы можете посмотреть на существующую реализацию диспетчера зависимостей, например Прыщ.
Да. Спасибо за ваш ответ. Я знаю про Прыщ. Но я использую PHP-DI. Но мой вопрос состоял в том, чтобы избавиться от ручного создания экземпляра (вернуть новый \ My \ Class \ For \ Dealing \ With \ MySQL ('mysql: user @ localhost', .., ...);) и воспользоваться преимуществами Функция автоматического подключения PHP-DI для работы с аргументами конструктора.
вы упомянули, что «... но вводить контейнер на заводе - плохая практика». Ну не совсем так. Фабрики не должны быть элементами кода приложения. Я бы посоветовал прочитать это (последние три абзаца являются наиболее важными в отношении вашего вопроса) и это, чтобы получить больше информации по теме.