Полезно ли внедрение зависимостей в переполнение стека

C # использует Инъекция зависимости (DI) много иметь без потерь а также тестируемый Платформа. Для этого мне нужен interface и может быть DI или же Контейнер инверсии контроля (IoC) для разрешения моих случаев.

Но как вы делаете это в C ++? Я немного читал об этом, и кажется, что внедрение зависимостей в C ++ не такая большая тема, как в C #. В C ++ вы используете ссылка на объект — это способ использовать DI в C ++, верно?

Если моя теория со ссылками верна, есть ли что-то вроде контейнера, где я могу разрешить все ссылки? В C # у меня есть "bad class/bad project/assembly" который регистрирует все мои экземпляры в статический контейнер при запуске программы. Затем в каждом классе я могу создать экземпляр статического контейнера и разрешить конкретный экземпляр, возможно ли это в C ++?

Вы используете Dependency Injection (или как там это называется) в C ++? Если да, то как вы это используете? Есть ли сходство с C #?

20

Решение

Для этого мне нужен интерфейс и, возможно, контейнер для разрешения моих экземпляров. Но как вы делаете это в C ++?

Точно так же. Разница в том, что когда вы «программируете на интерфейс» в C #, вы «программируете на базовый класс» в C ++. Кроме того, у вас есть дополнительные инструменты в C ++, которых нет в C # (например, шаблоны на основе политик реализуют внедрение зависимостей, выбранное во время компиляции).

В C ++ вы используете ссылку на объект, это способ использования DI в C ++, верно?

Нет; это не способ использовать DI, это способ использовать DI в C ++.

Также учтите:

  • использовать указатель на объект (или умный указатель, в зависимости от случая)
  • использовать аргумент шаблона для политики (например, см. использование std :: default_delete в интеллектуальных указателях)
  • используйте лямбда-исчисление с введенными функторами / предикатами.

В C # у меня есть «плохой класс / плохой проект / сборка», который регистрирует весь мой экземпляр в статическом контейнере при запуске программы.

Если я правильно понимаю, вы устанавливаете все свои данные в этом статическом контейнере и используете их во всем приложении. Если это так, то вы неправильно используете внедрение зависимостей, потому что это нарушает закон Деметера.

это возможно в C ++?

Да, это вполне возможно (но вы не должны этого делать, потому что это нарушает закон Деметры). Взгляните на boost :: any (это позволит вам хранить гетерогенные объекты в контейнере, подобно хранению объектов object ссылка в C #).

Используете ли вы внедрение зависимости или как оно называется в C ++?

Да (и это называется внедрением зависимости :)).

Если да, то как вы это используете?

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

21

Другие решения

Использование внедрения зависимостей довольно просто в C ++. Просто определите интерфейс (чистый абстрактный базовый класс), который вы используете в качестве ссылки или указателя (или умного указателя) аргумента для конструктора или функции инициализации класса, в который вы хотите внедрить зависимость.

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

Очень просто.

12

Да, внедрение зависимостей полезно и в C ++. Нет причины, по которой этого не должно быть, потому что для этого не требуется определенный язык или синтаксис, а просто объектно-ориентированная классовая архитектура (по крайней мере, это, вероятно, самый обычный случай).

В то время как в C # есть только «указатели» на динамически размещаемые объекты, C ++ имеет несколько вариантов, таких как «нормальные» локальные переменные, несколько видов указателей, ссылки … кроме того, концепция семантики перемещения очень важна для этого.

В C ++ вы используете ссылку на объект, это способ использования DI
в C ++, верно?

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

есть ли что-то вроде контейнера, где я могу решить все это
Рекомендации? В C # у меня есть «плохой класс / плохой проект / сборка», который
зарегистрировать весь мой экземпляр в статический контейнер

Может быть, вы упускаете точку введения зависимости. Это не то же самое, что набор глобальных переменных. Но да, конечно, это возможно и в C ++. Есть классы, есть staticи это все, что нужно.

4

С C ++ 11 в качестве предела проекта я закончил кататься самостоятельно. Я свободно основал его на .NET Ninject API без отражения курса.

ServiceLocator

Обратите внимание, хотя он и называется ServiceLocator (поскольку он не выполняет сам Dependancy Injection), если вы используете привязки лямбда-функций и предпочтительно классы ServiceLocator :: Module, вы получаете Injection (не основанный на отражении), и он работает действительно очень хорошо (IMO)

#include <iostream>
#include <vector>
#include "ServiceLocator.hpp"
template <class T>
using sptr = std::shared_ptr<T>;

// Some plain interfaces
class IFood {
public:
virtual std::string name() = 0;
};

class IAnimal {
public:
virtual void eatFavouriteFood() = 0;
};// Concrete classes which implement our interfaces, these 2 have no dependancies
class Banana : public IFood {
public:
std::string name() override {
return "Banana";
}
};

class Pizza : public IFood {
public:
std::string name() override {
return "Pizza";
}
};

// Monkey requires a favourite food, note it is not dependant on ServiceLocator
class Monkey : public IAnimal {
private:
sptr<IFood> _food;

public:
Monkey(sptr<IFood> food) : _food(food) {
}

void eatFavouriteFood() override {
std::cout << "Monkey eats " << _food->name() << "\n";
}
};

// Human requires a favourite food, note it is not dependant on ServiceLocator
class Human : public IAnimal {
private:
sptr<IFood> _food;

public:
Human(sptr<IFood> food) : _food(food) {
}

void eatFavouriteFood() override {
std::cout << "Human eats " << _food->name() << "\n";
}
};

/* The SLModule classes are ServiceLocator aware, and they are also intimate with the concrete classes they bind to
and so know what dependancies are required to create instances */
class FoodSLModule : public ServiceLocator::Module {
public:
void load() override {
bind<IFood>("Monkey").to<Banana>([] (SLContext_sptr slc) {
return new Banana();
});
bind<IFood>("Human").to<Pizza>([] (SLContext_sptr slc) {
return new Pizza();
});
}
};

class AnimalsSLModule : public ServiceLocator::Module {
public:
void load() override {
bind<IAnimal>("Human").to<Human>([] (SLContext_sptr slc) {
return new Human(slc->resolve<IFood>("Human"));
});
bind<IAnimal>("Monkey").to<Monkey>([] (SLContext_sptr slc) {
return new Monkey(slc->resolve<IFood>("Monkey"));
});
}
};

int main(int argc, const char * argv[]) {
auto sl = ServiceLocator::create();

sl->modules()
.add<FoodSLModule>()
.add<AnimalsSLModule>();

auto slc = sl->getContext();

std::vector<sptr<IAnimal>> animals;
slc->resolveAll<IAnimal>(&animals);

for(auto animal : animals) {
animal->eatFavouriteFood();
}

return 0;
}
4

Если моя теория со ссылками верна, есть ли что-то вроде контейнера, где я могу разрешить все ссылки? В C # у меня есть «плохой класс / плохой проект / сборка», который регистрирует все мои экземпляры в статический контейнер при запуске программы. Затем в каждом классе я могу создать экземпляр статического контейнера и разрешить конкретный экземпляр, возможно ли это в C ++?

Это не то, как DI должен использоваться, вы не передаете свой контейнер всем своим «потребительским» классам. В хорошо разработанном приложении вы просто решаете несколько вопросов в точке входа, и все. Большую часть времени необходимость «разрешения» может быть заменена использованием фабрики, которая будет зарегистрирована, а затем введена.

У вас будет много проблем с тестированием кода в зависимости от статического класса. Я бы порекомендовал, если вы действительно хотите внедрить свой контейнер в своем клиентском классе, по крайней мере, в экземпляр и внедрить его, статические зависимости — это ад, проще было бы поиграть для модульного тестирования.

1
По вопросам рекламы [email protected]