Почему Inversion of Control назван так?


98

Слова invertили controlвообще не используются для определения инверсии контроля в определениях, которые я видел.

Определения

Википедия

инверсия управления (IoC) - это метод программирования, выраженный здесь в терминах объектно-ориентированного программирования, в котором связывание объектов во время выполнения связано с ассемблерным объектом и обычно не известно во время компиляции с использованием статического анализа. ~ http://en.wikipedia.org/wiki/Inversion_of_control

Мартин Фаулер

Инверсия управления - это распространенный шаблон в сообществе Java, который помогает объединять легкие контейнеры или собирать компоненты из разных проектов в единое приложение. ~ основано на http://www.martinfowler.com/articles/injection.html (перефразировано)


Так почему Inversion of Control называется Inversion of Control? Какой контроль инвертируется и чем? Есть ли способ определить инверсию управления, используя терминологию: инвертировать и контролировать ?


22
Реквизит для человека, который может объяснить это простым английским языком.
Роберт Харви

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

2
Смотрите также на StackOverflow: Что такое инверсия управления?
Изката

2
@Izkata, словари очень часто определяют фразы в терминах собственных терминов фразы или синонимичных терминов. То есть: определение силы природы использует слова «сила и природа» в своем определении, или определение термина «служба» использует слова «правила и служба», где правила являются синонимами терминов.
Корей Хинтон

3
По этой причине я предпочитаю использовать термин «автоматизированное внедрение зависимостей» или ADI вместо IoC; Сам Фаулер упоминает, что большинство современных парадигм в программировании имеют своего рода «инверсию управления», где этот термин обычно определяется как «повторное определение определений кода, подлежащего выполнению, и времени, в которое он должен быть выполнен внешнему система управления, когда такие определения традиционно делаются самой программой ». Все, от аппаратных прерываний до программирования, управляемого событиями, до виртуальных машин и многозадачности происходит с IoC.
KeithS

Ответы:


67

Допустим, у вас есть какой-то класс «репозитория», и этот репозиторий отвечает за передачу вам данных из источника данных.

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

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

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

Мартин Фаулер предлагает использовать термин «внедрение зависимостей» для описания этого типа инверсии управления, поскольку инверсию управления как концепцию можно применять более широко, чем просто введение зависимостей в метод конструктора.


3
Это пример внедрения зависимостей, но внедрение зависимостей является лишь подмножеством инверсии управления. Есть и другие вещи, совершенно не связанные с внедрением зависимости, которые практикуют инверсию контроля.
dsw88

1
@ mustang2009cobra: Спасибо. Примером является то, что я искал, а не исчерпывающий каталог случаев, когда это происходит, и термин «инверсия управления» наиболее тесно связан с DI, а не с передачей сообщений или другими подобными понятиями.
Роберт Харви

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

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

79

Я не думаю, что кто-то может объяснить это лучше, чем Мартин Фаулер, далее в статье, на которую вы ссылались .

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

Как он объясняет в параграфах выше, это не совсем то же самое, что и причина возникновения термина «инверсия контроля».

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

Вопрос в том, какой аспект контроля они инвертируют? Когда я впервые столкнулся с инверсией управления, он был основным элементом управления пользовательского интерфейса. Ранние пользовательские интерфейсы контролировались прикладной программой. У вас будет последовательность команд, таких как «Введите имя», «введите адрес»; ваша программа выдаст подсказки и ответит на каждый из них. В графических (или даже на экранных) интерфейсах пользовательский интерфейс будет содержать этот основной цикл, а ваша программа вместо этого будет предоставлять обработчики событий для различных полей на экране. Основной элемент управления программы был перевернут, отодвинут от вас в рамки.

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

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

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

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

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

Фаулер и другие решили, что термин «инверсия контроля» охватывает слишком много методов, и нам нужен был новый термин для конкретного примера создания объектов (внедрение зависимости), но к моменту заключения соглашения фраза «Контейнер IoC» "снял.

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


4
Обычно я не так много голосую, но это отличный ответ. 1 голос не будет достаточно. :(
RJD22

1
этот ответ действительно отвечает на все вопросы о путанице IoC и DI.
Али Умайр

32

Вот «обычные» программы управления потоком, которые обычно следуют:

  • Выполнять команды последовательно
  • Вы сохраняете контроль над потоком управления программы

Инверсия управления «инвертирует» этот поток управления, то есть он переворачивает его на голову:

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

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

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


19
[вставить обязательную «по-советски» шутку сюда]
Роберт Харви

2
Шучу в сторону, то, что я думаю, вы описываете, больше похоже на передачу сообщений, которая была одним из основных пунктов ОО на протяжении десятилетий.
Роберт Харви

3
@JimmyHoffa - Это совсем не так - смотрите здесь . Инверсия управления является более общей концепцией, чем создание зависимостей, а внедрение зависимостей является лишь одной из форм IoC.
Ли

7
@JimmyHoffa: я не согласен. Это такое же правильное определение Инверсии Контроля, как и вашего ответа или ответа Роберта, и именно это путаница описывает Фаулер, когда придумывает фразу «Внедрение зависимости» (то, что вы оба называете IoC).
pdr

5
Я согласен с этим утверждением @pdr: «Инверсия управления означает все, что инвертирует управляющую структуру программы из классического процедурного дизайна». Это то, что я описываю, и это общая инверсия контроля. Инъекция зависимостей - это тип IoC, но это не единственное, что практикует IoC
dsw88

13

Это о том, кто контролирует создание зависимостей.

Традиционно, когда класс / метод должен использовать другой класс (зависимость), он создается экземпляром класса / метода напрямую. Он контролирует свои зависимости.

С помощью Inversion of Control (IoC) вызывающая сторона передала зависимость, следовательно, она создает зависимость. Вызывающая сторона контролирует зависимости.

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


1
Непростой английский. Провал.
Роберт Харви

2
@RobertHarvey - насколько просто?
Одд

4
@ РобертХарви, а? Это кажется довольно простым для меня ... Что не ясно? Слово "создание экземпляров" или "зависимости"?
Джимми Хоффа

@JimmyHoffa: см. Мой ответ, который обеспечивает конкретный пример. IoC - это один из тех проблемных терминов, который используют все, но никто не объясняет; люди используют контейнеры IoC в программах, которые не требуют их, потому что они неправильно понимают. Тем не менее, я думаю, что в этом ответе было несколько правок ниндзя. :)
Роберт Харви

Этот и пост be @ dsw88 выше довольно понятны для меня. Послушайте, если бы я никогда не слышал об IoC, я бы интуитивно подумал: «О, кто контролирует что-то, переворачивается с одной стороны на другую». Хорошая работа на этом!
Оливер Уильямс

2

Обычно код более высокого уровня вызывает (т.е. контролирует) код более низкого уровня. Main () вызывает функцию (), функция () вызывает функцию libraryFunction ().

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

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


1

Вот простой обзор:

  1. Контроль относится к тому, что программа делает дальше
  2. На верхнем уровне обычно есть две вещи, которые контролируют элемент управления: само приложение и пользователь

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

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

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


0

Давайте попробуем понять это на двух примерах.

Пример 1

В прежние времена приложения, используемые для генерации командных приглашений, принимали пользовательские вводы один за другим Сегодня фреймворки пользовательского интерфейса создают экземпляры различных элементов пользовательского интерфейса, перебирая различные события этих элементов пользовательского интерфейса (например, наведение мыши, щелчок и т. Д.), А пользовательские / основные программы предоставляют хуки (например, прослушиватели событий пользовательского интерфейса в Java) для прослушивания этих событий. Таким образом, основной поток элементов управления «контроль» перемещается из пользовательской программы в среду пользовательского интерфейса. В прежние времена это было в пользовательской программе.

Пример 2

Рассмотрим класс CustomerProcessorниже:

class CustomerProcessor
{
    SqlCustRepo custRepo = new SqlCustRepo(); 
    private void processCustomers()
    {
            Customers[] custs = custRepo.getAllCusts();
    }
}

Если я хочу processCustomer()быть независимым от какой-либо реализации getAllCusts(), а не только от той, которая предоставлена SqlCustRepo, мне нужно будет избавиться от строки: SqlCustRepo custRepo = new SqlCustRepo()и заменить ее чем-то более общим, способным принимать различные типы реализаций, так что они processCustomers()будут просто работать для любая предоставленная реализация. Вышеупомянутый код (создание необходимого класса с SqlCustRepoпомощью логики основной программы) является традиционным способом, и он не достигает этой цели отделения processCustomers()от реализации getAllCusts(). При инверсии управления контейнер создает экземпляр необходимого класса реализации (как указано, скажем, в конфигурации xml), внедряет его в логику основной программы, которая связывается в соответствии с указанными хуками (скажем, с помощью @Autowiredаннотации или getBean()метода в среде Spring).

Посмотрим, как это можно сделать. Рассмотрим код ниже.

Config.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <bean id="custRepo" class="JsonCustRepo" />
</beans>

CustRepo.java

interface ICustRepo 
{ ... }

JsonCustRepo.java

class JsonCustRepo implements CustRepo
{ ... }

App.java

class App
{
    public static void main(String[] args) 
    {
        ApplicationContext context = new ClassPathXmlApplicationContext("Config.xml");
        ICustRepo custRepo = (JsonCustRepo) context.getBean("custRepo");
    }
}

Мы также можем иметь

class GraphCustRepo implements ICustRepo { ... }   

а также

<bean id="custRepo" class="GraphCustRepo">

и нам не нужно будет менять App.java.

Над контейнером (который является платформой Spring) лежит ответственность за сканирование XML-файла, создание экземпляра bean-компонента определенного типа и внедрение его в пользовательскую программу. Пользовательские программы не имеют контроля над тем, какой класс создается.

PS: IoC является общей концепцией и достигается многими способами. Приведенные выше примеры достигают этого путем внедрения зависимости.

Ссылка: статья Мартина Фаулера .

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.