Я возвращаюсь к C++ после того, как провожу некоторое время на языках, управляемых памятью, и внезапно теряюсь в том, что является лучшим способом реализации внедрения зависимостей. (Я полностью продан DI, потому что я нашел, что это самый простой способ сделать дизайн, управляемый тестами, очень простым).
Теперь просмотр SO и Google принес мне довольно много мнений по этому вопросу, и я немного запутался.
В ответ на этот вопрос, Внедрение зависимостей в C++, кто-то предложил вам не передавать необработанные указатели даже для внедрения зависимостей. Я понимаю, что это связано с владением объектами.
Теперь право собственности на объекты также рассматривается (хотя и не достаточно подробно в моем состоянии;)) в печально известном руководстве по стилю Google: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Smart_Pointers
Так что я понимаю, что для того, чтобы прояснить, какой объект владеет какими-либо другими объектами, вы должны избегать передачи необработанных указателей. В частности, кажется, что против такого рода кодирования:
class Addict {
// Something I depend on (hence, the Addict name. sorry.)
Dependency * dependency_;
public:
Addict(Dependency * dependency) : dependency_(dependency) {
}
~Addict() {
// Do NOT release dependency_, since it was injected and you don't own it !
}
void some_method() {
dependency_->do_something();
}
// ... whatever ...
};
Если Dependency - это чисто виртуальный класс (так называемый Интерфейс бедняка), тогда этот код облегчает внедрение фиктивной версии Dependency (используя что-то вроде google mock).
Проблема в том, что я на самом деле не вижу проблем, с которыми я могу столкнуться с таким кодом, и почему я должен хотеть использовать что-то еще, кроме сырых указателей! Разве не ясно, откуда взялась зависимость?
Кроме того, я прочитал довольно много постов, намекающих на то, что в этой ситуации действительно следует использовать ссылки, так что этот код лучше?
class Addict {
// Something I depend on (hence, the Addict name. sorry.)
const Dependency & dependency_;
public:
Addict(const Dependency & dependency) : dependency_(dependency) {
}
~Addict() {
// Do NOT release dependency_, since it was injected and you don't own it !
}
void some_method() {
dependency_.do_something();
}
// ... whatever ...
};
Но потом я получаю и другие, не менее авторитетные советы против использования ссылок в качестве участника: http://billharlan.com/pub/papers/Managing_Cpp_Objects.html
Как вы видите, я не совсем уверен в относительных плюсах и минусах различных подходов, поэтому я немного запутался. Я сожалею, если это обсуждалось до смерти, или если это только вопрос личного выбора и последовательности внутри данного проекта... но любая идея приветствуется.
Резюме ответов
(Я не знаю, является ли это хорошим SO-тикетом для этого, но я добавлю пример кода для того, что я собрал из ответов...)
Из различных ответов вот что я, вероятно, в конечном итоге буду делать в моем случае:
- передать зависимости как ссылку (по крайней мере, чтобы убедиться, что NULL невозможен)
- в общем случае, когда копирование невозможно, явно запретите его и сохраните зависимости как ссылку
- в более редком случае, когда копирование возможно, сохраняйте зависимости как RAW-указатели
- пусть создатель зависимостей (какой-то фабрики) выбирает между выделением стека или динамическим размещением (и в последнем случае управление через интеллектуальный указатель)
- установить соглашение об отделении зависимостей от собственных ресурсов
Таким образом, я хотел бы получить что-то вроде:
class NonCopyableAddict {
Dependency & dep_dependency_;
// Prevent copying
NonCopyableAddict & operator = (const NonCopyableAddict & other) {}
NonCopyableAddict(const NonCopyableAddict & other) {}
public:
NonCopyableAddict(Dependency & dependency) : dep_dependency_(dep_dependency) {
}
~NonCopyableAddict() {
// No risk to try and delete the reference to dep_dependency_ ;)
}
//...
void do_some_stuff() {
dep_dependency_.some_function();
}
};
И для копируемого класса:
class CopyableAddict {
Dependency * dep_dependency_;
public:
// Prevent copying
CopyableAddict & operator = (const CopyableAddict & other) {
// Do whatever makes sense ... or let the default operator work ?
}
CopyableAddict(const CopyableAddict & other) {
// Do whatever makes sense ...
}
CopyableAddict(Dependency & dependency) : dep_dependency_(&dep_dependency) {
}
~CopyableAddict() {
// You might be tempted to delete the pointer, but its name starts with dep_,
// so by convention you know it is not your job
}
//...
void do_some_stuff() {
dep_dependency_->some_function();
}
};
Из того, что я понял, нет никакого способа выразить намерение "у меня есть указатель на некоторые вещи, но я им не владею", которое может применять компилятор. Поэтому мне придется прибегнуть к соглашению об именах здесь...
Хранится для справки
Как указал Мартин, следующий пример не решает проблему.
Или, если у меня есть конструктор копирования, что-то вроде:
class Addict {
Dependency dependency_;
public:
Addict(const Dependency & dependency) : dependency_(dependency) {
}
~Addict() {
// Do NOT release dependency_, since it was injected and you don't own it !
}
void some_method() {
dependency_.do_something();
}
// ... whatever ...
};