Каковы преимущества контейнеров для внедрения зависимостей?

Я понимаю преимущества самой инъекции зависимостей. Возьмем, к примеру, Spring. Я также понимаю преимущества других функций Spring, таких как АОП, различных помощников и т. д. Мне просто интересно, каковы преимущества конфигурации XML, такие как:

<bean id = "Mary" class = "foo.bar.Female">
  <property name = "age" value = "23"/>
</bean>
<bean id = "John" class = "foo.bar.Male">
  <property name = "girlfriend" ref = "Mary"/>
</bean>

по сравнению с обычным старым Java-кодом, например:

Female mary = new Female();
mary.setAge(23);
Male john = new Male();
john.setGirlfriend(mary);

который легче отлаживать, проверять во время компиляции и может понять любой, кто знает только java. Итак, какова основная цель инфраструктуры внедрения зависимостей? (или фрагмент кода, показывающий его преимущества.)


Обновлено:
В случае

IService myService;// ...
public void doSomething() {  
  myService.fetchData();
}

Как IoC framework угадать, какую реализацию myService я хочу внедрить, если их больше одной? Если существует только одна реализация данного интерфейса, и я позволяю контейнеру IoC автоматически решать его использовать, он будет сломан после появления второй реализации. И если намеренно существует только одна возможная реализация интерфейса, вам не нужно внедрять ее.

Было бы действительно интересно увидеть небольшую конфигурацию для IoC, которая показывает его преимущества. Я уже давно пользуюсь Spring и не могу привести такой пример. И я могу показать отдельные строки, демонстрирующие преимущества hibernate, dwr и других фреймворков, которые я использую.


ОБНОВЛЕНИЕ 2:
Я понимаю, что конфигурацию IoC можно изменить без перекомпиляции. Неужели это такая хорошая идея? Я могу понять, когда кто-то хочет изменить учетные данные БД без перекомпиляции - он может быть не разработчиком. Как часто в вашей практике кто-то, кроме разработчика, меняет конфигурацию IoC? Я думаю, что разработчикам не нужно перекомпилировать этот конкретный класс вместо изменения конфигурации. А не разработчику вы, вероятно, захотите облегчить ему жизнь и предоставить более простой файл конфигурации.


ОБНОВЛЕНИЕ 3:

External configuration of mapping between interfaces and their concrete implementations

Что же такого хорошего в том, чтобы сделать его внешним? Вы не делаете весь свой код внешним, хотя вы определенно можете - просто поместите его в файл ClassName.java.txt, прочтите и скомпилируйте вручную на лету - вау, вы избежали перекомпиляции. Почему следует избегать компиляции ?!

You save coding time because you provide mappings declaratively, not in a procedural code

Я понимаю, что иногда декларативный подход экономит время. Например, я объявляю только один раз, когда сопоставление между свойством компонента и столбцом БД, и спящий режим использует это сопоставление при загрузке, сохранении, построении SQL на основе HSQL и т. д. Здесь работает декларативный подход. В случае Spring (в моем примере) объявление имело больше строк и такую ​​же выразительность, что и соответствующий код. Если есть пример, когда такое объявление короче кода - хотелось бы его увидеть.

Inversion of Control principle allows for easy unit testing because you can replace real implementations with fake ones (like replacing SQL database with an in-memory one)

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

mary = new FakeFemale();

Я понимаю преимущества DI. Я не понимаю, какие преимущества добавляет внешняя конфигурация XML по сравнению с конфигурационным кодом, который делает то же самое. Не думаю, что следует избегать компиляции - я компилирую каждый день и все еще жив. Я считаю, что конфигурация DI - плохой пример декларативного подхода. Объявление может быть полезно, если оно объявлено один раз и используется много раз разными способами - например, hibernate cfg, где сопоставление между свойством bean-компонента и столбцом DB используется для сохранения, загрузки, построения поисковых запросов и т. д. Конфигурацию Spring DI можно легко преобразовать в настройка кода, как в начале этого вопроса, не так ли? И используется он только для инициализации bean-компонентов, не так ли? Значит, декларативный подход здесь ничего не добавляет?

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


ПОСЛЕДНЕЕ ОБНОВЛЕНИЕ:
Ребята, многие ответы говорят мне об инъекции зависимостей, которую я ЗНАЮ ХОРОШО. Вопрос заключается в назначении конфигурации DI вместо инициализации кода - я склонен думать, что код инициализации короче и понятнее. Единственный ответ, который я получил на свой вопрос, заключается в том, что он позволяет избежать перекомпиляции при изменении конфигурации. Думаю, мне стоит задать еще один вопрос, потому что для меня это большой секрет, почему в этом случае следует избегать компиляции.

Наконец-то у кого-то хватило смелости задать этот вопрос. Почему вы действительно хотите избегать перекомпиляции, если ваша реализация изменяется за счет принесения в жертву (или, по крайней мере, ухудшения) поддержки инструментов / IDE?

Christian Klauser 31.08.2010 21:10

Похоже, название не совсем правильное. Автор сказал, что с контейнерами IOC все в порядке, но, похоже, возникает проблема с использованием конфигурации XML вместо настройки с помощью кода (и достаточно справедливо). Я бы предложил, возможно, «Каковы преимущества настройки контейнеров IOC с помощью XML или других подходов, не связанных с кодом?»

Orion Edwards 01.09.2010 02:15

Примеры @Orion, которые я предоставил (с мужчинами и женщинами), не требуют контейнера IOC. Меня устраивает МОК; использование контейнера, независимо от того, настроен он с использованием XML или нет, для меня все еще остается открытым вопросом.

Pavel Feldman 03.09.2010 04:23

@Orion 2: Хотя я использую ту или иную форму IOC в большинстве проектов, некоторые из них извлекают выгоду из контейнера IOC так же, как они извлекают выгоду из контейнера назначения переменных или контейнера операторов If - мне часто достаточно просто языка. У меня нет проблем с перекомпиляцией проектов, над которыми я работаю, и с удобным разделением кода инициализации dev / test / production. Так что название мне подходит.

Pavel Feldman 03.09.2010 04:30

Я вижу проблему с образцом. Один из принципов - Внедряйте сервисы, а не данные

Jacek Cz 03.09.2015 21:26
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
104
5
27 441
16
Перейти к ответу Данный вопрос помечен как решенный

Ответы 16

В мире .NET большинство фреймворков IoC предоставляют конфигурацию как XML, так и кода.

Например, StructureMap и Ninject используют свободные интерфейсы для настройки контейнеров. Вы больше не ограничены использованием файлов конфигурации XML. Spring, который также существует в .NET, в значительной степени полагается на файлы XML, поскольку это его исторический основной интерфейс конфигурации, но по-прежнему можно настраивать контейнеры программно.

Здорово, что я наконец могу использовать код для того, что он намеревался использовать :) Но зачем мне вообще что-то, кроме языка программирования, чтобы делать такие вещи?

Pavel Feldman 25.09.2008 11:46

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

Romain Verdier 25.09.2008 11:57

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

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

Mike Stone 25.09.2008 11:47

Майк, я ничего не сказал о коде. Все мы знаем, что конфигурация XML - отстой :)

aku 25.09.2008 11:50

Хм .. как часто вы меняете компоненты без перекомпиляции и для чего? Я понимаю, если кто-то меняет учетные данные БД и не хочет перекомпилировать программу - возможно, он не тот, кто ее разрабатывает. Но я не могу представить, чтобы кто-то кроме разработчика менял конфигурацию пружины.

Pavel Feldman 25.09.2008 11:50

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

aku 25.09.2008 13:44

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

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

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

Однако конфигурация XML - это моя любимая мозоль ... Я стараюсь избегать ее любой ценой.

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

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

Ваш случай очень прост и поэтому не требует контейнера IoC (Inversion of Control), такого как Spring. С другой стороны, когда вы «программируете для интерфейсов, а не для реализации» (что является хорошей практикой в ​​ООП), у вас может быть такой код:

IService myService;
// ...
public void doSomething() {
  myService.fetchData();
}

(обратите внимание, что тип myService - IService - интерфейс, а не конкретная реализация). Теперь может быть удобно позволить вашему контейнеру IoC автоматически предоставлять правильный конкретный экземпляр IService во время инициализации - когда у вас много интерфейсов и много реализаций, это может быть обременительно делать это вручную. Основные преимущества контейнера IoC (структура внедрения зависимостей):

  • Внешняя конфигурация отображения между интерфейсами и их конкретными реализациями
  • Контейнер IoC решает некоторые сложные вопросы, такие как разрешение сложных графов зависимостей, управление временем жизни компонентов и т. д.
  • Вы экономите время кодирования, потому что вы предоставляете сопоставления декларативно, а не в процедурном коде.
  • Принцип инверсии управления позволяет упростить модульное тестирование, потому что вы можете заменить реальные реализации поддельными (например, заменить базу данных SQL на базу данных в памяти)

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

<bean id = "jane" class = "foo.bar.HotFemale">
  <property name = "age" value = "19"/>
</bean>
<bean id = "mary" class = "foo.bar.Female">
  <property name = "age" value = "23"/>
</bean>
<bean id = "john" class = "foo.bar.Male">
  <property name = "girlfriend" ref = "jane"/>
</bean>

(Выше предполагается, что Female и HotFemale реализуют один и тот же интерфейс GirlfFriend)

Почему модификация логики без перекомпиляции считается хорошей идеей?

Pavel Feldman 25.09.2008 12:05

Я определенно могу сделать HotFemale jane = new HotFmale (); jane.setAge (19); john.setGirlfriend (Джейн); Так единственная разница в том, что cfg можно изменить без перекомпиляции? Кажется, это обычный ответ, когда обсуждают Spring. Почему?! Почему лучше избегать компиляции?

Pavel Feldman 25.09.2008 12:14

Что ж, я могу лучше протестировать код, я могу издеваться над женским объектом.

Paul Whelan 25.09.2008 13:58

@Pavel Feldman: потому что, если у вас уже есть приложение, развернутое на клиенте, это проще.

Andrei Rînea 12.11.2010 18:52

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

Язык должен обеспечивать хорошую поддержку как обычных методов объектно-ориентированного программирования, так и поддержку объектных интерфейсов и отражения объектов (например, Java и C#). Хотя вы можете создавать программы с использованием шаблонов DI в системах C++, отсутствие поддержки отражения внутри самого языка не позволяет ему поддерживать серверы приложений и другие платформы DI и, следовательно, ограничивает выразительность шаблонов DI.

Сильные стороны системы, построенной с использованием шаблонов DI:

  1. Код DI намного проще использовать повторно, поскольку «зависимые» функциональные возможности экстраполируются в четко определенные интерфейсы, позволяя по желанию подключать отдельные объекты, конфигурация которых обрабатывается подходящей платформой приложения, к другим объектам.
  2. Код DI тестировать намного проще. Функциональность, выраженная объектом, может быть протестирована в черном ящике путем создания «фиктивных» объектов, реализующих интерфейсы, ожидаемые логикой вашего приложения.
  3. Код DI более гибкий. Это по своей природе слабо связанный код - до крайности. Это позволяет программисту выбирать способ подключения объектов исключительно на основе их требуемых интерфейсов на одном конце и их выраженных интерфейсов на другом.
  4. Внешняя (Xml) конфигурация объектов DI означает, что другие могут настраивать ваш код в непредвиденных направлениях.
  5. Внешняя конфигурация также является шаблоном разделения проблем, поскольку все проблемы инициализации объектов и управления взаимозависимостями объектов могут обрабатываться сервером приложений.
  6. Обратите внимание, что для использования шаблона DI внешняя конфигурация не требуется, для простых соединений часто достаточно небольшого строительного объекта. Между ними существует компромисс в гибкости. Объект-конструктор не такой гибкий вариант, как видимый извне файл конфигурации. Разработчик системы DI должен взвесить преимущества гибкости перед удобством, обращая внимание на то, что мелкомасштабный и мелкозернистый контроль над конструкцией объекта, выраженный в файле конфигурации, может привести к путанице и затратам на обслуживание в будущем.

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

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

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

Короче говоря, крупномасштабные приложения на основе DI легче отлаживать и легче понимать. Хотя конфигурация Xml не проверяется на этапе компиляции, все службы приложений, о которых известно автору, будут предоставлять разработчику сообщения об ошибках, если они попытаются внедрить объект, имеющий несовместимый интерфейс, в другой объект. И большинство из них предоставляют функцию «проверки», которая охватывает все известные конфигурации объектов. Это легко и быстро сделать, проверив, что внедряемый объект A реализует интерфейс, необходимый объекту B для всех сконфигурированных инъекций объекта.

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

Pavel Feldman 25.09.2008 13:08

> Внешняя конфигурация - это тоже разделение ... Разделение конфигурации - это сердце DI, и это хорошо. И это можно сделать с помощью кода инициализации. Что cfg добавляет по сравнению с кодом инициализации? Мне кажется, что каждая строка cfg имеет соответствующую строку кода инициализации.

Pavel Feldman 25.09.2008 13:23

Одна из самых привлекательных причин - «Голливудский принцип»: не звоните нам, мы вам перезвоним. Компонент не требуется для поиска других компонентов и служб; вместо этого они предоставляются ему автоматически. В Java это означает, что больше нет необходимости выполнять поиск JNDI внутри компонента.

Также намного проще провести модульное тестирование изолированного компонента: вместо того, чтобы дать ему реальную реализацию компонентов, которые ему нужны, вы просто используете (возможно, автоматически сгенерированные) макеты.

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

Pavel Feldman 25.09.2008 13:06

На самом деле не отвечает на вопрос

Casebash 20.09.2009 04:43

Легкость комбинирование частичных конфигураций в окончательной полной конфигурации.

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

  UI-context.xml
  Model-context.xml
  Controller-context.xml

Или загрузите с другим пользовательским интерфейсом и несколькими дополнительными контроллерами:

  AlternateUI-context.xml
  Model-context.xml
  Controller-context.xml
  ControllerAdditions-context.xml

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

Причина использования контейнера DI заключается в том, что вам не нужно предварительно настраивать в коде миллиард свойств, которые являются просто геттерами и сеттерами. Вы действительно хотите жестко запрограммировать все это с помощью new X ()? Конечно, у вас может быть значение по умолчанию, но контейнер DI позволяет создавать синглтоны, что чрезвычайно просто и позволяет вам сосредоточиться на деталях кода, а не на различных задачах его инициализации.

Например, Spring позволяет вам реализовать интерфейс InitializingBean и добавить метод afterPropertiesSet (вы также можете указать «метод инициализации», чтобы избежать привязки вашего кода к Spring). Эти методы позволят вам убедиться, что любой интерфейс, указанный в качестве поля в вашем экземпляре класса, настроен правильно при запуске, и тогда вам больше не нужно проверять на нуль свои геттеры и сеттеры (при условии, что вы разрешаете своим синглетонам оставаться потокобезопасными ).

Кроме того, гораздо проще выполнять сложные инициализации с помощью контейнера DI, чем выполнять их самостоятельно. Например, я помогаю использовать XFire (не CeltiXFire, мы используем только Java 1.4). Приложение использовало Spring, но, к сожалению, использовало механизм конфигурации XFire services.xml. Когда Коллекции элементов требовалось объявить, что у нее НОЛЬ или более экземпляров вместо ОДНОГО или нескольких экземпляров, мне пришлось переопределить часть предоставленного кода XFire для этой конкретной службы.

В схеме компонентов Spring есть определенные значения по умолчанию для XFire. Итак, если бы мы использовали Spring для настройки сервисов, можно было бы использовать bean-компоненты. Вместо этого произошло то, что мне пришлось предоставить экземпляр определенного класса в файле services.xml вместо использования bean-компонентов. Для этого мне нужно было предоставить конструктор и настроить ссылки, объявленные в конфигурации XFire. Реальное изменение, которое мне нужно было сделать, требовало перегрузки одного класса.

Но благодаря файлу services.xml мне пришлось создать четыре новых класса, установив их значения по умолчанию в соответствии с их значениями по умолчанию в файлах конфигурации Spring в их конструкторах. Если бы мы могли использовать конфигурацию Spring, я бы просто сказал:

<bean id = "base" parent = "RootXFireBean">
    <property name = "secondProperty" ref = "secondBean" />
</bean>

<bean id = "secondBean" parent = "secondaryXFireBean">
    <property name = "firstProperty" ref = "thirdBean" />
</bean>

<bean id = "thirdBean" parent = "thirdXFireBean">
    <property name = "secondProperty" ref = "myNewBean" />
</bean>

<bean id = "myNewBean" class = "WowItsActuallyTheCodeThatChanged" />

Вместо этого это выглядело примерно так:

public class TheFirstPointlessClass extends SomeXFireClass {
    public TheFirstPointlessClass() {
        setFirstProperty(new TheSecondPointlessClass());
        setSecondProperty(new TheThingThatWasHereBefore());
    }
}

public class TheSecondPointlessClass extends YetAnotherXFireClass {
    public TheSecondPointlessClass() {
        setFirstProperty(TheThirdPointlessClass());
    }
}

public class TheThirdPointlessClass extends GeeAnotherXFireClass {
    public TheThirdPointlessClass() {
        setFirstProperty(new AnotherThingThatWasHereBefore());
        setSecondProperty(new WowItsActuallyTheCodeThatChanged());
    }
}

public class WowItsActuallyTheCodeThatChanged extends TheXFireClassIActuallyCareAbout {
    public WowItsActuallyTheCodeThatChanged() {
    }

    public overrideTheMethod(Object[] arguments) {
        //Do overridden stuff
    }
}

Таким образом, в конечном итоге в базу кода пришлось добавить четыре дополнительных, в основном бессмысленных класса Java, чтобы добиться эффекта, достигнутого этим одним дополнительным классом и некоторой простой информацией о контейнере зависимостей. Это не «исключение, подтверждающее правило», это ЕСТЬ правило ... обработка причуд в коде намного чище, когда свойства уже предоставлены в контейнере DI, и вы просто меняете их в соответствии с особой ситуацией, что случается чаще всего.

Ответ принят как подходящий

Для меня одна из основных причин использования IoC (и использования внешней конфигурации) заключается в двух областях:

  • Тестирование
  • Сопровождение производства

Тестирование

Если вы разделите свое тестирование на 3 сценария (что вполне нормально при крупномасштабной разработке):

  1. Модульное тестирование
  2. Интеграционное тестирование
  3. Тестирование черного ящика

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

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

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

Производство

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

Резюме

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

  • Приложение распространяется на несколько сайтов / клиентов, где среды будут отличаться.
  • Ограниченный контроль разработки / ввод в производственную среду и настройку.
  • Сценарии тестирования.

На практике я обнаружил, что даже при разработке чего-то, что у вас есть под контролем, оно будет запускаться, со временем лучше дать кому-то еще возможность изменить конфигурацию:

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

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

Каждый раз, когда вы можете изменить свой код на данные, вы делаете шаг в правильном направлении.

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

Кроме того, XML-файл может быть прочитан в графическом интерфейсе или другом инструменте и легко обработан прагматично. Как бы вы это сделали с помощью примера кода?

Я постоянно учитываю то, что большинство людей реализует как код в данные, это делает оставшийся код НАМНОГО чище. Я считаю невероятным, чтобы люди создавали меню в коде, а не в виде данных - должно быть очевидно, что делать это в коде просто неправильно из-за шаблона.

имеет смысл, не думал об этом с этой точки зрения

Pavel Feldman 17.04.2009 00:48

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

Casebash 20.09.2009 04:45

@Casebash Это интересная точка зрения - мне был бы очень интересен пример. Я считаю, что все, что я могу перенести в данные, помогает. Я также считаю, что если я сделаю именно то, что вы говорите, язык на самом деле улучшится, потому что это DSL - но даже в этом случае для создания совершенно нового языка требуется серьезное оправдание.

Bill K 20.09.2009 20:51

«Каждый раз, когда вы можете изменить свой код на данные, вы делаете шаг в правильном направлении». Добро пожаловать в анти-шаблон Soft Coding.

Raedwald 11.04.2012 16:29

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

Bill K 12.04.2012 05:28

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

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

Это верный момент, но конфигурация пружины часто бывает довольно сложной. Хотя изменить возраст легко, системному администратору все равно приходится иметь дело с большим xml-файлом, который ему не обязательно полностью понимать. Разве не лучше извлечь часть, которая должна быть настроена, во что-то еще более простое, чем конфигурация Spring XML? Как файл свойств, с одной строкой «age = 23» и не позволять администратору изменять другие детали, такие как имена классов и т. д., Которые требуют знания внутренней структуры программы.

Pavel Feldman 18.06.2010 23:10

Недавно я работал над проектом, в котором использовался код Java и XSLT. Команда состояла из людей, которые были сильны в Java (и, возможно, менее комфортно работали с XML и XSLT); и люди, которые очень хорошо работали с XML и XSLT (и менее уверенно работали с Java). Поскольку конфигурацией собиралась управлять вторая группа, имело смысл использовать Spring и иметь конфигурации XML. Другими словами, Spring решил проблему разделения труда в команде. Это не решило «технической» проблемы; в том смысле, что конфигурирование можно было так же легко сделать с помощью кода Java.

Dawood ibn Kareem 30.12.2011 14:00

Когда «обслуживающий персонал» узнает что-нибудь о необходимости изменения некоторых классов, создаваемых в контейнере внедрения зависимостей? Неужели в предположении, что это работа разработчика?

Jimbo 09.05.2013 17:04

Именно по этой причине имеет смысл извлекать значения конфигурации (например, URL-адрес системы, с которой вы интегрируетесь): персонал службы поддержки редактирует файл свойств или (в худшем случае) XML-файл, скомпилированный класс Java остается прежним.

Miro A. 12.05.2013 05:49

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

У меня есть твой ответ

Очевидно, что в каждом подходе есть компромиссы, но внешние файлы конфигурации XML полезны для корпоративной разработки, в которой для компиляции кода используются системы сборки, а не ваша IDE. Используя систему сборки, вы можете захотеть ввести определенные значения в свой код - например, версию сборки (что может быть болезненно, если вам придется обновлять вручную каждый раз при компиляции). Боль еще больше, когда ваша система сборки извлекает код из какой-то системы контроля версий. Для изменения простых значений во время компиляции вам потребуется изменить файл, зафиксировать его, скомпилировать и затем возвращаться каждый раз для каждого изменения. Это не те изменения, которые вы хотите зафиксировать в системе управления версиями.

Другие полезные варианты использования системы сборки и внешних конфигураций:

  • внедрение стилей / таблиц стилей для единой базы кода для разных сборок
  • внедрение различных наборов динамического контента (или ссылок на них) для вашей единой базы кода
  • внедрение контекста локализации для разных сборок / клиентов
  • изменение URI веб-сервиса на резервный сервер (когда основной выходит из строя)

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

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

+1 за выделение корпоративного аспекта прямо вверху. Иногда тестирование плохо написанного устаревшего кода может быть кошмаром на целый день.

Richard Le Mesurier 10.12.2013 12:45

С точки зрения Spring я могу дать вам два ответа.

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

Во-вторых, Spring - это намного больше, чем просто структура DI. Он имеет множество других функций, включая управление транзакциями и АОП. Конфигурация Spring XML объединяет все эти концепции вместе. Часто в одном и том же файле конфигурации я указываю зависимости bean-компонентов, настройки транзакций и добавляю bean-компоненты с привязкой к сеансу, которые фактически обрабатываются с использованием AOP в фоновом режиме. Я считаю, что конфигурация XML обеспечивает лучшее место для управления всеми этими функциями. Я также считаю, что конфигурация на основе аннотаций и конфигурация XML лучше масштабируются, чем конфигурация на основе Java.

Но я понимаю вашу точку зрения и нет ничего плохого в определении конфигурации внедрения зависимостей в Java. Я обычно делаю это сам в модульных тестах и ​​когда работаю над достаточно маленьким проектом, чтобы я не добавил фреймворк DI. Обычно я не указываю конфигурацию на Java, потому что для меня это тот самый код сантехники, от написания которого я пытаюсь отказаться, когда я решил использовать Spring. Однако это предпочтение, это не означает, что конфигурация XML превосходит конфигурацию на основе Java.

Другие вопросы по теме