Я понимаю преимущества самой инъекции зависимостей. Возьмем, к примеру, 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 может угадать, какую реализацию myService я хочу внедрить, если их больше одной? Если существует только одна реализация данного интерфейса, и я позволяю контейнеру IoC автоматически решать его использовать, он будет сломан после появления второй реализации. И если есть намеренно только одна возможная реализация интерфейса, вам не нужно вводить ее.
Было бы действительно интересно увидеть небольшую конфигурацию для IoC, которая показывает его преимущества. Я уже давно использую Spring и не могу привести такой пример. И я могу показать отдельные строки, демонстрирующие преимущества hibernate, dwr и других фреймворков, которые я использую.
ОБНОВЛЕНИЕ 2:
я понимаю, что конфигурацию IoC можно изменить без перекомпиляции. Неужели это такая хорошая идея? Я могу понять, когда кто-то хочет изменить учетные данные БД без перекомпиляции - он может быть не разработчиком. Как часто в вашей практике кто-то другой, кроме разработчика, меняет конфигурацию IoC? Я думаю, что разработчикам не нужно перекомпилировать этот конкретный класс вместо изменения конфигурации. А не разработчику вы, вероятно, захотите облегчить его жизнь и предоставить более простой файл конфигурации.
ОБНОВЛЕНИЕ 3:
Внешняя конфигурация отображения между интерфейсами и их конкретными реализациями
Что же такого хорошего в том, чтобы сделать его внешним? Вы не делаете весь свой код внешним, хотя вы определенно можете - просто поместите его в файл ClassName.java.txt, прочтите и скомпилируйте вручную на лету - вау, вы избежали перекомпиляции. Почему следует избегать компиляции ?!
Вы экономите время на кодирование, потому что вы предоставляете сопоставления декларативно, а не в процедурном коде.
Понимаю, что иногда декларативный подход экономит время. Например, я объявляю только один раз сопоставление между свойством компонента и столбцом БД, и спящий режим использует это сопоставление при загрузке, сохранении, построении SQL на основе HSQL и т. Д. Здесь работает декларативный подход. В случае Spring (в моем примере) объявление имело больше строк и такую же выразительность, как и соответствующий код. Если есть пример, когда такое объявление короче кода - хотелось бы его увидеть.
Принцип инверсии управления позволяет упростить модульное тестирование, поскольку вы можете заменить реальные реализации поддельными (например, заменить базу данных SQL базой данных в памяти)
Я понимаю преимущества инверсии управления (я предпочитаю называть обсуждаемый здесь шаблон проектирования как внедрение зависимостей, потому что IoC является более общим - существует много видов управления, и мы инвертируем только один из них - контроль инициализации). Я спрашивал, зачем кому-то для этого нужно что-то, кроме языка программирования. Я определенно могу заменить настоящие реализации на поддельные с помощью кода. И этот код будет выражать то же самое, что и конфигурация - он просто инициализирует поля поддельными значениями.
mary = new FakeFemale();
Я понимаю преимущества DI. Я не понимаю, какие преимущества добавляет внешняя конфигурация XML по сравнению с конфигурационным кодом, который делает то же самое. Не думаю, что следует избегать компиляции - я компилирую каждый день и все еще жив. Я считаю, что конфигурация DI - плохой пример декларативного подхода. Объявление может быть полезно, если объявлено один раз и используется много раз разными способами - например, hibernate cfg, где сопоставление между свойством bean-компонента и столбцом DB используется для сохранения, загрузки, построения поисковых запросов и т. Д. Конфигурация Spring DI может быть легко преобразована в настройка кода, как в начале этого вопроса, не так ли? И используется он только для инициализации bean-компонентов, не так ли? Значит, декларативный подход здесь ничего не добавляет?
Когда я объявляю отображение гибернации, я просто даю спящему режиму некоторую информацию, и он работает на основе этого - я не говорю ему, что делать. В случае Spring мое объявление сообщает Spring, что именно делать - так зачем объявлять это, почему бы просто не сделать это?
ПОСЛЕДНЕЕ ОБНОВЛЕНИЕ:
Ребята, многие ответы говорят мне о внедрении зависимостей, что я ЗНАЮ ХОРОШО. Вопрос заключается в назначении конфигурации DI вместо инициализации кода - я склонен думать, что код инициализации короче и понятнее. Единственный ответ, который я получил на свой вопрос, заключается в том, что он позволяет избежать перекомпиляции при изменении конфигурации. Думаю, мне стоит задать еще один вопрос, потому что для меня это большой секрет, почему в этом случае следует избегать компиляции.
источник
Ответы:
Для меня одна из основных причин использовать IoC (и использовать внешнюю конфигурацию) заключается в двух областях:
Тестирование
Если вы разделите свое тестирование на 3 сценария (что вполне нормально при крупномасштабной разработке):
Что вам нужно сделать, так это в двух последних тестовых сценариях (интеграция и черный ящик) не перекомпилировать какую-либо часть приложения.
Если какой-либо из ваших тестовых сценариев требует от вас изменения конфигурации (например, использования другого компонента для имитации банковской интеграции или повышения производительности), с этим можно легко справиться (это действительно связано с преимуществами настройки стороны DI компонента Хотя IoC.
Кроме того, если ваше приложение используется либо на нескольких сайтах (с разной конфигурацией сервера и компонентов), либо имеет изменяющуюся конфигурацию в реальной среде, вы можете использовать более поздние этапы тестирования, чтобы убедиться, что приложение будет обрабатывать эти изменения.
Производство
Как разработчик вы не (и не должны) контролировать производственную среду (в частности, когда ваше приложение распространяется среди нескольких клиентов или отдельных сайтов), для меня это реальное преимущество использования как IoC, так и внешней конфигурации. , поскольку поддержка инфраструктуры / производства должна настраивать и настраивать живую среду без необходимости возвращаться к разработчикам и проходить тестирование (более высокая стоимость, когда все, что они хотят сделать, это переместить компонент).
Резюме
Основные преимущества внешней конфигурации IoC связаны с предоставлением другим (не разработчикам) возможности настраивать ваше приложение, по моему опыту, это полезно только при ограниченном наборе обстоятельств:
На практике я обнаружил, что даже при разработке чего-то, что у вас есть под контролем, оно будет запускаться, со временем лучше дать кому-то другому возможность изменить конфигурацию:
Примечание. Приложение означает полное решение (а не только исполняемый файл), то есть все файлы, необходимые для запуска приложения .
источник
Внедрение зависимостей - это стиль кодирования, который основан на наблюдении за тем, что делегирование объекта обычно является более полезным шаблоном проектирования, чем наследование объекта (т. Е. Связь между объектом более полезна, чем связь между объектом). Однако для работы DI необходим еще один компонент - создание объектных интерфейсов. Объединив эти два мощных шаблона проектирования, инженеры-программисты быстро поняли, что они могут создавать гибкий слабосвязанный код, и таким образом родилась концепция внедрения зависимостей. Однако только после того, как отражение объектов стало доступным на некоторых языках высокого уровня, DI действительно начал развиваться. Компонент отражения является основой большей части сегодняшнего дня ».
Язык должен обеспечивать хорошую поддержку как обычных методов объектно-ориентированного программирования, так и поддержку объектных интерфейсов и отражения объектов (например, Java и C #). Хотя вы можете создавать программы с использованием шаблонов DI в системах C ++, отсутствие поддержки отражения внутри самого языка не позволяет ему поддерживать серверы приложений и другие платформы DI и, следовательно, ограничивает выразительность шаблонов DI.
Сильные стороны системы, построенной с использованием шаблонов DI:
Определенно код DI кажется более громоздким, недостатки наличия всех тех XML-файлов, которые настраивают объекты для инъекции в другие объекты, кажутся сложными. Однако в этом суть систем DI. Ваша способность смешивать и сопоставлять объекты кода в виде набора параметров конфигурации позволяет создавать сложные системы с использованием стороннего кода с минимальным написанием кода с вашей стороны.
Пример, приведенный в вопросе, просто затрагивает поверхность выразительной силы, которую может обеспечить должным образом созданная библиотека объектов DI. При наличии некоторой практики и большой самодисциплины большинство практиков DI обнаруживают, что они могут создавать системы, которые имеют 100% тестовое покрытие кода приложения. Одно только это необычно. Это не 100% тестовое покрытие небольшого приложения из нескольких сотен строк кода, а 100% тестовое покрытие приложений, содержащих сотни тысяч строк кода. Я не могу описать какой-либо другой шаблон проектирования, обеспечивающий такой уровень тестируемости.
Вы правы в том, что приложение, состоящее всего из 10 строк кода, легче понять, чем несколько объектов плюс набор файлов конфигурации XML. Однако, как и в случае с наиболее мощными паттернами проектирования, преимущества будут обнаружены по мере того, как вы продолжаете добавлять новые функции в систему.
Короче говоря, крупномасштабные приложения на основе DI проще отлаживать и проще понимать. Несмотря на то, что конфигурация Xml не проверяется во время компиляции, все службы приложений, о которых известно автору, будут предоставлять разработчику сообщения об ошибках, если они попытаются внедрить объект, имеющий несовместимый интерфейс, в другой объект. И большинство из них предоставляют функцию «проверки», которая охватывает все известные конфигурации объектов. Это легко и быстро сделать, убедившись, что внедряемый объект A реализует интерфейс, необходимый объекту B для всех настроенных инъекций объектов.
источник
Это немного загруженный вопрос, но я склонен согласиться с тем, что огромные объемы конфигурации xml на самом деле не приносят большой пользы. Мне нравится, чтобы в моих приложениях было как можно меньше зависимостей, включая здоровенные фреймворки.
Они во много раз упрощают код, но у них также есть накладные расходы по сложности, что затрудняет отслеживание проблем (я видел такие проблемы на собственном опыте, и с чистой Java мне было бы намного удобнее иметь дело).
Я предполагаю, что это немного зависит от стиля и того, что вам удобно ... вам нравится использовать собственное решение и знать его наизнанку, или полагаться на существующие решения, которые могут оказаться трудными, когда конфигурация не является? не в самый раз? Все это компромисс.
Однако конфигурация XML - это моя любимая мозоль ... Я стараюсь избегать ее любой ценой.
источник
Каждый раз, когда вы можете изменить свой код на данные, вы делаете шаг в правильном направлении.
Кодирование чего-либо в виде данных означает, что ваш код является более общим и многоразовым. Это также означает, что ваши данные могут быть указаны на подходящем для них языке.
Кроме того, XML-файл может быть прочитан в графическом интерфейсе пользователя или другом инструменте и легко обработан прагматично. Как бы вы сделали это с помощью примера кода?
Я постоянно учитываю то, что большинство людей реализует как код в данные, это делает оставшийся код НАМНОГО чище. Я считаю невероятным, чтобы люди создавали меню в коде, а не в виде данных - должно быть очевидно, что делать это в коде просто неправильно из-за шаблона.
источник
Причина использования контейнера 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, я бы просто сказал:
Вместо этого это выглядело примерно так:
Таким образом, в конечном итоге в кодовую базу пришлось добавить четыре дополнительных, в основном бессмысленных класса Java, чтобы добиться эффекта от этого одного дополнительного класса и некоторой простой информации о контейнере зависимостей. Это не «исключение, подтверждающее правило», это ЕСТЬ правило ... обработка причуд в коде намного чище, когда свойства уже предоставлены в контейнере DI, и вы просто меняете их в соответствии с особой ситуацией, что случается чаще всего.
источник
У меня есть твой ответ
Очевидно, что в каждом подходе есть компромиссы, но внешние файлы конфигурации XML полезны для корпоративной разработки, в которой для компиляции кода используются системы сборки, а не ваша IDE. Используя систему сборки, вы можете захотеть ввести определенные значения в свой код - например, версию сборки (что может быть болезненно, если придется обновлять вручную каждый раз при компиляции). Беда еще больше, когда ваша система сборки извлекает код из какой-то системы контроля версий. Для изменения простых значений во время компиляции вам потребуется изменить файл, зафиксировать его, скомпилировать и затем возвращаться каждый раз для каждого изменения. Это не те изменения, которые вы хотите зафиксировать в системе управления версиями.
Другие полезные варианты использования системы сборки и внешних конфигураций:
Обновление: все приведенные выше примеры касались вещей, которые не обязательно требовали зависимостей от классов. Но вы можете легко создать случаи, когда необходим как сложный объект, так и автоматизация - например:
источник
Вам не нужно перекомпилировать код каждый раз, когда вы меняете что-то в конфигурации. Это упростит развертывание и обслуживание программы. Например, вы можете поменять местами один компонент на другой с помощью всего лишь одного изменения в файле конфигурации.
источник
Вы можете вставить новую реализацию для подруги. Таким образом, можно ввести новую женщину без перекомпиляции кода.
(Выше предполагается, что Female и HotFemale реализуют один и тот же интерфейс GirlfFriend)
источник
В мире .NET большинство фреймворков IoC предоставляют конфигурацию как XML, так и кода.
StructureMap и Ninject, например, используют свободные интерфейсы для настройки контейнеров. Вы больше не ограничены использованием файлов конфигурации XML. Spring, который также существует в .NET, в значительной степени полагается на файлы XML, поскольку это его исторический основной интерфейс конфигурации, но по-прежнему можно настраивать контейнеры программно.
источник
Легкость объединения частичных конфигураций в окончательную полную конфигурацию.
Например, в веб-приложениях модель, представление и контроллеры обычно указываются в отдельных файлах конфигурации. Используя декларативный подход, вы можете загрузить, например:
Или загрузите с другим пользовательским интерфейсом и несколькими дополнительными контроллерами:
Чтобы сделать то же самое в коде, требуется инфраструктура для объединения частичных конфигураций. Не невозможно сделать в коде, но, безусловно, проще сделать с помощью инфраструктуры IoC.
источник
Часто важным моментом является то, кто меняет конфигурацию после того, как программа была написана. С конфигурацией в коде вы неявно предполагаете, что лицо, изменяющее его, имеет те же навыки и доступ к исходному коду и т.д., что и первоначальный автор.
В производственных системах очень практично извлечь некоторые подмножества настроек (например, возраст в вашем примере) в файл XML и позволить, например, системному администратору или персоналу службы поддержки изменять значение, не давая им полную власть над исходным кодом или другими настройками - или просто изолировать их от сложностей.
источник
С точки зрения 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.
источник
Spring также имеет загрузчик свойств. Мы используем этот метод для установки переменных, которые зависят от среды (например, разработка, тестирование, приемка, производство и т. Д.). Это может быть, например, очередь для прослушивания.
Если нет причины, по которой свойство могло бы измениться, нет и причин настраивать его таким образом.
источник
Ваш случай очень прост и поэтому не требует контейнера IoC (Inversion of Control), такого как Spring. С другой стороны, когда вы «программируете для интерфейсов, а не для реализации» (что является хорошей практикой в ООП), у вас может быть такой код:
(обратите внимание, что тип myService - IService - интерфейс, а не конкретная реализация). Теперь может быть удобно позволить вашему контейнеру IoC автоматически предоставлять правильный конкретный экземпляр IService во время инициализации - когда у вас много интерфейсов и много реализаций, это может быть обременительно делать это вручную. Основные преимущества контейнера IoC (фреймворка внедрения зависимостей):
источник
Инициализация в файле конфигурации XML упростит вашу работу по отладке / адаптации с клиентом, на компьютерах которого развернуто ваше приложение. (Потому что не требует перекомпиляции + замены бинарных файлов)
источник
Одна из самых привлекательных причин - « голливудский принцип »: не звоните нам, мы вам перезвоним. Компоненту не требуется сам выполнять поиск других компонентов и служб; вместо этого они предоставляются ему автоматически. В Java это означает, что больше нет необходимости выполнять поиск JNDI внутри компонента.
Также намного проще провести модульное тестирование компонента изолированно: вместо того, чтобы предоставить ему фактическую реализацию необходимых ему компонентов, вы просто используете (возможно, автоматически сгенерированные) макеты.
источник