Что такое инверсия контроля? - PullRequest
1618 голосов
/ 06 августа 2008

Инверсия управления (или IoC) может привести к путанице при первом обнаружении.

  1. Что это?
  2. Какую проблему это решает?
  3. Когда целесообразно использовать, а когда нет?

Ответы [ 34 ]

10 голосов
/ 29 октября 2015

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

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

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

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

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

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

9 голосов
/ 04 ноября 2017

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

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

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

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

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

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

  • Приложению требуется Foo, которому нужен Bar, которому нужен Bim, поэтому:
  • Приложение создает Bim
  • Приложение создает бар и дает ему Bim
  • Приложение создает Foo и дает ему Bar
  • Приложение вызывает 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, когда думаете, что есть (или могут быть) альтернативные реализации класса.

8 голосов
/ 16 февраля 2017

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

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

enter image description here

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

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

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

enter image description here

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

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

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

...

8 голосов
/ 19 апреля 2015

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

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

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

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

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

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

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

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

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

5 голосов
/ 01 октября 2017

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

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

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

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

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

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

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

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

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

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

Справка:

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

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

4 голосов
/ 24 декабря 2017

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

3 голосов
/ 03 июня 2016

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

Чтобы узнать больше об этом вы можете прочитать мой блог Здесь

  1. Что это?

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

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

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

  1. Когда это уместно, а когда нет?

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

3 голосов
/ 19 сентября 2008
  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. Это становится более уместным, когда у вас есть проект среднего размера, несколько более сложный. Я бы сказал, что это уместно, как только ты начинаешь чувствовать боль.

3 голосов
/ 16 июля 2015

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

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

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

3 голосов
/ 13 мая 2015

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

...