10

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

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

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

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

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

  • Это анти-паттерн поиска сервисов. Погугли это.

    от
  • НЕТ, вы не должны Особенно не так ужасно, как ваш дизель.

    от
  • @Azirius Я добавил пример выше, где объект более гибкий.

    от
  • Это довольно ужасная реализация DIC. Кроме того, какой смысл передавать массив в качестве ссылки, когда вы не пишете в него?

    от
  • Вы действительно хотите написать в ссылочный массив в конструкторе ?! Это безумие. Или это просто потому, что вы не понимаете, что копировать при записи в php5 ..

    от
  • Внедрение зависимостей это способ реализации Ioc правильно?

    от
  • "Dependency Injection" is a 25-dollar term for a 5-cent concept -- James Shore

    от Shiplu Mokaddim
  • 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"

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