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

Понимание потребности в структуре DI

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

Рассмотрим следующее:

public abstract class Saw
{
    public abstract void cut(String wood);
}

public class HandSaw extends Saw
{
    public void cut(String wood)
    {
        // chop it up
    }
}

public class ChainSaw extends Saw
{
    public void cut(String wood)
    {
        // chop it a lot faster
    }
}

public class SawMill
{
    private Saw saw;

    public void setSaw(Saw saw)
    {
        this.saw = saw;
    }

    public void run(String wood)
    {
        saw.cut("some wood");
    }
}

Тогда вы могли бы просто сделать:

Saw saw = new HandSaw();
SawMill sawMill = new SawMill();
sawMill.setSaw(saw);
sawMill.run();

Что было бы эквивалентно:

<bean id="saw" class="HandSaw"/>

<bean id="sawMill" class="SawMill">
   <property name="saw" ref="saw"/>
</bean>

и

ApplicationContext context = new ClassPathXmlApplicationContext("sawmill.xml");
SawMill springSawMill = (SawMill)context.getBean("sawMill");
springSawMill.run();

Конечно, это пример, и с более сложными отношениями с объектами, возможно, более эффективным было бы зашифровать XML файл, чем писать его программно, но, конечно же, должно быть больше, чем это?

(Я знаю, что структура Spring больше, но я думаю о необходимости контейнера DI.)

В первом примере также было бы тривиально изменять зависимости midstream:

// gotta chop it faster
saw = new ChainSaw();
sawMill.setSaw(saw);
sawMill.run();
4b9b3361

Ответ 1

У меня был тот же самый вопрос, и на это ответил:
Конечно, вы могли бы сделать то, что вы описали в "Тогда вы могли бы просто сделать:..." (позвольте назвать это "класс A" ). Тем не менее, это приведет к соединению класса A с HandSaw или ко всем зависимостям, необходимым для класса SawMill. Почему A следует связать с HandSaw - или, если вы используете более реалистичный сценарий, почему моя бизнес-логика должна быть связана с реализацией соединения JDBC, необходимой для уровня DAO?
Решение, которое я предложил тогда, было "затем переместить зависимости на один шаг дальше" - хорошо, так что теперь у меня есть мое представление, связанное с соединением JDBC, где мне нужно иметь дело только с HTML (или Swing, выберите ваш вкус).

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

Кроме того, у вас есть неправильное представление о "плюсе:" (где вы делаете SawMill springSawMill = (SawMill)context.getBean("sawMill"); springSawMill.run();) - вам не нужно получать sawMill bean из контекста - пилаMill bean должна была быть вводится в ваш объект (класс A) каркасом DI. поэтому вместо... getBean (...), вы просто заходите "sawMill.run()", не заботясь о том, откуда он пришел, кто его инициализировал и как. Для вас все равно, он может перейти прямо к /dev/null или тестовому результату или к реальному движку CnC... Дело в том, что вам все равно. Все, что вам нужно, это ваш крошечный класс А, который должен делать то, что он сократил, - активировать лесопильную станцию.

Ответ 2

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

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

Структуры зависимостей впрыска компенсируют отсутствие неявных параметров, Curried functions и удобные средства для monads на этом языке.

Ответ 3

Spring имеет три функции, которые одинаково важны:

  • инъекция зависимостей
  • аспектно-ориентированное программирование
  • библиотека классов фреймворка, которая помогает с сохранением, удалением, веб-mvc и т.д.

Я соглашусь с тем, что трудно увидеть преимущество для инъекций зависимостей, когда вы сравниваете это с одним вызовом нового. В этом случае последний, безусловно, будет выглядеть проще, потому что это одна строка кода. Конфигурация Spring всегда будет увеличивать строки кода, поэтому это не выигрышный аргумент.

Он начинает выглядеть намного лучше, когда вы можете использовать сквозные проблемы, такие как транзакции из своих классов, и использовать аспекты для их декларирования. Сравнение с одним "новым" вызовом - это не то, для чего был создан Spring.

Возможно, лучший результат использования Spring - это то, как его рекомендуемая идиома использует интерфейсы, слоизацию и хорошие принципы, такие как DRY. Это действительно просто дистилляция объектно-ориентированных лучших практик, которые Род Джонсон использовал в своих консультационных концертах. Он обнаружил, что код, который он создал с течением времени, помог ему заработать больше средств для своих клиентов. Он обобщил свой опыт в "Эксперте 1:1 J2EE" и закончил открытый поиск кода как Spring.

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

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

Ответ 4

Конечно, это пример, и с более сложными отношениями с объектами, возможно, более эффективным было бы зашифровать XML файл, чем писать его программно, но, конечно же, должно быть больше, чем это?

Я думаю, что имеет смысл добавить "проводку вверх" в файл конфигурации, а не делать это вручную в коде по нескольким причинам:

  • Конфигурация является внешней по отношению к вашему коду.
  • Изменения в проводке вверх (чтобы сообщить sawmill использовать другой экземпляр Saw) можно просто сделать во внешний (XML) файл и не требовать изменения кода, повторной компиляции, повторного развертывания и т.д.
  • Когда у вас есть десятки классов и несколько уровней впрыска (например: у вас есть класс Controller, который получает класс Service, который содержит вашу бизнес-логику, которая использует DAO для получения Saw из базы данных, которая получает в нее DataSource и т.д.), ручная проводка коллаборационистов утомительна и требует нескольких десятков строк кода, которые ничего не делают, кроме проводов.
  • Это несколько менее понятная "польза", но, имея все "проводку" вне кода, я думаю, что это помогает закрепить разработчикам идеи, лежащие в основе инъекции зависимостей, в частности идея кодирования интерфейса, а не реализация. При ручной проводке вверх можно легко вернуться к старым путям.

Ответ 5

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

public class SawDI{
    public Saw CreateSaw(){
        return new HandSaw();
    }

    public SawMill CreateSawMill(){
        SawMill mill = new SawMill();
        mill.SetSaw(CreateSaw());
        return mill;
    }
}

// later on

SawDI di = new SawDI();
SawMill mill = di.CreateSawMill();

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

Ответ 6

Одна вещь, которую большинство (если не все) контейнеров/библиотек DI приносит вам дополнительно, - это возможность перехвата методов для всех экземпляров, созданных с помощью DI.

Ответ 7

Не забывайте, что один из основных недостатков инъекции зависимостей: вы теряете способность легко узнать, откуда что-то инициализируется с помощью функции поиска использования мощной Java IDE. Это может быть очень серьезным моментом, если вы много рефакторируете и хотите избежать того, чтобы тестовый код был в 10 раз больше, чем код приложения.

Ответ 8

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

Ответ 9

В последнее время большое внимание уделяется рамкам DI, даже если забыть о шаблоне DI. Принципы DI как обобщены J. B. Rainsberger:

Это просто: сделать зависимости явными, требуя от коллабораторов как параметров в конструкторе. Повторяйте, пока не решите все решения о том, какие объекты нужно создать в точке входа. Конечно, это только применяется к Услугам (в смысле DDD). Готово.

Как вы заметили, нет никакой разницы между настройкой зависимостей вручную и использованием фреймворка. При инсталляции конструктора, как показано ниже, в вашем примере кода будет еще меньше шаблона, и компилятор заставит вас предоставить все необходимые зависимости (и ваша IDE, вероятно, напечатает их для вас).

SawMill sawMill = new SawMill(new HandSaw());
sawMill.run();

Рамка DI может уменьшить шаблон создания фабрик и объединить объекты вместе, но в то же время может также затруднить выяснение того, откуда происходит каждая зависимость - конфигурация каркаса DI является еще одним слоем абстракции, чтобы прорыть, и ваша среда IDE не сможет сообщить вам, откуда вызывается конкретный конструктор.

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

Некоторые преимущества каркасов DI исходят из их поддержки расширенных функций, таких как AOP (например, Spring @Transactional), определение области охвата (хотя много раз простого кода будет достаточно) и возможность подключения (если действительно необходима плагиновая структура).

Недавно я сделал эксперимент с ручным DI и основанным на каркасе DI. Прогресс и результаты показаны в виде скринкастов в Пусть эпизоды Code Dimdwarf от 42 до 47. В рассматриваемом проекте была Guice система плагинов для создания действующих лиц, который я затем переписал с использованием ручного DI, без Guice. Результат был намного проще и понятнее, и только немного больше шаблонов.

Сводка: Сначала попробуйте использовать только ручной DI (предпочтительно, впрыск конструктора). Если будет много шаблонов, попробуйте переосмыслить дизайн, чтобы уменьшить зависимости. Используйте инфраструктуру DI, если некоторые из ее функций предоставляют вам ценность.

Ответ 10

Важно понимать, что Spring - это в основном две вещи, одна из которых построена поверх другой:

  • Легкая структура DI/IoC и классы для ее реализации (например, контексты XML-приложений и т.д.); и
  • Это легкий контейнер.

(2) - основная часть кода Spring. В основном выберите технологию Java, и вы, вероятно, найдете Spring для этого есть вспомогательные классы. Это значит, что вы можете использовать ActiveMQ, Sun One MQ и т.д., А абстрактные - это Spring JmsTemplate, а также технологии доступа к данным, веб-сервисы и т.д.

Все эти помощники используют (1) для их соединения.

Ответ 11

Одним из самых больших преимуществ использования Injection Dependency Injection является то, что при создании unit test для этого класса гораздо проще предоставлять макеты или заглушки зависимостей классов. Это позволяет тестировать класс отдельно, в зависимости от его соавторов.

В вашем примере нет способа издеваться или вырезать пилу или SawMill в классе, где они создаются. Если Saw и SawMill были установлены через сеттеры или конструктор, тогда вы можете передать свою собственную макетную пилу и издеваться над SawMill при запуске unit test.

Ответ 12

Spring помогает вам понять и даже поощрять модели DI. Тем не менее, я не верю, что вы должны иметь Spring.

У вас могут быть файлы конфигурации на Java, которые можно отлаживать, рефакторировать и выполнять анализ кода.

Я предлагаю вам разместить эти файлы конфигурации java в другом пакете, но они в остальном эквивалентны файлам конфигурации XML. Вы можете даже загрузить этот файл динамически, если это важно.