Инверсия контроля является ценностно-стойким методом, который используется для модульной системы и отделения компонентов друг от друга.
Низкая связь всегда является преимуществом: она упрощает автоматическое тестирование компонентов и делает код лучше соответствующим принцип единой ответственности.
Среди способов объявить зависимость от другого класса (сервисный локатор, внедрение свойства, вызывающее открытый метод / устанавливающее открытое свойство …), внедрение конструктора кажется наилучшим подходом.
Хотя это, вероятно, самый сложный (по крайней мере, из перечисленных трех) для реализации, он имеет значительные преимущества:
Каковы плюсы / минусы множества вариантов, предлагаемых C ++ для выполнения инъекции через конструктор?
class object
{
public:
object(dependency d) : dep_(d) {}
private:
dependency dep_;
};
Работает только в случае dependency
класс полностью не имеет статуса, то есть не имеет членов. На практике это случается редко, потому что dependency
класс может хранить свою собственную зависимость.
class object
{
public:
object(dependency *d) : dep_(d)
{
if (d == nullptr)
throw std::exception("null dependency");
}
private:
dependency *dep_;
};
Это работает как настоящая инъекция. Мы должны проверить переданный указатель для nullptr
значение.
object
класс не владеет dependency
класс, таким образом, это обязанность вызова кода, чтобы убедиться, что object
уничтожен до dependency
объект.
В реальном приложении это иногда очень трудно проверить.
#define DISALLOW_COPY_AND_ASSIGN(Class) \
Class(const Class &) = delete; \
Class &operator=(const Class &) = delete
class object
{
public:
object(dependency &d) : dep_(d) {}
DISALLOW_COPY_AND_ASSIGN(object);
private:
dependency &dep_;
};
Ссылка не может быть нулевой, так что в этой перспективе это немного безопаснее.
Однако такой подход создает дополнительные ограничения для object
класс: он не должен копироваться, так как ссылка не может быть скопирована. Вы должны либо вручную переопределить оператор присваивания и конструктор копирования, чтобы остановить копирование, либо наследовать его от чего-то вроде boost::noncopyable
,
Как и в случае с необработанным указателем, ограничение владения на месте. Вызывающий код должен обеспечивать правильный порядок уничтожения для обоих классов, в противном случае ссылка становится недействительной, и приложение завершает работу с нарушением доступа.
Если зависимость является константной ссылкой:
class object
{
public:
object(const dependency &d) : dep_(d) {}
private:
const dependency &dep_;
};
Вы должны обратить внимание на тот факт, что object
класс принимает ссылки на временные объекты:
dependency d;
object o1(d); // this is ok, but...
object o2(dependency()); // ... this is BAD.
Более подробная информация:
class object
{
public:
object(std::shared_ptr<dependency> d) : dep_(d)
{
if (!d)
throw std::exception("null dependency");
}
private:
std::shared_ptr<dependency> dep_;
};
Аналогичен необработанному указателю, но владение контролируется механизмом интеллектуального указателя.
Еще нужно проверить nullptr
в теле конструктора.
Основным преимуществом является dependency
контроль времени жизни объекта: нет необходимости для вызывающего приложения правильно контролировать порядок уничтожения (но учтите, что Вы должны быть очень осторожны при разработке ваших API с std::shared_ptr
).
Однажды dependency
класс больше не используется, он автоматически уничтожается shared_ptr
деструктор.
Есть случаи, когда shared_ptr
Собственные объекты не уничтожаются (так называемые циклические ссылки). Однако при внедрении в конструктор циклические зависимости невозможны из-за определенного четко определенного порядка построения.
Это работает, конечно, если никакие другие методы впрыска не используются в приложении.
Интеллектуальный указатель имеет небольшие накладные расходы, но в большинстве случаев это не является реальной проблемой.
Более подробная информация:
Других решений пока нет …