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

Почему контейнеры IOC не нужны с динамическими языками

Кто-то из подкаста № 68 под стражей, http://herdingcode.com/?p=231, заявил, что контейнерам IOC не место с Python или Javascript, или словами с этой целью. Я предполагаю, что это общепринятая мудрость и что она применима ко всем динамическим языкам. Зачем? Что такое динамические языки, которые делают контейнеры IOC ненужными?

4b9b3361

Ответ 1

IoC обеспечивает механизм разрыва связи, которую вы получаете, когда объект вызывает "новый" в другом классе. Эта связь связывает вызывающий объект с инстанцируемой реализацией любого интерфейса, который он реализует.

В статических языках, когда вы ссылаетесь на класс по имени (называть new на нем), нет никакой двусмысленности. Это плотная связь для определенного класса.

В динамических языках вызов new X является заполнителем для "экземпляр любого класса, определенного как X в точке выполнения". Это более слабое соединение, поскольку оно связано только с именем X.

Это тонкое различие означает, что на динамическом языке вы обычно можете изменить то, что X, поэтому решение о том, какой класс создается, все еще модифицируется вне вызывающего класса.

Однако лично я считаю, что для IoC есть два преимущества, которые я не получаю, полагаясь на динамический язык, чтобы разрешить инъекцию.

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

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

Как сказал Йорг У Миттаг. "Эти инструменты не нужны, принципы дизайна - нет". Я считаю, что они не нужны, но сделаны правильно, все еще ценные.

Ответ 2

У меня другое мнение. Я думаю, что контейнеры МОК, безусловно, играют роль в динамических языках.

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

Контейнер IOC - это просто инструмент для управления этой организацией.

Даже на динамическом языке я хочу "объединить" компоненты. Без жестких зависимостей между этими компонентами. Или, возможно, даже без указания фактического класса реализации для этих компонентов.

Ответ 3

Я согласен с приведенными выше ответами, но я подумал, что я немного обманул бы здесь тестирование:

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

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

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

Должны быть два типа тестов:

  • Единичные тесты, которые запускаются в ЛЮБОЙ среде и проверяют логику отдельных блоков кода.
  • Интеграция/функциональные тесты, которые проверяют логику комбинированного приложения.

Теперь на вопрос: IoC. Для чего подходит IoC? Это удобно для нескольких вещей, но действительно полезно для упрощения использования инъекции зависимостей:

// Do this every time you want an instance of myServiceType
var SystemA = new SystemA()
var SystemB = new SystemB()
var SystemC = new SystemC(SystemA, "OtherThing")
var SystemD = new SystemD(SystemB, SystemC)
var IRepo = new MySqlRepo()
var myService = new myServiceType(SystemD, IRepo)

В эту логику:

// Do this at application start
Container.Register(ISystemA, SystemA)
Container.Register(ISystemB, SystemB)
Container.Register(ISystemC, SystemC)
Container.Register(ISystemD, SystemD)
Container.Register(IRepo, MySqlRepo)
Container.Register(myServiceType)

// Do this any time you like
var myService = Container.resolve(myServiceType)

Итак, почему мы не видим IOC на многих динамических языках?

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

... и это было бы потому, что обычно тестирование, выполненное в них, не существует.

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

Все это вздор.

Нет никакого оправдания для проекта без модульных тестов или модульных тестов с плохим покрытием кода.

... но это удивительно количество проектов javascript и python, которые я видел (выбор этих двух специально только потому, что они представляют интерес, и я видел больше проектов такого типа, чем другие) без IoC, нет DI, и неудивительно, никаких тестов.

Существует отличная статья о DI на веб-сайте guice: http://code.google.com/p/google-guice/wiki/Motivation

Нет ничего о динамических языках, которые решают любую из этих проблем.

Резюме:

  • IoC полезен для вещей, но прежде всего для реализации DI
  • IoC не является файлами конфигурации xml. > _ & Л;
  • DI полезен для тестов
  • Отсутствие МОК свидетельствует об отсутствии ДИ, что свидетельствует об отсутствии хорошего тестирования.
  • Используйте IoC.

Ответ 4

Потому что они уже встроены в язык.

Контейнер IoC предоставляет две вещи:

  • динамическое связывание
  • динамический язык (обычно невероятно дрянной, построенный поверх XML или в более новых версиях поверх атрибутов Java/.NET)

Динамическое связывание уже является частью динамического языка, а динамический язык уже является динамическим языком. Поэтому контейнер IoC просто не имеет смысла: язык уже является контейнером IoC.

Еще один способ взглянуть на это: что позволяет контейнер IoC? Это позволяет вам брать независимые компоненты и объединять их вместе в приложение без каких-либо компонентов, зная что-либо друг о друге. Существует имя для подключения независимых частей вместе в приложение: скриптинг! (Это в значительной степени определение сценариев.) Многие динамические языки также очень хороши при написании сценариев, поэтому они идеально подходят для контейнеров IoC.

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

Ответ 5

Одной из основных особенностей контейнеров IOC является то, что вы можете автоматически "подключать" свои модули вместе во время выполнения. В динамических языках вы можете сделать это довольно легко без какой-либо фантастической логики на основе отражения. Тем не менее, контейнеры IOC являются полезной моделью, которую многие люди понимают, и иногда может быть полезно использовать один и тот же стиль дизайна. См. эту статью для другой точки зрения.

Ответ 6

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

Это наивный взгляд на IoC. Обычно IoC также решает:

  • разрешение зависимостей
  • автоматический поиск и инициализация компонентов (если вы используете "require" с IoC, там что-то не так)
  • работает не только с одиночными, но и с динамическим объемом
  • 99,9% времени, которое оно невидимо для разработчика
  • устраняет необходимость в app.config

полная статья Вы недооцениваете силу IoC

Ответ 7

Я считаю, что контейнеры IoC необходимы в больших приложениях JavaScript. Вы можете видеть, что некоторые популярные фреймворки JavaScript включают контейнер IoC (например, Angular $injector).

У меня есть контейнер IoC, который называется InversifyJS, вы можете узнать об этом более подробно на http://inversify.io/.

В некоторых контейнерах JavaScript IoC объявляются вложенные зависимости следующим образом:

import Katana from "./entitites/katana";
import Shuriken from "./entitites/shuriken";

@inject(Katana, Shuriken) // Wrong as Ninja is aware of Katana and Shuriken!
class Ninja {
  constructor(katana: IKatana, shuriken: IShuriken) {
    // ...

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

InversifyJS предлагает вам реальную развязку. Файл ninja.js никогда не укажет на файлы katana или shuriken. Тем не менее, он укажет на интерфейсы (во время разработки) или строковые литералы (во время выполнения), которые допустимы, потому что это абстракции и в зависимости при абстракциях - вот что такое DI.

import * as TYPES from "./constants/types";

@inject(TYPES.IKATANA, TYPES.ISHURIKEN) // Right as Ninja is aware of abstractions of Katana and Shuriken!
class Ninja { 
  constructor(katana: IKatana, shuriken: IShuriken) {
    // ...

Ядро InversifyJS является единственным элементом приложения, знакомым с жизненным циклом и зависимостями. Мы рекомендуем сделать это в файле с именем inversify.config.ts и сохранить файл в корневой папке, содержащей исходный код приложения:

import * as TYPES from "./constants/types";
import Katana from "./entitites/katana";
import Shuriken from "./entitites/shuriken";
import Ninja from "./entitites/ninja";

kernel.bind<IKatana>(TYPES.IKATANA).to(Katana);
kernel.bind<IShuriken>(TYPES.ISHURIKEN).to(Shuriken);
kernel.bind<INinja>(TYPES.ININJA).to(Ninja);

Это означает, что вся муфта в вашем приложении имеет место в одном уникальном месте: файле inversify.config.ts. Это действительно важно, и мы докажем это на примере. Представьте, что мы меняем сложность в игре. Нам просто нужно перейти к inversify.config.ts и изменить привязку Katana:

import Katana from "./entitites/SharpKatana";

if(difficulty === "hard") {
    kernel.bind<IKatana>(TYPES.IKATANA).to(SharpKatana);
} else {
    kernel.bind<IKatana>(TYPES.IKATANA).to(Katana);
}

Вам не нужно менять файл Ninja!

Платежная стоимость - это строковые литералы, но эта цена может быть уменьшена, если вы объявите все свои строковые литералы в файле, который содержит константы (как действия в Redux). Хорошей новостью является то, что в будущем строковые литералы могут быть созданы компилятором TS, но это находится в руках комитета TC39 на данный момент.

Вы можете попробовать его онлайн здесь.

Ответ 8

Контейнеры IoC действительно позволяют создавать композиционный слой в статически типизированных, процедурных/OO-языках.

Этот композиционный слой существует относительно естественно в динамических языках, таких как Python или Javascript (считают, что Javascript сильно основан на схеме).

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

Ответ 9

Herding Code 82 (6/6/10) сравнивает Ruby с .NET и включает подробное обсуждение того, в какой степени .NET требуется больше IOC/DI, чем Ruby.