C # использует Инъекция зависимости (DI) много иметь без потерь а также тестируемый Платформа. Для этого мне нужен interface
и может быть DI или же Контейнер инверсии контроля (IoC) для разрешения моих случаев.
Но как вы делаете это в C ++? Я немного читал об этом, и кажется, что внедрение зависимостей в C ++ не такая большая тема, как в C #. В C ++ вы используете ссылка на объект — это способ использовать DI в C ++, верно?
Если моя теория со ссылками верна, есть ли что-то вроде контейнера, где я могу разрешить все ссылки? В C # у меня есть "bad class/bad project/assembly"
который регистрирует все мои экземпляры в статический контейнер при запуске программы. Затем в каждом классе я могу создать экземпляр статического контейнера и разрешить конкретный экземпляр, возможно ли это в C ++?
Вы используете Dependency Injection (или как там это называется) в C ++? Если да, то как вы это используете? Есть ли сходство с C #?
Для этого мне нужен интерфейс и, возможно, контейнер для разрешения моих экземпляров. Но как вы делаете это в C ++?
Точно так же. Разница в том, что когда вы «программируете на интерфейс» в C #, вы «программируете на базовый класс» в C ++. Кроме того, у вас есть дополнительные инструменты в C ++, которых нет в C # (например, шаблоны на основе политик реализуют внедрение зависимостей, выбранное во время компиляции).
В C ++ вы используете ссылку на объект, это способ использования DI в C ++, верно?
Нет; это не способ использовать DI, это способ использовать DI в C ++.
Также учтите:
В C # у меня есть «плохой класс / плохой проект / сборка», который регистрирует весь мой экземпляр в статическом контейнере при запуске программы.
Если я правильно понимаю, вы устанавливаете все свои данные в этом статическом контейнере и используете их во всем приложении. Если это так, то вы неправильно используете внедрение зависимостей, потому что это нарушает закон Деметера.
это возможно в C ++?
Да, это вполне возможно (но вы не должны этого делать, потому что это нарушает закон Деметры). Взгляните на boost :: any (это позволит вам хранить гетерогенные объекты в контейнере, подобно хранению объектов object
ссылка в C #).
Используете ли вы внедрение зависимости или как оно называется в C ++?
Да (и это называется внедрением зависимости :)).
Если да, то как вы это используете?
Как я описал выше (аргументы шаблона политики, внедренные функторы и предикаты в качестве компонентов многократного использования, внедрение объектов по ссылке, умный указатель или значение указателя).
Использование внедрения зависимостей довольно просто в C ++. Просто определите интерфейс (чистый абстрактный базовый класс), который вы используете в качестве ссылки или указателя (или умного указателя) аргумента для конструктора или функции инициализации класса, в который вы хотите внедрить зависимость.
Затем в модульном тесте внедрите фиктивный объект (экземпляр класса, унаследованный от абстрактного интерфейсного класса), а в реальном коде внедрите экземпляр реального класса (также наследующий от того же интерфейсного класса).
Очень просто.
Да, внедрение зависимостей полезно и в C ++. Нет причины, по которой этого не должно быть, потому что для этого не требуется определенный язык или синтаксис, а просто объектно-ориентированная классовая архитектура (по крайней мере, это, вероятно, самый обычный случай).
В то время как в C # есть только «указатели» на динамически размещаемые объекты, C ++ имеет несколько вариантов, таких как «нормальные» локальные переменные, несколько видов указателей, ссылки … кроме того, концепция семантики перемещения очень важна для этого.
В C ++ вы используете ссылку на объект, это способ использования DI
в C ++, верно?
Не только. Вы можете использовать все, что захотите, до тех пор, пока вы можете передавать что-то в метод класса, и это будет существовать до тех пор, пока существует объект класса. Все три вышеуказанные возможности могут сделать это (каждый из них с определенными ограничениями)
есть ли что-то вроде контейнера, где я могу решить все это
Рекомендации? В C # у меня есть «плохой класс / плохой проект / сборка», который
зарегистрировать весь мой экземпляр в статический контейнер
Может быть, вы упускаете точку введения зависимости. Это не то же самое, что набор глобальных переменных. Но да, конечно, это возможно и в C ++. Есть классы, есть static
и это все, что нужно.
С C ++ 11 в качестве предела проекта я закончил кататься самостоятельно. Я свободно основал его на .NET Ninject API без отражения курса.
Обратите внимание, хотя он и называется 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;
}
Если моя теория со ссылками верна, есть ли что-то вроде контейнера, где я могу разрешить все ссылки? В C # у меня есть «плохой класс / плохой проект / сборка», который регистрирует все мои экземпляры в статический контейнер при запуске программы. Затем в каждом классе я могу создать экземпляр статического контейнера и разрешить конкретный экземпляр, возможно ли это в C ++?
Это не то, как DI должен использоваться, вы не передаете свой контейнер всем своим «потребительским» классам. В хорошо разработанном приложении вы просто решаете несколько вопросов в точке входа, и все. Большую часть времени необходимость «разрешения» может быть заменена использованием фабрики, которая будет зарегистрирована, а затем введена.
У вас будет много проблем с тестированием кода в зависимости от статического класса. Я бы порекомендовал, если вы действительно хотите внедрить свой контейнер в своем клиентском классе, по крайней мере, в экземпляр и внедрить его, статические зависимости — это ад, проще было бы поиграть для модульного тестирования.