Инъекция зависимостей в С++
Это также вопрос, который я задал в комментарии в одном из сообщений Miško Hevery google, который касался инъекции зависимостей, но он был похоронен в комментариях.
Интересно, как может работать factory/builder шаг проводки зависимостей вместе на С++.
т.е. мы имеем класс A, который зависит от B. Строитель будет выделять B в кучу, передать указатель на B в конструкторе A, а также выделить в куче и вернуть указатель на A.
Кто после этого очищается? Хорошо ли позволить строителю очистить его после его завершения? Кажется, это правильный метод, поскольку в разговоре говорится, что строитель должен установить объекты, которые, как ожидается, будут иметь одинаковое время жизни или, по крайней мере, более длительный срок службы (у меня также есть вопрос). Что я имею в виду в коде:
class builder {
public:
builder() :
m_ClassA(NULL),m_ClassB(NULL) {
}
~builder() {
if (m_ClassB) {
delete m_ClassB;
}
if (m_ClassA) {
delete m_ClassA;
}
}
ClassA *build() {
m_ClassB = new class B;
m_ClassA = new class A(m_ClassB);
return m_ClassA;
}
};
Теперь, если есть зависимость, которая, как ожидается, продлится дольше, чем время жизни объекта, мы вставляем ее (например, ClassC - это зависимость). Я понимаю, что мы должны изменить метод сборки на что-то вроде:
ClassA *builder::build(ClassC *classC) {
m_ClassB = new class B;
m_ClassA = new class A(m_ClassB, classC);
return m_ClassA;
}
Каков ваш предпочтительный подход?
Ответы
Ответ 1
Этот разговор посвящен внедрению Java и зависимостей.
В С++ мы попробуем NOT передать указатели RAW. Это связано с тем, что указатель RAW не имеет связанной с ним семантики собственности. Если у вас нет собственности, мы не знаем, кто несет ответственность за очистку объекта.
Я нахожу, что большая часть инъекции зависимостей времени выполняется через ссылки в С++.
В редких случаях, когда вы должны использовать указатели, оберните их в std:: unique_ptr < > или std:: shared_ptr < > в зависимости от того, как вы хотите управлять собственностью.
Если вы не можете использовать функции С++ 11, используйте std:: auto_ptr < > или boost:: shared_ptr < > .
Я бы также отметил, что стили программирования С++ и Java теперь настолько расходятся, что применение стиля одного языка к другому неизбежно приведет к катастрофе.
Ответ 2
Это интересно, DI в С++ с использованием шаблонов:
http://adam.younglogic.com/?p=146
Я думаю, что автор делает правильные шаги, чтобы не переводить Java DI на С++ слишком буквально. Стоит прочитать.
Ответ 3
Недавно я был укушен ошибкой DI. Я думаю, что он решает множество проблем сложности, особенно автоматизированную часть. Я написал прототип, который позволяет использовать DI в симпатичном С++-способе, или, по крайней мере, я так думаю. Вы можете посмотреть здесь пример кода: http://codepad.org/GpOujZ79
То, что явно отсутствует: нет области охвата, отсутствие привязки интерфейса к реализации. Последнее довольно легко решить, первое, я понятия не имею.
Буду признателен, если у кого-то есть мнение о коде.
Ответ 4
Используйте RAII.
Передача необработанного указателя на кого-то такая же, как передача им права собственности. Если это не то, что вы хотите сделать, вы должны дать им какой-то фасад, который также знает, как очистить объект, о котором идет речь.
shared_ptr < > может это сделать; второй аргумент его конструктора может быть функциональным объектом, который знает, как удалить объект.
Ответ 5
Все становится сложнее, если вы не решаетесь на вопрос о праве собственности раз и навсегда. Вам просто нужно решить в своей реализации, если возможно, что зависимости живут дольше, чем объекты, в которые они вставляются.
Лично я бы сказал нет: объект, в который вводится зависимость, будет очищаться после этого. Попытка сделать это через построитель означает, что строителю придется жить дольше, чем зависимость и объект, в который он вводится. Это вызывает больше проблем, чем решает, на мой взгляд, потому, что строитель не служит более полезной цели после завершения строительства с инжекцией зависимостей.
Ответ 6
В С++, как правило, когда вы делаете все правильно, вам вообще не нужно писать деструкторы в большинстве случаев. Вы должны использовать интеллектуальные указатели для автоматического удаления объектов. Я думаю, что построитель не похож на владельца экземпляров ClassA и ClassB. Если вы не любите использовать интеллектуальные указатели, вы должны подумать об объекте жизни и их владельцах.
Ответ 7
Основываясь на собственном опыте, лучше иметь четкие правила владения. Для небольших конкретных объектов лучше использовать прямую копию, чтобы избежать перекрестной зависимости.
Иногда кросс-зависимость неизбежна, и нет четкой собственности. Например, (m) экземпляры собственных (n) B экземпляров, а некоторые экземпляры B могут принадлежать нескольким As. В этом случае наилучшим подходом является использование подсчета ссылок для B, аналогично подсчету ссылок на COM. Любые функции, которые овладевают B *, должны сначала увеличить счетчик ссылок и уменьшить его при освобождении владения.
Я также избегаю использования boost:: shared_ptr, поскольку он создает новый тип (shared_ptr и B * становятся двумя разными типами). Я обнаружил, что при добавлении методов он приносит больше головных болей.
Ответ 8
Вы также можете проверить FFEAD Injection Dependency. Он предоставляет DI на линиях Spring для JAVA и имеет ненавязчивый способ борьбы с вещами. Он также имеет множество других важных функций, таких как встроенная поддержка AJAX, отражение, сериализация, интерпретатор С++, бизнес-компоненты для С++, ORM, Messaging, веб-сервисы, пулы потоков и сервер приложений который поддерживает все эти функции.