Чому саме так називається інверсія управління?


98

Слова invertабо controlвзагалі не використовуються для визначення інверсії управління у визначених мною означеннях.

Визначення

Вікіпедія

інверсія управління (IoC) - це метод програмування, виражений тут через об'єктно-орієнтоване програмування, в якому зв'язок об'єктів пов'язаний під час виконання об'єктом асемблера і, як правило, не відомий під час компіляції за допомогою статичного аналізу. ~ http://en.wikipedia.org/wiki/Inversion_of_control

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

Інверсія управління - це загальна модель у спільноті Java, яка допомагає проводити легкі контейнери або збирати компоненти з різних проектів у згуртовану програму. ~ на основі http://www.martinfowler.com/articles/injection.html (перероблено)


То чому інверсія управління називається Інверсія управління? Який контроль перевертається і чим? Чи є спосіб визначити Інверсію управління за допомогою термінології: інвертування та управління ?


22
Реквізити для людини, яка може пояснити це простою англійською мовою.
Роберт Харві

2
Якщо у визначенні використовується слово, яке воно визначає, це буде збій словника. Подібна логіка застосовується і тут.
Ізката

2
Дивіться також на StackOverflow: Що таке інверсія управління?
Ізката

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

3
Я вважаю за краще використовувати термін "автоматизована ін'єкційна залежність" або АДІ замість IoC саме з цієї причини; Сам Фаулер зазначає, що більшість сучасних парадигм у програмуванні мають якусь "інверсію управління", де цей термін визначається як "звільнення визначень коду, який повинен виконуватися, і часу, коли він повинен бути виконаний до зовнішнього система контролю, коли такі визначення традиційно здійснюються самою програмою ". Все, від апаратних переривань до керованого подіями програмування до віртуальних машин і багатозадачності відбувається з IoC.
KeithS

Відповіді:


67

Скажімо, у вас є якийсь клас "сховища", і це сховище відповідає за передачу вам даних із джерела даних.

Репозиторій може встановити з'єднання з джерелом даних самостійно. Але що робити, якщо це дозволило вам перейти в з'єднання з джерелом даних через конструктор сховища?

Дозволяючи абоненту надати з'єднання, ви від'єднали залежність з'єднання джерела даних від класу сховища, дозволяючи будь-якому джерелу даних працювати з сховищем, а не лише тим, яке репозиторій визначає.

Ви перевернули контроль , передавши відповідальність за створення з'єднання з класу сховища абоненту.

Мартін Фаулер пропонує використовувати термін "залежність впорскування" для опису цього типу інверсії управління, оскільки Інверсія управління як поняття може бути застосована ширше, ніж просто введення залежностей у конструкторському методі.


3
Це приклад введення залежності, але введення залежності є лише підмножиною інверсії контролю. Є й інші речі, абсолютно не пов'язані з ін'єкцією залежності, які практикують інверсію контролю.
dsw88

1
@ mustang2009cobra: Дякую Прикладом є те, що я був після, а не вичерпний каталог випадків, коли це відбувається, а термін "Інверсія контролю" найбільш тісно пов'язаний з DI, а не передачею повідомлень чи іншими подібними поняттями.
Роберт Харві

Правда, ін'єкційна залежність є найпоширенішим прикладом ІОК, тому приклад DI має найбільш сенс. Можливо, невелика зміна вашої відповіді, вказуючи, що це приклад DI, який є найвидатнішим типом IoC?
dsw88

1
Це саме те, що я шукав! Дуже багато прикладів та визначень IoC не чітко вказують на те, який елемент управління перетворюється. Я усвідомлюю, що в IoC може бути більше, ніж просто введення залежності, але зараз щось остаточно клацнуло в моєму мозку. Дякую!
Корей Хінтон

79

Я не думаю, що хтось може пояснити це краще, ніж це робить Мартін Фаулер, далі в статті, з якою ви посилаєтесь .

Для цієї нової породи контейнерів інверсія полягає в тому, як вони шукають реалізацію плагіна. У моєму наївному прикладі лістер підшукував реалізацію пошуку, безпосередньо інстанціюючи його. Це перешкоджає пошуковій програмі бути плагіном. Підхід, який використовують ці контейнери, полягає у тому, щоб будь-який користувач плагіну дотримувався певної конвенції, яка дозволяє окремому модулю асемблера вводити реалізацію в лістер.

Як він пояснює у вищезазначених абзацах, це не зовсім збігається з причиною виникнення терміну "Інверсія контролю".

Коли ці контейнери говорять про те, наскільки вони є настільки корисними, оскільки вони реалізують "Інверсію управління", я закінчуюся дуже здивовано. Інверсія керування є загальною характеристикою рамок, тому сказати, що ці легкі контейнери є особливими, оскільки вони використовують інверсію управління, як мовляв, моя машина особлива, тому що у неї є колеса.

Питання: який аспект контролю вони перевертають? Коли я вперше зіткнувся з інверсією управління, це було в головному управлінні користувальницьким інтерфейсом. Ранні користувацькі інтерфейси контролювалися прикладною програмою. У вас була б послідовність команд, таких як "Введіть ім'я", "Введіть адресу"; ваша програма буде керувати підказками та отримувати відповідь на кожне з них. З графічними (або навіть на екрані) інтерфейсами інтерфейс інтерфейсу міститиме цей основний цикл, а ваша програма натомість надає обробники подій для різних полів на екрані. Головне управління програмою було перевернуто, віддалене від вас до рамки.

Ось чому він продовжує вводити термін "Вприскування вприскування", щоб охопити цю конкретну реалізацію Інверсії управління.

В результаті я думаю, що нам потрібна більш конкретна назва цього шаблону. Інверсія контролю є надто загальним терміном, і тому люди вважають його заплутаним. В результаті багато дискусій з різними захисниками IoC ми зупинилися на назві Dependency Injection.

Щоб трохи уточнити: Інверсія управління означає все, що перевертає структуру управління програми від класичного процесуального дизайну.

За минулих днів ключовим прикладом цього було дозволити рамці обмінюватися зв’язком між інтерфейсом користувача та кодом, а не залишати свій код безпосередньо для генерації інтерфейсу користувача.

У більш пізні часи (коли такі рамки значною мірою домінували, тому питання вже не було актуальним), прикладом було перевернення контролю над інстанцією об'єктів.

Фоулер та інші вирішили, що термін «Інверсія контролю» охоплює занадто багато прийомів, і нам потрібен новий термін для конкретного прикладу інстанції об'єктів (залежність впорскування), але до моменту укладення цієї угоди словосполучення «контейнер IoC "зняли.

Це багато каламутить воду, тому що контейнер IoC є специфічним видом вприскування залежностей, але введення залежностей - це специфічний вид інверсії управління. Ось чому ви отримуєте такі заплутані відповіді, куди б ви не дивилися.


4
Я зазвичай не так голосую, але це чудова відповідь. 1 голос буде недостатньо. :(
RJD22

1
ця відповідь справді відповідає на все питання про плутанину IoC та DI.
Алі Умайр

32

Ось "звичайні" програми управління потоком зазвичай виконують:

  • Запускайте команди послідовно
  • Ви підтримуєте контроль над потоком управління програмою

Інверсія управління "інвертує" цей керуючий потік, тобто він перевертає його на голову:

  • Ваша програма більше не контролює потік. Замість того, щоб викликати команди, як вам здається, ви чекаєте, коли хтось інший зателефонує вам .

Цей останній рядок є важливим. Замість того, щоб дзвонити комусь іншому, коли вам це подобається, хтось інший дзвонить вам, коли вони відчувають, що це подобається.

Поширений приклад цього - веб-рамки, такі як Rails. Ви визначаєте контролери, але ви насправді не вирішуєте, коли їм зателефонують. Rails дзвонить їм, коли вирішить, що є потреба.


19
[вставте сюди обов’язковий жарт "по-радянськи"]
Роберт Харві

2
Відмовляючись убік, те, що я думаю, що ви описуєте, більше схоже на передачу повідомлень, яка є основою роботи OO протягом десятиліть.
Роберт Харві

3
@JimmyHoffa - Це зовсім не так - дивіться тут . Інверсія управління є більш загальним поняттям, ніж створення залежностей, а введення залежності - лише одна форма ІОК.
Лі

7
@JimmyHoffa: Я не згоден. Це настільки ж правильне визначення Інверсії управління, як і ваша відповідь, або Роберта, саме те плутанина описує Фоулер, коли формує фразу Dependency Injection (яку ви обидва називаєте IoC).
пдр

5
Я погоджуюся з цим твердженням @pdr: "Інверсія управління означає все, що інвертує структуру управління програми від класичного процесуального дизайну". Ось що я описую, і це загальна інверсія управління. Ін'єкція в залежність - це тип IoC, але це не єдине, що практикує IoC
dsw88

13

Йдеться про те, хто контролює існування залежностей.

Традиційно, коли клас / метод потребує використання іншого класу (залежності), він створюється безпосередньо класом / методом. Він контролює свої залежності.

З інверсією управління (IoC), абонент перейшов у залежність, отже, він створює залежність. Абонент контролює залежності.

Контроль того, де встановлена ​​залежність, було перевернуто - замість того, щоб бути в "нижній частині", де код, який йому потрібен, він створюється в "верхньому" місці, де викликається код, який потребує.


1
Не простою англійською. Збій.
Роберт Харві

2
@RobertHarvey - наскільки рівнина звичайна?
Одід

4
@RobertHarvey Так? Мені це здається досить простим ... Що не просто? Слово "інстанція" чи "залежності"?
Джиммі Хоффа

@JimmyHoffa: Дивіться мою відповідь, яка дає конкретний приклад. IoC - один із тих клопітних термінів, якими користуються всі, але ніхто не пояснює; люди використовують контейнери IoC у програмах, які не потребують їх, оскільки вони неправильно розуміють. Однак, я думаю, що ця відповідь мала кілька змін у ніндзя. :)
Роберт Харві

Це і публікація be @ dsw88 вище для мене досить зрозуміла. Подивіться, якби я ніколи не чув про IoC, я інтуїтивно подумав би: «Ой, де хто може контролювати щось перевертається з однієї сторони на іншу». Гарна робота над цим!
Олівер Вільямс

2

Зазвичай вищий код рівня виклику (тобто управління) код нижчого рівня. Основна () функція викликів (), функція () бібліотека викликівFunction ().

Це можна перевернути, тому бібліотека низького рівня внизу викликає функції вищого рівня.

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


1

Ось простий огляд:

  1. Контроль стосується того, що програма робить далі
  2. На найвищому рівні, як правило, дві речі, які керують керуванням: сама програма та користувач

За старих часів управління належало додатку першим, а користувачеві - другим. Якщо програмі щось потрібно від користувача, воно зупиниться і запитає, а потім перейде до свого наступного завдання. Взаємодія користувача надала переважно дані, а не контроль того, що додаток робив далі. Нам це зараз мало чуже, оскільки такий тип поведінки ми не бачимо дуже часто.

Якщо ми переключимо це навколо і дамо користувачеві первинний контроль, ми перевернули контроль. Це означає, що замість того, щоб користувач чекав на додаток, щоб дати йому щось робити, програма сидить навколо, чекаючи, коли користувач зможе щось зробити. GUI - чудовий приклад цього, і майже все, що цикл подій перевернув керування.

Зауважимо, що мій приклад знаходиться на найвищому рівні і що ця концепція інверсії управління може бути віднесена до різних шарів управління в рамках програми (тобто введення залежності). Ось чому так важко отримати пряму відповідь.


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()методом у весняній рамці).

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

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.

Над контейнером (який є весняним фреймом) несе відповідальність за сканування файлу xml, інстанціювання файлу конкретного типу та введення його в користувальну програму. Користувацька програма не має контролю над тим, який клас інстанціюється.

PS: IoC - це загальна концепція, яка досягається багатьма способами. Наведені вище приклади досягають цього шляхом введення залежності.

Довідка: стаття Мартіна Фаулера .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.