Инъекция зависимостей 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);
}
}