В чем разница между инверсией управления и внедрением зависимости в C ++? - PullRequest
13 голосов
/ 16 апреля 2010

Я недавно читал о DI и IoC в C ++. Я немного сбит с толку (даже после прочтения связанных вопросов здесь на SO) и надеялся на некоторые разъяснения.

Мне кажется, что знакомство с STL и Boost приводит к использованию внедрения зависимостей совсем немного. Например, допустим, я сделал функцию, которая нашла среднее значение диапазона чисел:

template <typename Iter>
double mean(Iter first, Iter last)
{
    double sum = 0;
    size_t number = 0;
    while (first != last)
    {
        sum += *(first++);
        ++number;
    }
    return sum/number;
};

Является ли это (т.е. использование итераторов вместо доступа к самой коллекции) внедрением зависимости? Инверсия контроля? Ни

Давайте посмотрим на другой пример. У нас есть класс:

class Dice
{
public:
    typedef boost::mt19937 Engine;
    Dice(int num_dice, Engine& rng) : n_(num_dice), eng_(rng) {}
    int roll()
    {
        int sum = 0;
        for (int i = 0; i < num_dice; ++i)
            sum += boost::uniform_int<>(1,6)(eng_);
        return sum;
    }
private:
    Engine& eng_;
    int n_;
};

Это похоже на внедрение зависимости. Но разве это инверсия контроля?

Кроме того, если я что-то упустил, кто-нибудь может мне помочь? Кажется, это естественный способ сделать что-то, поэтому, если это все, что нужно для инъекции зависимости, почему людям трудно использовать ее?

Ответы [ 2 ]

17 голосов
/ 16 апреля 2010

Инверсия управления - это очень общая концепция, имеющая различные значения в зависимости от типа «контроля», о котором вы говорите. Зависимость инъекций является специфической формой.

Инверсия управления и итерации

В этом случае «управление» означает «управление потоком».

Я думаю, что ваш первый пример, включающий итерацию, на самом деле не является инверсией управления, потому что этот код явно выполняет управление потоком. Инверсия управления отделяет действие, которое нужно выполнить, от управления потоком. Это может выглядеть так (простите за мой java / C #):

SumVisitor sumVisitor = new SumVisitor();
collection.AcceptVisitor(sumVisitor);
int sum = sumVisitor.GetSum();

Объект посетителя что-то делает для каждого элемента коллекции, который он посещает, например, обновить поле счетчика суммы. Но у него нет контроля над тем, как или когда он вызывается коллекцией, следовательно, инверсия управления . Вы также можете реализовать MedianVisitor, MeanVisitor, MaximumVisitor и так далее. Каждый из них реализует общий интерфейс IVisitor с методом Visit(Element).

Для коллекции верно обратное: он не знает, что делает посетитель, и просто заботится об управлении потоком, вызывая visitor.Visit(element) для каждого элемента в коллекции. Различные реализации посетителей выглядят одинаково для коллекции.

Инверсия управления и построение графов объектов

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

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

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

Еще один способ отказаться от управления построением графов объектов - это шаблон Service Locator , хотя он имеет свои недостатки .

1 голос
/ 16 апреля 2010

Позвольте мне попытаться ответить.

Ваш первый пример - ни тот, ни другой. Это просто шаблон.

Для того, чтобы это было внедрение зависимости, РЕАЛИЗАЦИЯ должна была бы быть выбрана и предоставлена ​​шаблону.

Чтобы это был IoC, шаблон должен быть предоставлен во время выполнения (не во время компиляции) для типа IMPLEMENTATION и использоваться как реализация функции «mean ()» (представьте себе фабрику, которая предоставляет функцию среднего реализации)

Ваш второй пример выглядит как потребитель DI / IoC. Код, который отправляет реализацию Engine в ваш класс, будет компонентом DI / IoC.

Надеюсь, это верно и помогает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...