image

Вступление


Наверняка первый вопрос, который возник у вас при взгляде на заголовок, был "Шта?". На самом деле я просто перевел фразу "Инверсия управления, внедрение зависимости" в Google Translate на китайский, а затем обратно. Зачем? Затем, что на мой взгляд, это хорошая иллюстрация того, что происходит на самом деле. Люди вокруг путают, коверкают и извращают эти понятия. По долгу службы я провожу много интервью, и 90% того, что я слышу, когда задаю вопрос про DI — честно говоря, откровенный бред. Я сделал поиск по Хабру и нашел несколько статей, которые пытаются раскрыть эту тему, но не могу сказать, что они мне сильно понравились (ладно, ладно, я проглядел только три первых страницы, каюсь). Здесь же на Хабре я встречал в комментариях такую расшифровку IoC, как Injection of Container. Кто-то всерьез предполагает, что есть некий механизм инъекции контейнеров, который сосуществует где-то рядом с DI, и, видимо, даже делает нечто похожее. Только с контейнерами. Мда. На самом деле понять внедрение зависимости очень просто, надо всего лишь…

Удивительно, но факт — эта штука со «всего лишь...» действительно работает! Иначе вы бы здесь не оказались, не так ли?

Ричард Фейнман был удивительным рассказчиком, умевшим ясно и доступно объяснять весьма сложные вещи (посмотрите, хотя бы, это видео). Джоэл Спольски считает, что по-настоящему умный программист обязательно должен уметь изъясняться на человеческом языке (а не только на Си). Ну и, наверное, практически каждому известен афоризм Альберта Эйнштейна: "Если вы что-то не можете объяснить шестилетнему ребёнку, вы сами этого не понимаете". Конечно же, я не собираюсь сравнивать вас с шестилетними детьми, но тем не менее постараюсь рассказать про DI, IoC и еще один DI максимально просто и понятно.

NB. А вы знали, что помимо внедрения зависимости через конструктор и сеттер, существует еще и третий способ — внедрение посредством интерфейса? Хоть это и выходит за рамки данной статьи, но, держу пари, что либо вы сейчас открыли для себя кое-что новенькое, либо по крайней мере опустили уже заготовленный тухлый помидор.

Инверсия управления (Inversion of Control)


Что вы делаете в свой выходной? Может быть, читаете книги. Может быть, играете в видеоигры. Может быть, пишете код, а может потягиваете пиво за просмотром очередного сериала (вместо того, чтобы засаживать Марс яблонями). Но что бы вы ни делали, весь день в вашем полном распоряжении и только вы управляете его распорядком.

Однако, к сожалению, выходные заканчиваются, наступает понедельник, и приходится идти на работу (если, конечно, она у вас есть). По условиям трудового договора вы должны быть на месте в 8 утра. Вы работаете до полудня. Потом у вас перерыв на обед, а затем еще четыре часа кипучей деятельности. Наконец, в 17:00 вы выбираетесь из офиса и отправляетесь домой, где снова можете расслабиться и вмонтировать пивандрия. Чувствуете разницу? Вы больше не управляете своим дневным расписанием, это делает кое-кто другой — ваш работодатель.

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

Но в один прекрасный день в кабинет входит босс и сообщает пренеприятнейшее известие — консоль больше не в моде, миром правят графические интерфейсы, а значит все надо переделать. Будучи современным и гибким (речь не только о ваших занятиях йогой) программистом, вы сразу принимаетесь за внесение изменений. Для этого вы подключаете GUI-фреймворк и пишете код обработки событий. Если нажата вот эта кнопка, то надо сделать то-то и то-то. А если пользователь изменил свой выбор в выпадающем списке, то не обойтись без вот этого и этого. Все идет хорошо, но тут вы понимаете, что раньше было как-то по-другому. А кто, собственно, вызывает эти обработчики событий, которые вы так усердно программируете? Кто вообще определяет, куда и когда нажал пользователь? Что вообще происходит? Где мои носки? GUI-фреймворк оказался явно хитрее, чем вы думали, и перехватил у вас управление потоком выполнения приложения.

Это и есть Inversion of Control — очень абстрактный принцип, постулирующий факт задания потока выполнения некой внешней по отношению к вам сущностью.

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

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

Инверсия зависимости (Dependency Inversion)


Это та самая буква D в аббревиатуре SOLID — принцип, говорящий о том, что:

  • модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций;
  • абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

Немного путаная формулировка, поэтому рассмотрим следующий пример (для примеров я буду использовать C#).

public class Foo {
  private Bar itsBar;

  public Foo() {
     itsBar = new Bar();
  }
}

Проблема здесь заключается в том, что класс Foo зависит от конкретного класса Bar. По той или иной причине — в целях расширяемости, переиспользуемости или тестируемости ради — может встать задача их разделить. Согласно принципу инверсии зависимости для этого следует ввести между ними промежуточную абстракцию.

public class Foo {
  private IBar itsBar;

  public Foo() {
     itsBar = new Bar();
  }
}

Диаграмма UML наглядно демонстрирует оба варианта.

image

Сложности начинаются, когда спрашиваешь, а где же здесь, собственно, инверсия? Основополагающая идея, без понимания которой невозможно ответить на этот вопрос, заключается в том, что интерфейсы принадлежат не своим реализациям, а использующим их клиентам. Имя интерфейса IBar вводит в заблуждение и заставляет рассматривать связку IBar + Bar как единое целое. В то же время истинным владельцем IBar является класс Foo, и если принять это во внимание, то направление связи между Foo и Bar действительно обратится вспять.

image

Внедрение зависимости (Dependency Injection)


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

public class Foo {
  private IServer itsServer;

  public Foo() {
     itsServer = new Bar();
  }
}

Чтобы избавить класс Foo от такой неприятной обязанности, хорошо было бы вынести код инстантинации куда-то в другое место и инкапсулировать его там (поскольку все мы чрезвычайно прагматичны и не любим ничего писать дважды). Сделать это можно двумя способами — используя либо Service Locator, либо Dependency Injection.

Service Locator — это такой реестр соответствия абстракций и их реализаций. Вы скармливаете ему интересующий вас интерфейс, а в ответ получаете готовый экземпляр конкретного класса. Выглядит это примерно так:

public class Foo {
  private IServer itsServer;

  public Foo() {
     itsServer = ServiceLocator.Resolve<IServer>();
  }
}

Нюанс заключается в том, что класс Foo теперь совершенно не зависит от класса Bar, но по-прежнему управляет его инстантинацией. Как мы уже знаем, избежать этого можно инвертировав поток управления, т.е. передав оное управление в руки некоего внешнего механизма. Dependency Injection и является таким механизмом, реализуемым в виде фреймворков под названием IoC-контейнеры:

public class Foo {
  private IServer itsServer;

  public Foo(IServer server) {
     itsServer = server;
  }
}

Заключение


На самом деле IoC-контейнер — настолько дурацкое название, что навскидку даже сложно придумать что-то хуже. Оно совершенно ничего не говорит о том, чем на самом деле занимается, вводя в заблуждение десятки все новых и новых программистов каждый день. Абсолютно любой фреймворк можно назвать IoC-контейнером, так как он по определению реализует инверсию управления и является оболочкой для некоего кода общего назначения. Это термин был (и продолжает быть) настолько отвратительным, что Мартин Фаулер придумал другой — внедрение зависимости.

Подытожим. Мы используем Dependency Inversion, чтобы разделить модули абстракцией, Dependency Injection, чтобы избавиться от инстантинации вручную, а реализуем это посредством фреймворка, построенного по принципу Inversion of Control. И ничто из этого не является синонимами, поэтому IoC-контейнеры — яркий пример того, как можно намертво всех запутать с помощью одного единственного неудачного термина.

Надеюсь, в этом маленьком опусе у меня получилось донести до вас разницу между этими вещами, и вы больше никогда их не перепутаете. А если перепутает кто-то из ваших коллег, то вы сможете просто и понятно объяснить им, в чем они не правы.
Поделиться с друзьями
-->

Комментарии (49)


  1. MonkAlex
    08.02.2017 20:53
    +1

    В то же время истинным владельцем IBar является класс Foo, и если принять это во внимание, то направление связи между Foo и Bar действительно обратится вспять.
    Нелогично. Интерфейс, на то и интерфейс, что им никто не владеет, а он просто описывает абстракцию или какую то общую идею. Почему кто-то кем-то должен владеть то?

    Нюанс заключается в том, что класс Foo теперь совершенно не зависит от класса Bar
    но зависит от ServiceLocator, от которого таким образом начинает зависеть буквально каждый метод, а значит переиспользовать код, не переиспользовав ServiceLocator — невозможно. Раньше зависимость была прямо в классе, теперь — она где то в инициализации приложения видимо. Потому что неизвестно кто где и как заполняет ассоциации ServiceLocator.


    1. Cromathaar
      08.02.2017 21:11
      +2

      Нелогично. Интерфейс, на то и интерфейс, что им никто не владеет, а он просто описывает абстракцию или какую то общую идею. Почему кто-то кем-то должен владеть то?

      Такова была идея автора этого принципа — Роберта Мартина. Если эту идею игнорировать, то вообще непонятно, что за инверсия там имеется в виду. В общем-то это чистая условность.

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

      Совершенно верно, а еще зависимость от IBar/IServer так и остается для клиента класса Foo неявной.


    1. VolCh
      09.02.2017 09:47

      Нелогично. Интерфейс, на то и интерфейс, что им никто не владеет, а он просто описывает абстракцию или какую то общую идею. Почему кто-то кем-то должен владеть то?

      Абстракция или общая идея никому не нужна. :) Но в общем и в целом реализуемые провайдером интерфейсы определяются (в идеальном мире) требованиями клиентов. Если провайдер реализует какой-то интерфейс, который клиент не использует, то интерфейс останется безхозным. :)


      1. MonkAlex
        09.02.2017 09:57
        +1

        Интерфейс может быть требованием клиента, а может — особенностями реализации с сервера.

        Да, я понимаю о чём вы, но прямо таки «принадлежность» другому классу только потому, что класс его использует — кривая и опасная формулировка, потому как это не так. Особенно для распространенных интерфейсов, которые используются в нескольких местах.


        1. VolCh
          09.02.2017 10:19

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


          1. MonkAlex
            09.02.2017 10:22

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

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


    1. DmitryITWorksMakarov
      09.02.2017 10:07
      +1

      Что означает «класс является владельцем интерфейса»? С точки зрения с#, я так понимаю, это означает: в какой сборке должен быть описан интерфейс…

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

      С другой стороны есть такой сценарий использования: сборка содержит несколько реализаций интерфейса и сам интерфейс, а наружу поставляет объекты некоторой фабрикой. Получается такая самодостаточная библиотечка. Но тогда, чтобы расширить множество реализаций этого интерфейса, то придется тянуть за собой и старые реализации, что не всегда надо. Таким образом опять лучше держать интерфейс в отдельной сборке.

      В общем, интерфейс он сам по себе…


      1. Cromathaar
        09.02.2017 10:14
        +1

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


        1. vintage
          11.02.2017 20:53

          Можете вкратце описать его идею?


          1. Cromathaar
            13.02.2017 15:20

            Если совсем вкратце, то можно вот тут почитать — http://dimakudr.blogspot.ru/2010/07/blog-post.html (плюс там еще вторая часть есть).


            1. vintage
              13.02.2017 21:25

              Спасибо, большое.


  1. sleeply4cat
    08.02.2017 23:09

    Я не понимаю, как управлять внедрением зависимостей.
    У нас есть, допустим, поставщик объектов IPlayer в одной библиотеке и получатель в другой. Контейнер берёт реализацию по умолчанию и внедряет, всё хорошо. Но тут в другом классе нам понадобилась не реализация LocalPlayer, а RemotePlayer. Окей, мы аннотируем поставщика и получателя (либо жонглируем одноразовыми интерфейсами, не так важно), добавляя и тому, и другому некий связующий идентификатор. Всё работает. Славненько. Затем у нас появляются MigratedPlayer, MobilePlayer, MicrosoftStorePlayer и ещё два десятка. Все они висят в одной глобальной области видимости, соединённые простыми строковыми id либо разделяя один интерфейс, что не проверяется при компиляции. Каждый получатель, несмотря на то, что получает объект IPlayer, вынужден указывать, объект какого типа (чтиай: какого класса) он хочет получить. И в итоге мы имеем запутанную и оттого слабо поддерживаемую и не поддающуюся автоматическому рефакторингу систему, где всё держится вместе крайне неочевидным образом, и, по сути, теряется изначальный замысел, поскольку клиент вынужден знать, к какому поставщику он обращается, но вынужден делать это неудобным и ненадёжным образом.

    Есть ли у вас ложка для того, чтобы размешать эту кашу? ._.


    1. eugeneego
      08.02.2017 23:45
      +2

      Каждый получатель, несмотря на то, что получает объект IPlayer, вынужден указывать, объект какого типа (чтиай: какого класса) он хочет получить

      Значит ему не нужен интерфейс, а нужен конкретный класс.

      Суть в том, что получателю все равно какую конкретную реализацию ему дадут, он сможет работать с любой.


      1. sleeply4cat
        09.02.2017 00:14

        Окей, а если ресурсы одинаковы с точки зрения объектной модели, но несут в себе разные данные? Соединения с разными БД, например?


        1. eugeneego
          09.02.2017 01:32
          +6

          Если данные идеологически разные, то зачем их в один интерфейс пытаться объединить?

          А если данные одинаковы, но просто разные способы получения этих данных, допустим через разные каналы связи, то потребителю как раз не важно, как именно получаются данные, он знает, что предоставленный ему объект имеет интерфейс для получения этих данных.


    1. onyxmaster
      08.02.2017 23:45
      +1

      Я знаю, что я таким ответом себе не заработаю популярности, но советую прочесть Dependency Injection in .NET.


      1. sleeply4cat
        09.02.2017 00:09

        Для меня это неэффективно по банальной причине: я джавист :)
        но, пожалуй, почитать что-нибудь действительно стоит.


        1. onyxmaster
          09.02.2017 15:55
          +1

          О, в случае этой книги это совершенно не принципиально.


    1. Cromathaar
      08.02.2017 23:57

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


      1. sleeply4cat
        09.02.2017 00:05

        Но в каких случаях применять его стоит?


        1. Cromathaar
          09.02.2017 00:19

          Тогда, когда можно, грубо говоря, «один раз сконфигурировать и забыть», избавившись таким образом от написания оператора new или вызовов Service Locator'а каждый раз, когда вам нужен экземпляр класса, на который указывает зависимость. Здесь, скорее, стоит вопрос, когда надо использовать инверсию зависимостей, а когда нет, а это отдельная большая тема для дискуссий.


        1. Michael_SL
          09.02.2017 00:31

          Использование DI предполагает, что вашем клиенту все равно какая реализация IPlayer будет ему предоставлена. Классический пример, это тестирование — например, вам не нужно чтобы тест слал статистику по игрокув Windows Store, и вы поставляете класс реализующий IPlayer, но не взаимодействущий с сервисом Windows Store.


  1. unsafePtr
    09.02.2017 00:09

    Представьте себе ваше приложение, разделите его условно на слои. Например слой доступа к данным, бизнес логика, пользовательский интерфейс. Теперь когда вы провели грань, попробуйте их описать так, что бы каждый слой был максимально независим от другого. IoC это собственно и есть способ реализовать ваши слои независимыми друг от друга. Dependency Injection и Service Locator являются способами реализации самого контейнера. При этом Service Locator считается анти-паттерном, так как ведёт к тому что наши слои будут зависимы от конкретного контейнера, что в принципе немного не укладывается в саму суть IoC контейнера. Хотелось бы увидеть побольше статей на эту тему, так как дискутировать можно очень долго


    1. Cromathaar
      09.02.2017 00:29
      +3

      IoC это собственно и есть способ реализовать ваши слои независимыми друг от друга

      Чтобы сделать слои независимыми друг от друга, надо разорвать связь, т.е. убрать ссылку на класс B из класса A вообще :) Но это не в наших интересах, т.к. тогда приложение не сможет выполнять свои функции. В наших интересах эту связь ослабить, а делается это путем введения промежуточной абстракции (интерфейса или абстрактного класса). Понадобиться это может, если мы захотим, например, протестировать класс A изолированно от класса B (тогда мы заменим B фэйковой реализацией (моком) и подсунем его A). Или если мы предполагаем, что в дальнейшем мы можем использовать в системе другую реализацию B, но при этом не хотим, чтобы изменения вообще хоть как-то касались класса А. А вот как мы собираемся инстанцировать класс B — это уже вопрос отдельный, и именно при ответе на него и появляются фабрики, сервис локаторы и депенденси инжекшены.


      1. VolCh
        09.02.2017 10:08

        Чтобы сделать слои независимыми друг от друга, надо разорвать связь, т.е. убрать ссылку на класс B из класса A вообще :)

        Так введение интерфейса IB в слое, где лежит класс A и убирает ссылку на класс B из класса A. Класс А зависит теперь только от абстрактной (в большинстве случаев) сущности (в широком смысле слова) в своём слое. Дело клиентов слоя класса A (верхний слой, приложение в целом, тестовый фреймворк и т. п.) передать классу A нужную им зависимость явно, посредством IoC-контейнера или ещё как. Класс B теперь зависимость другого слоя, того же (или выше) зависимостью которого является класс A, в слое класса A зависимости от вышележащих слоев теперь нет, он самодостаточный (если это самый нижний слой), его можно скомпилировать в отдельную сборку/бандл, к которой не нужно линковать вышележащие слои.


        1. Cromathaar
          09.02.2017 10:31

          С тем маленьким нюансом, что изначально слой с А зависел от слоя с B, а после введения IB уже слой с B стал зависеть от слоя с А. Таким образом мы не разрываем полностью связь между слоями, а лишь ослабляем ее и меняем направление, получая слабое зацепление. В результате получается подобная штука — https://habrahabr.ru/post/269589/


          1. VolCh
            09.02.2017 11:35
            +1

            Как разработчикам слоя А нам всё равно, что клиенты слоя A зависели только от A, может даже не догадываясь, что есть B, а теперь зависят от A и IB :) Мы-то от B не зависим больше, а они как зависели от нашего слоя, так и зависят.

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


  1. gearbox
    09.02.2017 08:44

    public class Foo {
      private IBar itsBar;
    
      public Foo() {
         itsBar = new Bar();
      }
    }
    

    Читать как

    public class Foo {
      private IBar itsBar;
    
      public Foo() {
         itsBar = new IBar();
      }
    }
    

    или я чего то не понимаю?


    1. gearbox
      09.02.2017 08:49

      Согласен, не понимаю. Теперь понял. Прошу не пинать, сообщение уже не удаляется.


      1. Cromathaar
        09.02.2017 10:34

        Да, использована майкрософтовская конвенция добавлять к именам интерфейсов букву I, раз уж примеры на C#.


        1. PopeyetheSailor
          09.02.2017 17:18

          Речь не о том, у вас, в статье опечатка и используется new Bar(); вместо new IBar();, о чем он и пытается вам сообщить.


          1. Cromathaar
            09.02.2017 17:26

            IBar() — это интерфейс, а интерфейсы не инстанцируются. Эту свою ошибку и осознал gearbox :)


            1. PopeyetheSailor
              10.02.2017 01:09

              А, тогда извините.
              Я, дяденька, не настоящий сварщик, просто это выглядело как-то, логично, что ли :)


              1. gearbox
                10.02.2017 22:14
                +1

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


  1. 3aicheg
    09.02.2017 09:01

    Наверняка первый вопрос, который возник у вас при взгляде на заголовок, был «Шта?».

    У меня он при взгляде на КДПВ возник.


  1. staticlab
    09.02.2017 10:21

    public class Foo {
      private IServer itsServer;
    
      public Foo(IServer server) {
         itsServer = server;
      }
    }

    У вас же получился типичный DI через конструктор, не?


    1. Cromathaar
      09.02.2017 10:40

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


      1. staticlab
        09.02.2017 10:54

        Во введении к статье вы написали буквально следующее:


        А вы знали, что помимо внедрения зависимости через конструктор и сеттер, существует еще и третий способ — внедрение посредством интерфейса?

        Общий стиль введения подразумевает, что читатель уже знаком с концепциями IoC и DI хотя бы путанно и в общих чертах, но хотя бы что-то. Кроме того, вы обязались рассказать про "еще один DI".


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


        1. Cromathaar
          09.02.2017 11:13

          Совершенно верно, поэтому я и написал, что это «выходит за рамки данной статьи». Два DI — это Dependency Inversion и Dependency Injection, два понятия, которые путают так же, как и Dependency Injection и Inversion of Control. Цель статьи — дать понимание, что мы используем Dependency Inversion, чтобы разделить модули асбтракцией, Dependency Injection, чтобы избавиться от инстантинации вручную, а реализуем это посредством фреймворка, построенного по принципу Inversion of Control. И ничто из этого не является синонимом друг друга.


          1. staticlab
            09.02.2017 11:23

            Теперь понял, что вы имели в виду. В любом случае, описывать изначально в статье две сущности одной аббревиатурой не совсем красиво по отношению к читателю. Поэтому, кстати, "инверсия зависимостей" на самом деле "принцип инверсии зависимостей" (и SOLID есть набор принципов), потому его лучше бы писать явно как Dependency Inversion Principle, что сокращается как DIP, во избежание путаницы с Dependency Injection.


            1. VolCh
              09.02.2017 11:37
              +1

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

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


          1. ghost404
            11.02.2017 14:32

            Цель статьи — дать понимание, что мы используем Dependency Inversion, чтобы разделить модули асбтракцией, Dependency Injection, чтобы избавиться от инстантинации вручную, а реализуем это посредством фреймворка, построенного по принципу Inversion of Control. И ничто из этого не является синонимом друг друга.

            Вот это стоило написать в статье, а то лично я понял всё только после вашего комментария.
            И да, фраймворки, как правило, реализуют IoC по средствам Service Locator у себя в недрах.


            1. Cromathaar
              13.02.2017 15:28
              +1

              Хорошее замечание, добавил это в подытоживающий вывод.


        1. Garond
          09.02.2017 16:09
          +1

          Раньше не сталкивался с понятием interface injection, но некоторые IoC фреймворки поддерживают такую фичу как инъекция в метод. Получается что-то типа такого:

          public class Foo
          {
              IBar _bar;
              Qux _qux;
          
              [Inject]
              public Init(IBar bar, Qux qux)
              {
                  _bar = bar;
                  _qux = qux;
              }
          }
          

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


          1. Deosis
            10.02.2017 06:28

            В данном случае это похоже на гибрид инъекции через конструктор и сеттер.


            1. Garond
              11.02.2017 19:44

              Такой подход удобен, например, в Unity3D, где нельзя (не рекомендуется) объявлять конструктор для классов, наследованных от MonoBehaviour. Приходится делать инъекцию в метод.


  1. tlv
    09.02.2017 16:09

    Управление впрыском — задача определения необходимого объема впрыскиваемого форсунками инжекторного ДВС топлива.

    Управление базируется (зависит) на данных с некоторых датчиков (ДМРВ, лямбды, положения заслонки, колен/распредвала, других).

    «Инверсии зависимостей управления впрыском» — как при взгляде на название, так и на КДПВ, не подумал «Шта?». Подумал, что статья про альтернативный алгоритм управления форсунками. Может, про rusEfi наконец свежая статья. Может, про что другое. Но не «Шта?»


    1. Cromathaar
      09.02.2017 16:13

      Забавно, но «задача определения необходимого объема впрыскиваемых» зависимостей — это очень даже смежная тема :)


  1. orcy
    11.02.2017 23:03
    +1

    Мне встречалась такая характеристика:
    > «Dependency Injection» is a 25-dollar term for a 5-cent concept.

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