Инъекция зависимостей PHP

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

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

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

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

4 ответов


Если у вас есть несколько зависимостей, то да, контейнер DI может быть решением.

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

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

// 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)
);

и ваши конструкторы класса принять контейнер в качестве параметра:

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 как сам класс и создать его экземпляр с классами компонентов, введенными в него индивидуально. Одно из преимуществ использования объекта вместо массива заключается в том, что по умолчанию он будет передаваться по ссылке в классы, использующие его, в то время как массив передается по значению (хотя объекты внутри массива все еще ссылки на литературу.)

редактировать

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

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

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);
  }
}

Инъекции Зависимостей !== DIC

люди должны действительно перестать путать их. Инъекции Зависимостей - это идея, что от принцип инверсии зависимостей.

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

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

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

если вы хотите узнать больше об инъекции зависимостей, я бы рекомендуем вам посмотреть" Чистый код " на youtube:


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

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 или что helpAction нужен SomeObject делать свою работу;

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

П. С. Джика мой ник :)


Я рекомендую вам использовать Singltones или Mutlitones. В этих случаях вы всегда сможете получить объекты с помощью методов статического класса.

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

class Registry {
    private static $instances = array();

    public static function add($k, $v) {
        $this->instances[$k] = $v;
    }

    public static function get($k) {
        return $this->instances[$k];
    }
}

class MyClass {
    public function __construct() {
        Registry::add('myclass', $this);
    }
}