Что такое инверсия контроля?

1828

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

  1. Что это?
  2. Какую проблему это решает?
  3. Когда уместно использовать, а когда нет?
Майк Минутилло
источник
292
Проблема большинства этих ответов заключается в используемой терминологии. Что такое контейнер? Инверсия? Зависимость? Объясните это в терминах непрофессионала без громких слов.
kirk.burleson
13
Смотрите также на Programmers.SE: Почему Inversion of Control назван так?
Изката
8
Это инъекция зависимостей (DI) - см. Описание Мартина Фаулера здесь: martinfowler.com/articles/injection.html#InversionOfControl
Рикардо Санчес
Это прилагательное, а не существительное, это не вещь, это описание изменения, внесенного в код, где управление потоком находится в делегате, а не в контейнере.
Мартин Спамер

Ответы:

1524

Шаблоны Inversion of Control (IoC) и Dependency Injection (DI) предназначены для удаления зависимостей из вашего кода.

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

public class TextEditor {

    private SpellChecker checker;

    public TextEditor() {
        this.checker = new SpellChecker();
    }
}

То, что мы сделали здесь, создает зависимость между TextEditorи SpellChecker. В сценарии IoC мы бы вместо этого сделали что-то вроде этого:

public class TextEditor {

    private IocSpellChecker checker;

    public TextEditor(IocSpellChecker checker) {
        this.checker = checker;
    }
}

В первом примере кода мы используем instantiating SpellChecker( this.checker = new SpellChecker();), что означает, что TextEditorкласс напрямую зависит от SpellCheckerкласса.

Во втором примере кода мы создаем абстракцию, имея SpellCheckerкласс зависимости в TextEditorсигнатуре конструктора (не инициализируя зависимость в классе). Это позволяет нам вызвать зависимость, а затем передать ее классу TextEditor следующим образом:

SpellChecker sc = new SpellChecker; // dependency
TextEditor textEditor = new TextEditor(sc);

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

urini
источник
50
Хороший понятный пример. Однако предположим, что вместо того, чтобы требовать передачи интерфейса ISpellChecker в конструктор объекта, мы представили его как настраиваемое свойство (или метод SetSpellChecker). Будет ли это все еще представлять собой IoC?
devios1
25
chainguy1337 - да, будет. Использование таких сеттеров называется инъекцией сеттера, а не инжектором конструктора (оба метода внедрения зависимостей). IoC - это довольно общий шаблон, но инъекция зависимостей достигает IoC
Джек Уклея
257
Несмотря на множество голосов, этот ответ неверен. Пожалуйста, смотрите martinfowler.com/articles/injection.html#InversionOfControl . В частности, обратите внимание на часть, в которой говорится: «Инверсия контроля - это слишком общий термин, и поэтому люди находят его непонятным. В результате большого обсуждения с различными сторонниками IoC мы остановились на названии Dependency Injection».
Rogério
45
Я согласен с @Rogeria. это не объясняет, почему он называется IoC, и я удивлен количеством голосов «за» ;-)
Аравинд Яррам
31
Я на стороне @Rogerio и @Pangea. Это может быть хорошим примером для внедрения в конструктор, но не хорошим ответом на исходный вопрос. IoC, как определено Фаулером , может быть реализован без использования инъекций любого вида, например, с использованием локатора службы или даже простого наследования.
МЦЗ
642

Инверсия управления - это то, что вы получаете, когда ваши программные обратные вызовы, например, программы с графическим интерфейсом.

Например, в меню старой школы вы могли бы иметь:

print "enter your name"
read name
print "enter your address"
read address
etc...
store in database

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

В программе с графическим интерфейсом или что-то подобное мы говорим:

when the user types in field a, store it in NAME
when the user types in field b, store it in ADDRESS
when the user clicks the save button, call StoreInDatabase

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

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

Марк Харрисон
источник
161
не помечайте этого парня. технически он прав. martinfowler.com/bliki/InversionOfControl.html IoC является очень общим принципом. Поток управления «инвертируется» путем внедрения зависимостей, потому что вы фактически делегировали зависимости некоторой внешней системе (например, контейнеру IoC)
Джек Уклея
38
Согласился с комментарием Шнайдера. 5 понижений? Разум поражает, так как это единственный правильный ответ. Обратите внимание на открытие: « как программа с графическим интерфейсом». Внедрение зависимостей является лишь наиболее распространенной реализацией IoC.
Джефф Стерн
40
Действительно, это один из немногих правильных ответов! Ребята, IoC принципиально не о зависимостях. Не за что.
Rogério
13
+1 - это хорошее описание (с примером) следующего высказывания Мартина Фаулера: «Ранние пользовательские интерфейсы контролировались прикладной программой. У вас была бы последовательность команд, таких как« Введите имя »,« введите адрес »; Программа будет выдавать запросы и получать ответы на каждый из них. С графическими (или даже на основе экрана) пользовательскими интерфейсами инфраструктура пользовательского интерфейса будет содержать этот основной цикл, а ваша программа вместо этого будет предоставлять обработчики событий для различных полей на экране. программа была перевернута, отодвинута от вас в рамки ".
Ашиш Гупта
14
Теперь я понимаю, почему его иногда злобно называют «принципом Голливуда: не звоните нам, мы вам позвоним»
Александр Сурафель
419

Что такое инверсия контроля?

Если вы выполните эти два простых шага, вы сделали инверсию управления:

  1. Разделить , что -в-делать часть из когда -в-не разлучит.
  2. Убедитесь в том, что , когда часть знает , как Литтл , как можно о какой части; и наоборот.

Для каждого из этих шагов возможно несколько методов, основанных на технологии / языке, которые вы используете для своей реализации.

-

Инверсия часть Инверсии управления (IoC) является запутанной вещью; потому что инверсия это относительный термин. Лучший способ понять IoC - забыть об этом слове!

-

Примеры

  • Обработка событий. Обработчики событий (часть «что нужно делать») - «Возникновение событий» (часть «когда нужно делать»)
  • Интерфейсы. Компонентный клиент (часть при выполнении) - реализация интерфейса компонента (часть, что нужно сделать)
  • xUnit крепеж. Настройка и TearDown (часть «что делать») - платформы xUnit вызывают программу установки в начале и TearDown в конце (часть «когда нужно делать»)
  • Шаблон метода проектирования шаблона. часть шаблона метода when-to-do - примитивная реализация подкласса what-to-do
  • Методы контейнера DLL в COM. DllMain, DllCanUnload и т. Д. (Часть «что делать») - COM / OS (часть «когда делать»)
rpattabi
источник
Как вы говорите, интерфейсы. Компонент клиент (часть «когда делать») как «когда» не имеет смысла, когда мы используем интерфейсы (например: внедрение зависимостей), мы просто абстрагируем его и даем клиенту гибкость при добавлении любой реализации, но «когда» нет участвует там. Я согласен с «когда» в случае возникновения событий обработки событий.
OldSchool
Под компонентным клиентом я подразумевал пользователя / клиента интерфейса. Клиент знает «когда» для запуска части «что делать», является ли намерение расширить функциональность или нет.
rpattabi
Взгляните на эту замечательную статью Мартина Фаулера. Он показывает, как интерфейсы составляют фундаментальную часть инверсии управления: martinfowler.com/articles/injection.html#InversionOfControl
rpattabi
два первых предложения блестящие. классно !! идеальное разделение: когда делать и что делать !! я не знаю, почему другие ответы получают так много голосов. они просто говорят коды без какого-либо понимания.
Амир Зиарати
2
Мне нравится объяснение what-to-doиwhen-to-do
KimchiMan
164

Инверсия Контроля - это разделение проблем.

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

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

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

Ло Цзюн Хуэй
источник
1
@Luo Jiong Hui Хорошее объяснение.
Сачин
3
Большинство шаблонов дизайна, если не все, имеют свои аналоги в нашей повседневной жизни, которые мы так хорошо видим и понимаем. Самый эффективный способ понять шаблон дизайна - это узнать его коллег в повседневной жизни. И я верю, что их много.
Ло Цзюн Хуэй
6
Неправильный ответ. Вы объясняете внедрение зависимостей, а не IoC. См. Комментарий Рожерио к этому ответу выше
MickyD
2
Согласен. Это DI, а не IoC. По-прежнему получает отклик, потому что это простой подход, но помогает расширить понимание темы.
MAR
Это объясняет внедрение зависимости. Не иок. Но приятное и понятное объяснение.
Чамин Викрамаратна
120

Инверсия Контроля (или IoC) - это получение свободы (Вы женаты, вы потеряли свободу и вас контролируют. Вы развелись, вы только что внедрили Инверсию Контроля. Это то, что мы назвали «разъединенными». Хорошая компьютерная система). препятствует некоторым очень близким отношениям.) больше гибкости (кухня в вашем офисе подает только чистую водопроводную воду, это ваш единственный выбор, когда вы хотите пить. Ваш босс внедрил Inversion of Control, установив новую кофемашину. Теперь вы получаете гибкость выбора водопроводной воды или кофе.) и меньшая зависимость (У вашего партнера есть работа, у вас нет работы, вы финансово зависите от своего партнера, поэтому вы находитесь под контролем. Вы нашли работу, вы внедрили Inversion of Control. Хорошая компьютерная система поощряет независимость.)

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

Благодаря реализации Inversion of Control потребитель программного / объектного объекта получает больше элементов управления / опций над программным обеспечением / объектами, вместо того, чтобы управлять им или иметь меньше опций.

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

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

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

Современные веб-приложения с архитектурой MVC зависят от среды, в которой выполняется маршрутизация URL-адресов, и устанавливают контроллеры для вызова платформы.

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

Ло Цзюн Хуэй
источник
21
Я проголосовал за сравнение DI для брака и IoC для развода.
Marek Bar
«Несмотря на то, что работники дают контроль над управлением проектом на высшем уровне менеджерам (каркасу). Но хорошо, что некоторые профессионалы помогают. Это концепция IoC, которая действительно исходит». - Во-первых, контроль с менеджером. Можете ли вы объяснить, как этот контроль инвертируется? С помощью профессионалов (что за профессионал)? Как ?
Истак Ахмед
@Istiaque Ahmed, по сравнению с гаражом, где работники полностью контролируют все, менеджеры на современном автомобильном заводе контролируют производство. Так что теперь рабочие контролируются, а не контролируются. Имейте в виду, менеджеры в этом контексте являются частью современного автомобильного завода, а не частью рабочих. Профессионалы - это менеджеры, которые профессионально планируют и производят автомобили.
Ло Цзюн Хуэй
2
Послание женатым людям: не разводитесь сейчас, в классах вашего ребенка также может быть установлен IoC.
Джулиан
Да, это только мое видение брака также IoC
Balron
94

Перед использованием Inversion of Control вы должны хорошо знать, что у него есть свои плюсы и минусы, и вы должны знать, почему вы используете его, если вы это делаете.

Плюсы:

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

Минусы:

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

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

АЭХ
источник
3
Первый обман неверен. В идеале в вашем коде должен использоваться только один контейнер IOC, и это ваш основной метод. Все остальное должно идти вниз оттуда
mwjackson 28.10.10
23
Я думаю, что он имеет в виду, вы не можете просто прочитать: myService.DoSomething () и перейти к определению DoSomething, потому что в IoC myService - это просто интерфейс, а фактическая реализация вам неизвестна, если вы не посмотрите это в конфигурационных файлах xml или в основном методе, где ваш ioc настраивается.
Chrismay
7
Вот где Resharper помогает - «нажми перейти к реализации» в интерфейсе. Отказ от IoC (или, точнее, DI из вашего примера), вероятно, также означает, что вы не тестируете должным образом
IThasTheAnswer
10
Re: он превращает ваше программное обеспечение в набор классов, которые больше не представляют собой «настоящую» программу, а представляют собой нечто, что необходимо объединить с помощью XML-конфигурации или метаданных аннотации и развалиться (и развалиться) без него - я думаю, это очень вводит в заблуждение. То же самое можно сказать о любой программе, написанной поверх фреймворка. Разница с хорошим контейнером IoC заключается в том, что вы должны иметь возможность, если ваша программа хорошо разработана и написана, вынуть ее и добавить другую с минимальными изменениями в коде, или вообще отказаться от IoC и сконструировать объекты вручную. ,
Медведь Ошри
7
Приятно видеть реальный ответ, как этот! Я думаю, что есть много опытных программистов, знакомых с объектно-ориентированным проектированием и практикой TDD, уже использующих интерфейсы, фабричные шаблоны, модели, управляемые событиями, и имитацию, где это имеет смысл, до того, как это умное слово «IoC» было изобретено. К сожалению, слишком много разработчиков / «архитекторов» заявляют о плохой практике, если вы не используете их предпочтительные фреймворки. Я предпочитаю лучший дизайн, использование встроенных языковых концепций и инструментов, чтобы достичь той же цели с небольшой долей сложности, то есть без «затуманивания» реализации, как вы говорите :-)
Тони Уолл
68
  1. Статья в Википедии . Для меня инверсия контроля превращает ваш последовательно написанный код и превращает его в структуру делегирования. Вместо того, чтобы ваша программа явно управляла всем, ваша программа устанавливает класс или библиотеку с определенными функциями, которые будут вызываться, когда происходят определенные вещи.

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

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

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

NilObject
источник
37
Я нахожу эту статью в Википедии очень запутанной и нуждающейся в исправлении. Проверьте страницу обсуждения для смеха.
devios1
Написание собственного цикла обработки событий все еще может быть инверсией управления, если этот цикл обработки событий занимает место фреймворка, а остальная часть кода использует принцип IoC, вы только что написали свою собственную фреймворк. Это на самом деле неплохая вещь, она повышает читабельность за счет лишь немного большего кодирования (не то чтобы это всегда было уместно).
Лижат
45

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

Как в этом примере с TextEditor: если у вас есть только один SpellChecker, может быть, нет необходимости использовать IoC? Если вам не нужно писать модульные тесты или что-то ...

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

Михал Шнайдер
источник
3
Откуда ты знаешь, что у тебя будет только одна проверка орфографии?
Трассировка
@Trace Вы можете знать, как программа, которую вы собираетесь написать, например, будет использоваться. Тем не менее, некоторые методы, такие как внедрение зависимостей, настолько дешевы, что редко есть причина не использовать их в подобном случае.
Лижат
44

IoC / DI для меня выталкивает зависимости к вызывающим объектам. Супер просто.

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

ferventcoder
источник
44

Предположим, вы объект. И вы идете в ресторан

Без IoC : вы просите «яблоко», и вам всегда подают яблоко, когда вы спрашиваете больше.

С IoC : Вы можете попросить «фрукты». Вы можете получать разные фрукты каждый раз, когда вас обслуживают. например, яблоко, апельсин или арбуз.

Итак, очевидно, что IoC предпочтительнее, когда вам нравятся сорта.

Ло Цзюн Хуэй
источник
26
  1. Инверсия управления - это шаблон, используемый для разделения компонентов и слоев в системе. Шаблон реализуется путем внедрения зависимостей в компонент при его создании. Эти зависимости обычно предоставляются в качестве интерфейсов для дальнейшей развязки и поддержки тестируемости. Контейнеры IoC / DI, такие как Castle Windsor, Unity - это инструменты (библиотеки), которые можно использовать для обеспечения IoC. Эти инструменты предоставляют расширенные функции помимо простого управления зависимостями, включая время жизни, AOP / перехват, политику и т. Д.

  2. а. Освобождает компонент от ответственности за управление его зависимостями.
    б. Предоставляет возможность менять реализации зависимостей в разных средах.
    с. Позволяет компоненту быть проверенным путем проверки зависимостей.
    д. Предоставляет механизм для совместного использования ресурсов в приложении.

  3. а. Критически важен при разработке на основе тестов. Без IoC тестирование может быть затруднено, поскольку тестируемые компоненты тесно связаны с остальной частью системы.
    б. Критически важен при разработке модульных систем. Модульная система - это система, компоненты которой могут быть заменены без перекомпиляции.
    с. Критически важен, если есть много сквозных проблем, которые необходимо решить, особенно в корпоративном приложении.

Гленн Блок
источник
2
На самом деле, IoC в основном не занимается управлением зависимостями. Пожалуйста, посетите martinfowler.com/articles/injection.html#InversionOfControl. В частности, обратите внимание на часть, в которой говорится: «Инверсия контроля - слишком общий термин, и поэтому люди находят его непонятным. остановился на названии «Инъекция зависимости».
Рожерио
26

Отвечая только на первую часть. Что это?

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

user2330678
источник
Речь
идет
22

Я напишу мое простое понимание этих двух терминов:

For quick understanding just read examples*

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

С этим объекты сообщают свои зависимости. И весна делает это доступным.
Это приводит к слабосвязанной разработке приложений.

Quick Example:EMPLOYEE OBJECT WHEN CREATED,
              IT WILL AUTOMATICALLY CREATE ADDRESS OBJECT
   (if address is defines as dependency by Employee object)

Контейнер инверсии управления (IoC):
это общая характеристика сред, IOC управляет объектами Java
- от создания экземпляров до уничтожения через BeanFactory.
Компоненты Java, которые создаются контейнером IoC, называются bean-компонентами, а контейнер IoC управляет областью действия bean-компонента, событиями жизненного цикла и любыми функциями AOP, для которых он был настроен и закодирован.

QUICK EXAMPLE:Inversion of Control is about getting freedom, more flexibility, and less dependency. When you are using a desktop computer, you are slaved (or say, controlled). You have to sit before a screen and look at it. Using keyboard to type and using mouse to navigate. And a bad written software can slave you even more. If you replaced your desktop with a laptop, then you somewhat inverted control. You can easily take it and move around. So now you can control where you are with your computer, instead of computer controlling it,

Внедряя Inversion of Control, потребитель программного / объектного объекта получает больше элементов управления / опций над программным обеспечением / объектами вместо того, чтобы управлять им или иметь меньше опций.

Инверсия управления в качестве руководства по проектированию служит следующим целям:

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

Детальное объяснение

VdeX
источник
17

Я согласен с NilObject , но я хотел бы добавить к этому:

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

Если вы обнаружите, что копируете и вставляете код, вы почти всегда делаете что-то не так. Зашифрован как принцип дизайна Один раз и только один раз .

Питер Бернс
источник
17

Например, задача № 1 - создать объект. Без концепции IOC задача №1 должна выполняться программистом. Но с концепцией IOC задача №1 будет выполняться контейнером.

Короче говоря, управление переворачивается из программатора в контейнер. Итак, это называется инверсией контроля.

Я нашел один хороший пример здесь .

gogs
источник
Контейнер - это концепция в IoC, где объектная модель, включая зависимости (отношения между объектом «пользователь» и «используемым» объектом) и экземплярами объекта, находится и управляется - например, содержится. Контейнер обычно предоставляется средой IoC, такой как Spring. Думайте об этом как о хранилище времени выполнения для объектов, которые составляют ваше приложение.
Медведь Ошри
17

Позвольте сказать, что мы делаем некоторые встречи в каком-то отеле.

Много людей, много графинов с водой, много пластиковых стаканчиков.

Когда кто-то хочет пить, она наполняет чашку, пьет и бросает чашку на пол.

Через час или около того у нас пол покрыт пластиковыми стаканчиками и водой.

Пусть инвертирует контроль.

Та же встреча в том же месте, но вместо пластиковых стаканчиков у нас есть официант с одной стеклянной чашкой (синглтон)

и она все время предлагает гостям выпить.

Когда кто-то хочет выпить, она достает из официанта стакан, пьет и возвращает его обратно официанту.

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

И это именно то, что делает Spring (другой контейнер IoC, например: Guice). Вместо того, чтобы позволить приложению создавать то, что ему нужно, используя новое ключевое слово (с пластиковой чашкой), контейнер Spring IoC все время предлагает приложению один и тот же экземпляр (singleton) нужного объекта (стакан воды).

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

участникам собрания понадобится стакан воды, но не кусок пирога.

Пример:-

public class MeetingMember {

    private GlassOfWater glassOfWater;

    ...

    public void setGlassOfWater(GlassOfWater glassOfWater){
        this.glassOfWater = glassOfWater;
    }
    //your glassOfWater object initialized and ready to use...
    //spring IoC  called setGlassOfWater method itself in order to
    //offer to meetingMember glassOfWater instance

}

Полезные ссылки:-

Jainendra
источник
не являются объектами статического типа синглетонов?
Gokigooooks
16

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

Нужно ли нам имя, чтобы описать разницу между процедурным и событийным программированием? Хорошо, если нам нужно, но нужно ли нам выбирать новое имя «больше, чем жизнь», которое смущает больше, чем решает?

email.privacy
источник
1
IoC! = Управляемый событиями. Сходства (а в некоторых случаях частично совпадают), но они принципиально не являются одной и той же парадигмой.
Медведь Ошри
Хороший вопрос. Событийное программирование - это, безусловно, IoC. Мы пишем обработчики событий, и они вызываются из цикла событий. Но IoC является более общей концепцией, чем программирование, управляемое событиями. Если вы переопределяете метод в подклассе, это также своего рода IoC. Вы пишете код, который будет вызываться при использовании соответствующей ссылки (экземпляра).
vi.su.
15

Смена контроля - это когда вы идете в продуктовый магазин, и ваша жена дает вам список продуктов, которые нужно купить.

В терминах программирования она передала функцию обратного вызова getProductList()функции, которую вы выполняете - doShopping().

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

Сергей Островский
источник
5
Моя жена обычно делает покупки со мной, но я согласен с этим утверждением.
ha9u63ar
1
@ ha9u63ar Твоя жена делает с тобой покупки? Ну, это называется агрегация.
Джулиан
2
Если она тоже дает деньги, это называется DI.
Сан
1
Слово «инверсия» - вверх ногами - появилось, когда ваша жена звонит getProductList()вам, чтобы найти источник денег, означает, что контроль находится на вашей стороне. В случае инверсии она будет контролировать, значит, деньги она также предоставит на покупку.
Сан
13

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

Классический код (без внедрения зависимостей)

Вот как примерно будет работать код, не использующий DI:

  • Приложению требуется Foo (например, контроллер), поэтому:
  • Приложение создает Foo
  • Приложение вызывает Foo
    • Foo нуждается в Bar (например, услуга), поэтому:
    • Фу создает бар
    • Фу называет бар
      • Бар нужен Bim (сервис, хранилище,…), поэтому:
      • Бар создает Бим
      • Бар что-то делает

Использование внедрения зависимости

Вот как примерно будет работать код, использующий DI:

  • Приложению нужен Foo, которому нужен Bar, которому нужен Bim, так:
  • Приложение создает Bim
  • Приложение создает бар и дает ему Бим
  • Приложение создает Foo и дает ему Бар
  • Приложение вызывает Foo
    • Фу называет бар
      • Бар что-то делает

Управление зависимостями инвертируется от одного вызываемого к одному вызывающему.

Какие проблемы это решает?

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

Пример: предположим, что ваше приложение хранит загруженный пользователем файл на Google Диске, с DI код вашего контроллера может выглядеть так:

class SomeController
{
    private $storage;

    function __construct(StorageServiceInterface $storage)
    {
        $this->storage = $storage;
    }

    public function myFunction () 
    {
        return $this->storage->getFile($fileName);
    }
}

class GoogleDriveService implements StorageServiceInterface
{
    public function authenticate($user) {}
    public function putFile($file) {}
    public function getFile($file) {}
}

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

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

Вместо этого, если у вас был класс контроллера для создания объекта хранилища с newключевым словом:

class SomeController
{
    private $storage;

    function __construct()
    {
        $this->storage = new GoogleDriveService();
    }

    public function myFunction () 
    {
        return $this->storage->getFile($fileName);
    }
}

Если вы хотите изменить реализацию Dropbox, вы должны заменить все строки, в которых newпостроен объект GoogleDriveService, и использовать DropboxService. Кроме того, при тестировании класса SomeController конструктор всегда ожидает класс GoogleDriveService и срабатывают фактические методы этого класса.

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

Рагхавендра Н
источник
Это должен быть самый правильный ответ, так как только он объясняет, как инвертируется «контроль».
Яримадам
лучшее объяснение до сих пор
Ali80
12

Очень простое письменное объяснение можно найти здесь

http://binstock.blogspot.in/2008/01/excellent-explanation-of-dependency.html

Это говорит -

«Любое нетривиальное приложение состоит из двух или более классов, которые взаимодействуют друг с другом для выполнения некоторой бизнес-логики. Традиционно каждый объект отвечает за получение своих собственных ссылок на объекты, с которыми он взаимодействует (его зависимости). При применении DI объекты получают свои зависимости во время создания некоторой внешней сущностью, которая координирует каждый объект в системе. Другими словами, зависимости вводятся в объекты ".

agaase
источник
12

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

Рассматривая Инверсию Контроля как шаблон проектирования, нам нужно посмотреть на то, что мы инвертируем. Dependency Injection инвертирует управление построением графа объектов. Если сказать в терминах непрофессионала, инверсия управления подразумевает изменение потока управления в программе. Например. В традиционном автономном приложении у нас есть метод main, из которого управление передается сторонним библиотекам (в случае, если мы использовали функцию сторонней библиотеки), но посредством инверсии управления управление передается из кода сторонней библиотеки в наш код , так как мы берем на себя обслуживание сторонней библиотеки. Но есть и другие аспекты, которые необходимо инвертировать в программе - например, вызов методов и потоков для выполнения кода.

Для тех, кто заинтересован в более глубоком рассмотрении Inversion of Control, была опубликована статья с изложением более полной картины Inversion of Control как шаблона проектирования (OfficeFloor: использование шаблонов office для улучшения проектирования программного обеспечения http://doi.acm.org/10.1145/ 2739011.2739013 с бесплатной копией, доступной для загрузки с http://www.officefloor.net/about.html ).

То, что идентифицировано, является следующим отношением:

Инверсия управления (для методов) = впрыск зависимости (состояния) + впрыск продолжения + впрыск резьбы

Резюме вышеуказанных отношений для Инверсии Контроля доступно - http://dzone.com/articles/inversion-of-coupling-control

Даниэль Сагеншнейдер
источник
Это очень четкий ответ. Спасибо за объяснение.
КунЮ Цай
11

IoC об инверсии отношений между вашим кодом и сторонним кодом (библиотека / фреймворк):

  • При обычной разработке программного обеспечения вы пишете метод main () и вызываете «библиотечные» методы. Вы контролируете :)
  • В IoC «framework» управляет main () и вызывает ваши методы. Framework контролирует :(

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

В итоге ты всегда побеждаешь :)

Khanh
источник
10

Мне нравится это объяснение: http://joelabrahamsson.com/inversion-of-control-an-introduction-with-examples-in-net/

Все начинается просто и показывает примеры кода.

введите описание изображения здесь

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

Разве не достаточно того, что X знает, что использует то, что имеет поведение, методы, свойства и т. Д. Y, не зная, кто на самом деле реализует это поведение?

Извлекая абстрактное определение поведения, используемого X в Y, проиллюстрированного как I ниже, и позволяя потребителю X использовать экземпляр этого вместо Y, он может продолжать делать то, что он делает, не зная специфики о Y.

введите описание изображения здесь

На иллюстрации выше Y реализует I, а X использует экземпляр I. Хотя вполне возможно, что X все еще использует Y, интересно то, что X этого не знает. Он просто знает, что использует то, что реализует I.

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

  • X больше не зависит от Y
  • Более гибкая реализация может быть решена во время выполнения
  • Выделение блока кода, упрощение тестирования

...

DDan
источник
Ссылка очень полезна. На самом деле спасибо :)
M Fuat NUROĞLU
10

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

Инверсия контроля (IoC) была построена по очень простому принципу, который называется принцип Голливуда . И это говорит о том, что

Не звоните нам, мы вам позвоним

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

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

В нашем мире программирования Голливуд представляет собой общую структуру (может быть написана вами или кем-то еще), вы представляете написанный вами код пользователя, а задача представляет собой то, что вы хотите выполнить с помощью своего кода. Теперь вы никогда не запускаете свою задачу самостоятельно, а не в IoC! Скорее вы спроектировали все так, чтобы ваша структура вызывала вашу задачу для вас. Таким образом, вы создали многоразовую структуру, которая может сделать кого-то героем или другого злодеем. Но эта структура всегда отвечает, она знает, когда кого-то выбирать, и что кто-то знает только то, кем он хочет быть.

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

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

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

Laravel и EJB являются примерами таких рамок.

Ссылка:

https://martinfowler.com/bliki/InversionOfControl.html

https://en.wikipedia.org/wiki/Inversion_of_control

Абдулла Аль Фарук
источник
1
Самый подходящий ответ я нашел здесь.
Blueray
8

Программирование говорения

IoC в простых терминах: это использование интерфейса в качестве определенного элемента (такого как поле или параметр) в качестве подстановочного знака, который может использоваться некоторыми классами. Это позволяет повторно использовать код.

Например, предположим, что у нас есть два класса: Dog и Cat . Оба имеют одинаковые качества / состояния: возраст, размер, вес. Таким образом, вместо создания класса обслуживания, называемого DogService и CatService , я могу создать один, называемый AnimalService, который позволяет использовать Dog и Cat только в том случае, если они используют интерфейс IAnimal .

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

а) Большинство разработчиков не знают, как его использовать . Например, я могу создать класс с именем Customer и автоматически (с помощью инструментов IDE) создать интерфейс под названием ICustomer . Таким образом, не редко можно найти папку, заполненную классами и интерфейсами, независимо от того, будут ли интерфейсы использоваться повторно или нет. Это называется BLOATED. Некоторые люди могут утверждать, что «возможно, в будущем мы могли бы использовать это». : - |

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

б.1) Если я добавлю trainDays()в Сервис AnimalService, то он также работает с кошками и вообще не действует.

б.2) Я могу добавить условие, в trainDays()котором он оценивает, какой класс используется. Но это полностью сломает IoC.

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

Магеллана
источник
О раздутых классах / интерфейсах: не всегда нужно повторно использовать каждый отдельный интерфейс. Иногда имеет смысл разделить большой интерфейс на несколько меньших, чтобы увидеть его функциональные границы. Меньшие интерфейсы также легче использовать в других реализациях. Также это побуждает вас кодировать интерфейс везде, где это имеет смысл. Рассмотрим «Разделение интерфейса». Только то, что вы используете интерфейс, не означает, что вы отделены. Единый толстый интерфейс бесполезен. - Просто мои 2 цента :)
MK
7

Я прочитал много ответов на этот вопрос, но если кто-то все еще смущен и нуждается в дополнительном «термине непрофессионала», чтобы объяснить IoC, вот мое мнение:

Представьте, что родитель и ребенок разговаривают друг с другом.

Без IoC:

* Родитель : Вы можете говорить только тогда, когда я задаю вам вопросы, и вы можете действовать только тогда, когда я даю вам разрешение.

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

Родитель : ты хочешь есть?

Ребенок : Нет.

Родитель : Хорошо, я вернусь. Подожди меня.

Ребенок : (Хочет играть, но поскольку от родителей нет вопросов, ребенок ничего не может сделать).

После 1 часа...

Родитель : я вернулся. Ты хочешь поиграть?

Ребенок : да.

Родитель : разрешение предоставлено.

Ребенок : (наконец-то умеет играть).

Этот простой сценарий объясняет, что контроль сосредоточен на родителе. Свобода ребенка ограничена и сильно зависит от вопроса родителей. Ребенок может говорить ТОЛЬКО в том случае, когда его просят говорить, и может действовать ТОЛЬКО при наличии разрешения.

С IoC:

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

С технологической точки зрения это очень похоже на взаимодействие консоли / оболочки / cmd и GUI. (Что является ответом Марка Харрисона выше № 2 верхний ответ). В консоли вы зависите от того, что вам задают / отображают, и вы не можете перейти к другим меню и функциям, не ответив сначала на его вопрос; после строгого последовательного потока. (программно это похоже на цикл метода / функции). Однако в графическом интерфейсе меню и функции выложены, и пользователь может выбирать все, что ему нужно, таким образом, имея больший контроль и меньшие ограничения. (программно, меню имеют обратный вызов, когда выбрано, и действие происходит).

Рувим Ямес Авиньо Грута
источник
6

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

Серджиу Старчук
источник
5

Поскольку уже есть много ответов на этот вопрос, но ни один из них не показывает разбивку термина «инверсионный контроль», я вижу возможность дать более краткий и полезный ответ.

Инверсия управления - это шаблон, который реализует принцип инверсии зависимости (DIP). DIP заявляет следующее: 1. Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракций (например, интерфейсы). 2. Абстракции не должны зависеть от деталей. Детали (конкретные реализации) должны зависеть от абстракций.

Существует три типа инверсии контроля:

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

Инверсия потока Изменяет управление потоком. Например, у вас есть консольное приложение, в котором вас просят ввести много параметров, и после каждого введенного параметра вы вынуждены нажимать Enter. Вы можете применить Flow Inversion здесь и реализовать настольное приложение, в котором пользователь может выбрать последовательность ввода параметров, пользователь может редактировать параметры, и на последнем шаге пользователь должен нажать Enter только один раз.

Инверсия создания Это может быть реализовано следующими шаблонами: Factory Pattern, Service Locator и Dependency Injection. Инверсия создания помогает устранить зависимости между типами, перемещая процесс создания объектов зависимостей за пределы типа, который использует эти объекты зависимостей. Почему зависимости плохие? Вот пара примеров: прямое создание нового объекта в вашем коде усложняет тестирование; невозможно изменить ссылки в сборках без перекомпиляции (нарушение принципа OCP); Вы не можете легко заменить настольный интерфейс веб-интерфейсом.

Вадим С.
источник
3
  1. Итак, номер 1 выше . Что такое инверсия контроля?

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

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

И как только вы получаете дженерики, это становится еще лучше. Представьте, что у вас есть издатель сообщений, который получает записи и публикует сообщения. Его не волнует, что он публикует, но ему нужен картограф, чтобы взять что-то из записи в сообщение.

public class MessagePublisher<RECORD,MESSAGE>
{
    public MessagePublisher(IMapper<RECORD,MESSAGE> mapper,IRemoteEndpoint endPointToSendTo)
    {
      //setup
    }
}

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

О да, есть проблемы с тестируемостью, но они вторичны по отношению к преимуществам IoC / DI.

Я определенно люблю IoC / DI.

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

ferventcoder
источник
3

Создание объекта в классе называется тесной связью, Spring удаляет эту зависимость, следуя шаблону проектирования (DI / IOC). В котором объект класса передается в конструкторе, а не в классе. Более того, мы даем ссылочную переменную суперкласса в конструкторе, чтобы определить более общую структуру.

shA.t
источник