В чем разница между инверсией управления и зависимостью в С++?

Недавно я читал о DI и IoC на С++. Я немного запутался (даже после прочтения связанных вопросов здесь на 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_;
};

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

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

Ответ 1

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 2

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

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

Для того, чтобы быть инъекцией зависимостей, было бы выбрано ОСУЩЕСТВЛЕНИЕ и предоставлено шаблону.

Для того чтобы он был IoC, шаблон должен был быть предоставлен во время выполнения (не время компиляции) для типа РЕАЛИЗАЦИЯ и использоваться как реализация функции "среднее()" (подумайте о factory, который обеспечивает реализация средних функций)

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

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