Вопрос по php, dependency-injection – Внедрение зависимостей PHP

10

Я пытаюсь разобраться с зависимостью от инъекций, и я понимаю, по большей части.

Однако, скажем, если по какой-то причине один из моих классов зависел от нескольких классов, вместо того, чтобы передавать все это одному классу в конструкторе, есть ли лучший, более разумный метод?

Я слышал о DI Containers, так я бы решил эту проблему? С чего мне начать с этого решения? Должен ли я передать зависимости в мой DIC, а затем передать это классу, который нуждается в этих зависимостях?

Любая помощь, которая укажет мне правильное направление, будет фантастической.

"Dependency Injection" is a 25-dollar term for a 5-cent concept -- James Shore Shiplu Mokaddim

Ваш Ответ

3   ответа
0

Я написалстатья об этой проблеме. Идея состоит в том, чтобы использовать комбинацию абстрактной фабрики и внедрения зависимостей для достижения прозрачного разрешения зависимостей (возможно вложенных). Я скопирую / вставлю сюда основные фрагменты кода:

namespace Gica\Interfaces\Dependency;

interface AbstractFactory
{
    public function createObject($objectClass, $constructorArguments = []);
}

Абстрактная фабричная реализация:

namespace Gica\Dependency;

class AbstractFactory implements \Gica\Interfaces\Dependency\AbstractFactory, \Gica\Interfaces\Dependency\WithDependencyInjector
{
    use WithDependencyInjector;

    /**
     * @param string $objectClass
     * @param array $constructorArguments
     * @return object instanceof $class
     */
    public function createObject($objectClass, $constructorArguments =     [])
    {
        $instance = new $objectClass(...$constructorArguments);

        $this->getDependencyInjector()->resolveDependencies($instance);

        return $instance;
    }
}

Инжектор зависимости это:     пространство имен Gica \ Dependency;

class DependencyInjector implements \Gica\Interfaces\Dependency\DependencyInjector
{
    use \Gica\Traits\WithDependencyContainer;

    public function resolveDependencies($instance)
    {
        $sm = $this->getDependencyInjectionContainer();

        if ($instance instanceof \Gica\Interfaces\WithAuthenticator) {
            $instance->setAuthenticator($sm->get(\Gica\Interfaces\Authentication\Authenticator::class));
        }
        if ($instance instanceof \Gica\Interfaces\WithPdo) {
            $instance->setPdo($sm->get(\Gica\SqlQuery\Connection::class));
        }

        if ($instance instanceof \Gica\Interfaces\Dependency\WithAbstractFactory) {
            $instance->setAbstractFactory($sm->get(\Gica\Interfaces\Dependency\AbstractFactory::class));
        }
        //... all the dependency declaring interfaces go below
    }
}

контейнер зависимостей является стандартным. Код клиента может выглядеть примерно так:

$abstractFactory = $container->get(\Gica\Interfaces\Dependency\AbstractFactory::class);

$someHelper = $abstractFactory->createObject(\Web\Helper\SomeHelper::class);

echo $someHelper->helpAction();

Обратите внимание, что зависимости скрыты, и мы можем сосредоточиться на основном бизнесе. Мой клиентский код не заботится или не знает, что $ someHelper нуженAuthenticator или что помощь нужна действиеSomeObject делать свою работу;

В фоновом режиме происходит много вещей, много зависимостей обнаруживаются, разрешаются и внедряются. Обратите внимание, что я не используюnew оператор для создания$someObject, Ответственность за фактическое создание объекта передаетсяAbstractFactory

ПостскриптумGica мой ник :)

12

то да, DI-контейнер может быть решением.

Контейнер DI может быть объектом или массивом, созданным из различных зависимых объектов, которые вам нужны, которые передаются в конструктор и распаковываются.

Предположим, вам нужен объект конфигурации, соединение с базой данных и объект информации о клиенте, передаваемый каждому из ваших классов. Вы можете создать массив, который содержит их:

// Assume each is created or accessed as a singleton, however needed...
// This may be created globally at the top of your script, and passed into each newly
// instantiated class
$di_container = array(
  'config' = new Config(),
  'db' = new DB($user, $pass, $db, $whatever),
  'client' = new ClientInfo($clientid)
);

И ваши конструкторы класса принимают контейнер DI в качестве параметра:

class SomeClass {
  private $config;
  private $db;
  private $client;

  public function __construct(&$di_container) {
    $this->config = $di_container['config'];
    $this->db = $di_container['db'];
    $this->client = $di_container['client'];
  }
}

Вместо массива, как я делал выше (это просто), вы также можете создать DI-контейнер как сам класс и создать его экземпляр с классами компонентов, введенными в него индивидуально. Одним из преимуществ использования объекта вместо массива является то, что по умолчанию он передается по ссылке в классы, использующие его, а массив передается по значению (хотя объекты внутри массива по-прежнему являются ссылками).

Edit

Есть несколько способов, которыми объект является более гибким, чем массив, хотя и сложнее изначально кодировать.

Контейнерный объект также может создавать / создавать экземпляры содержащихся в нем классов в своем конструкторе (а не создавать их снаружи и передавать их внутрь). Это может сэкономить вам немного кода для каждого сценария, который его использует, так как вам нужно создать только один объект (который сам создает несколько других).

Class DIContainer {
  public $config;
  public $db;
  public $client;

  // The DI container can build its own member objects
  public function __construct($params....) {
    $this->config = new Config();

    // These vars might be passed in the constructor, or could be constants, or something else
    $this->db = new DB($user, $pass, $db, $whatever);

    // Same here -  the var may come from the constructor, $_SESSION, or somewhere else
    $this->client = new ClientInfo($clientid);
  }
}
Это анти-паттерн поиска сервисов. Погугли это.
Вы действительно хотите написать в ссылочный массив в конструкторе ?! Это безумие. Или это просто потому, что вы не понимаете, что копировать при записи в php5 ..
НЕТ, вы не должны Особенно не так ужасно, как ваш дизель.
@Azirius Я добавил пример выше, где объект более гибкий.
Это довольно ужасная реализация DIC. Кроме того, какой смысл передавать массив в качестве ссылки, когда вы не пишете в него?
29

Dependency Injection !== DIC

Внедрение зависимости это идея, которая исходит отПринцип обращения зависимостей.

ДИК это"magic cure", который обещает позволить вам использовать внедрение зависимостей, но в PHP обычно реализуется путем нарушения любого другого принципа объектно-ориентированного программирования. Худшие реализации имеют тенденцию также связывать все это с глобальным состоянием, через статическийRegistry или жеSingleton.

В любом случае, если ваш класс зависит от слишком многих других классов, то в целом это означает недостаток дизайна в самом классе. У вас есть класс, у которого слишком много причин для изменения, что нарушаетПринцип единой ответственности.

В этом случае контейнер для инъекций зависимостей будет скрывать только основные проблемы проектирования.

Если вы хотите больше узнать о внедрении зависимостей, я бы порекомендовал вам посмотреть «Чистые разговоры по коду». на ютубе:

The Clean Code Talks - Don't Look For Things! The Clean Code Talks - "Global State and Singletons"
Внедрение зависимостей это способ реализации Ioc правильно?

Похожие вопросы