Воспользуйтесь преимуществами DI в фабричном классе

Предположим, у меня есть группа классов, экземпляры которых должна создавать фабрика. Эта фабрика должна ввести параметр в экземпляр класса.

<?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 13.03.2018 12:44

@xmike Хороший ответ и очень интересные ссылки. Ты открыл мне глаза. Я введу контейнер, чтобы иметь дело с созданием этих объектов. Спасибо!

Manel R. Doménech 13.03.2018 13:15
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
0
2
193
1

Ответы 1

Вы можете делегировать создание конкретного класса анонимной функции, например

<?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 для работы с аргументами конструктора.

Manel R. Doménech 13.03.2018 12:26

Другие вопросы по теме