Подтвердить что ты не робот

Что лучше: инъекция зависимости + реестр или инъекция зависимостей или глобальный реестр?

Во-первых, я хочу ограничить этот вопрос только веб-разработкой. Так что это агностик языка, пока язык используется для веб-разработки. Лично я нахожу это на фоне PHP.

Часто нам нужно использовать объект из нескольких областей. Например, нам может потребоваться использовать класс базы данных в обычной области, но также и из класса контроллера. Если мы создадим объект базы данных в нормальной области, то мы не получим доступ к нему из класса контроллера. Мы хотим избежать создания двух объектов базы данных в разных областях и, следовательно, необходимо повторно использовать класс базы данных независимо от области действия. Для этого у нас есть два варианта:

  • Сделать объект базы данных глобальным, чтобы к нему можно было получить доступ из любого места.
  • Передайте класс базы данных классу контроллера в виде, например, параметра конструктора контроллера. Это называется инъекцией зависимостей (DI).

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

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

Лично я использую последний подход из-за многих статей, которые его защищают, используя глобальные переменные, но я столкнулся с двумя проблемами. Во-первых, класс реестра будет содержать огромное количество рекурсии. Например, класс реестра будет содержать переменные входа в базу данных, необходимые классу базы данных. Поэтому нам нужно ввести класс реестра в базу данных. Тем не менее, база данных потребуется многим другим классам, поэтому база данных необходимо будет добавить в реестр, создав цикл. Могут ли современные языки справиться с этим, или это вызывает огромные проблемы с производительностью? Обратите внимание, что глобальный реестр не страдает от этого, поскольку он ничего не передается.

Во-вторых, я начну передавать большие объемы данных объектам, которые им не нужны. Моя база данных не заботится о моем маршрутизаторе, но маршрутизатор будет передан в базу данных вместе с деталями подключения к базе данных. Это осложняется проблемой рекурсии, потому что, если у маршрутизатора есть реестр, в реестре есть база данных, а реестр и реестр передаются в базу данных, тогда база данных передается сама через маршрутизатор (т.е. Я мог бы сделать $this->registry->router->registry->database из класса базы данных`).

Кроме того, я не вижу, что DI дает мне больше, чем больше сложности. Я должен передать дополнительную переменную в каждый объект, и я должен использовать объекты реестра с $this->registry->object->method() вместо $registry->object->method(). Теперь это, очевидно, не является серьезной проблемой, но, похоже, это бесполезно, если она не дает мне ничего общего с глобальным подходом.

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

Учитывая эти проблемы с обеими версиями DI, не является глобальным реестром? Что я теряю, используя глобальный реестр по DI?

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

4b9b3361

Ответ 1

Разрешите это по одному.

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

Вам не нужно вводить класс Registry в класс базы данных. Вы также можете иметь специальные методы в реестре для создания необходимых классов для вас. Или, если вы вводите реестр, вы можете просто не хранить его, а только извлекать из него то, что необходимо для правильного создания класса. Нет рекурсии.

Обратите внимание, что глобальный реестр не страдает от этого, так как он ничего не передается.

Не может быть рекурсии для самого реестра, но объекты в реестре могут иметь круговые ссылки. Это может потенциально привести к утечкам памяти при удалении объектов из реестра с версиями PHP до 5.3, когда Garbage Collector не будет правильно их собирать.

Во-вторых, я начну передавать большие объемы данных объектам, которые им не нужны. Моя база данных не заботится о моем маршрутизаторе, но маршрутизатор будет передан в базу данных вместе с деталями подключения к базе данных.

True. Но для чего нужен реестр. Он не сильно отличается от передачи $_GLOBALS в ваши объекты. Если вы этого не хотите, не используйте реестр, но передавайте только аргументы, необходимые для того, чтобы экземпляры классов находились в допустимом состоянии. Или просто не храните его.

Я мог бы сделать $this- > registry- > router- > registry- > database

Маловероятно, что маршрутизатор предоставляет публичный метод для получения реестра. Вы не сможете добраться до database от $this до router, но вы сможете напрямую добраться до database. Безусловно. Это реестр. Это то, на что вы его написали. Если вы хотите сохранить реестр в своих объектах, вы можете перенести их в сегрегированный интерфейс, который разрешает доступ только к подмножеству данных, содержащихся внутри.

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

Не обязательно. При использовании инъекции конструктора вы можете ограничить количество аргументов теми, которые абсолютно необходимы, чтобы привести объект в допустимое состояние. Оставшиеся необязательные зависимости могут быть также установлены путем инжекции сеттера. Кроме того, никто не мешает вам добавлять аргументы в объект Array или Config. Или используйте Строители.

Учитывая эти проблемы с обеими версиями DI, не является глобальным реестром? Что я теряю, используя глобальный реестр по DI?

Когда вы используете глобальный реестр, вы жестко связываете эту зависимость с классом. Это означает, что классы использования не могут использоваться без этого конкретного класса реестра. Вы предполагаете, что будет только этот реестр, а не другая реализация. При введении зависимостей вы можете вводить все, что отвечает за зависимость.

Одна вещь, которая часто упоминается при обсуждении DI vs Globals, заключается в том, что глобальные блоки запрещают вашу способность правильно тестировать вашу программу. Как именно глобальные перемены мешают мне тестировать программу, в которой DI не будет?

Они не мешают вам тестировать код. Они просто усложняют работу. Когда Unit-Testing вы хотите иметь систему в известном и воспроизводимом состоянии. Если у вашего кода зависимости от глобального состояния, вы должны создать это состояние в каждом тестовом прогоне.

Во многих местах я читал, что это связано с тем, что глобальное можно изменить из любого места и, таким образом, сложно издеваться над

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

Ответ 2

Я отправлю это как ответ, так как хочу включить код.

Я сравнивал прохождение объекта с использованием global. Я в основном создал относительно простой объект, но один с самоналожением и вложенным объектом.

Результаты:

Passed Completed in 0.19198203086853 Seconds
Globaled Completed in 0.20970106124878 Seconds

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

Итак, да, похоже, нет никакой реальной разницы в производительности между этими двумя разными способами передачи данных. Так что сделайте лучший архитектурный выбор (ИМХО, что Инъекция зависимостей)...

script:

$its = 10000;
$bar = new stdclass();
$bar->foo = 'bar';
$bar->bar = $bar;
$bar->baz = new StdClass();
$bar->baz->ar = 'bart';

$s = microtime(true);
for ($i=0;$i<$its;$i++) passed($bar);
$e = microtime(true);
echo "Passed Completed in ".($e - $s) ." Seconds\n";

$s = microtime(true);
for ($i=0;$i<$its;$i++) globaled();
$e = microtime(true);
echo "Globaled Completed in ".($e - $s) ." Seconds\n";

function passed($bar) {
    is_object($bar);
}

function globaled() {
    global $bar;
    is_object($bar);
}

Протестировано на 5.3.2