Внедрение зависимостей в C ++ - PullRequest
24 голосов
/ 09 декабря 2008

Это также вопрос, который я задал в комментарии в одном из разговоров google Мишко Хевери, посвященном внедрению зависимостей, но он был похоронен в комментариях.

Интересно, как на заводе / сборщике этап соединения зависимостей может работать в C ++.

т.е. у нас есть класс 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;
}

Какой ваш предпочтительный подход?

Ответы [ 8 ]

13 голосов
/ 09 декабря 2008

Этот разговор о Java и внедрении зависимостей.

В C ++ мы пытаемся NOT передавать RAW-указатели. Это связано с тем, что указатель RAW не имеет семантики владения, связанной с ним. Если у вас нет прав собственности, мы не знаем, кто несет ответственность за уборку объекта.

Я считаю, что большая часть внедрения зависимости от времени выполняется через ссылки в C ++.
В тех редких случаях, когда необходимо использовать указатели, оберните их в std :: unique_ptr <> или std :: shared_ptr <> в зависимости от того, как вы хотите управлять владельцем.
Если вы не можете использовать функции C ++ 11, используйте std :: auto_ptr <> или boost :: shared_ptr <>.

Я также хотел бы отметить, что стили программирования на C ++ и Java теперь настолько различаются, что применение стиля одного языка к другому неизбежно приведет к катастрофе.

9 голосов
/ 23 декабря 2009

Это интересно, DI в C ++ с использованием шаблонов:

http://adam.younglogic.com/?p=146

Я думаю, что автор делает правильные шаги, чтобы не переводить Java DI в C ++ слишком буквально. Стоит прочитать.

6 голосов
/ 10 мая 2010

Я недавно был укушен ошибкой DI. Я думаю, что это решает много проблем сложности, особенно автоматизированной части. Я написал прототип, который позволяет вам использовать DI довольно C ++, или, по крайней мере, я так думаю. Вы можете посмотреть пример кода здесь: http://codepad.org/GpOujZ79

Вещи, которые явно отсутствуют: нет границ, нет привязки интерфейса к реализации. Последнее довольно легко решить, первое, я понятия не имею.

Буду признателен, если у кого-то есть мнение по поводу кода.

3 голосов
/ 09 декабря 2008

Используйте RAII.

Передача необработанного указателя кому-либо - это то же самое, что и передача ему права собственности. Если это не то, что вы хотите сделать, вы должны дать им какой-то фасад, который также знает, как очистить рассматриваемый объект.

shared_ptr <> может сделать это; вторым аргументом его конструктора может быть объект функции, который знает, как удалить объект.

2 голосов
/ 23 декабря 2009

Исходя из моего собственного опыта, лучше всего иметь четкие правила владения. Для небольших конкретных объектов лучше всего использовать прямое копирование, чтобы избежать перекрестной зависимости.

Иногда взаимозависимость неизбежна, и нет четкого права собственности. Например, (m) экземпляры A владеют (n) экземплярами B, а некоторые экземпляры B могут принадлежать нескольким As. В этом случае лучшим подходом является применение подсчета ссылок к B способом, аналогичным подсчету ссылок COM. Любые функции, которые вступают во владение B *, должны сначала увеличить счетчик ссылок и уменьшить его при освобождении владения.

Я также избегаю использования boost :: shared_ptr, так как он создает новый тип (shared_ptr и B * становятся двумя разными типами). Я обнаружил, что это добавляет больше головной боли, когда я добавляю методы.

2 голосов
/ 09 декабря 2008

Обычно в C ++, когда вы все делаете правильно, в большинстве случаев вам вообще не нужно писать деструкторы. Вы должны использовать умные указатели для автоматического удаления вещей. Я думаю, что строитель не похож на владельца экземпляров ClassA и ClassB. Если вам не нравятся умные указатели, подумайте о времени жизни объектов и их владельцах.

2 голосов
/ 09 декабря 2008

Ситуация усложняется, если вы не решите вопрос о собственности раз и навсегда. Вам просто нужно решить в своей реализации, возможно ли, что зависимости живут дольше, чем объекты, в которые они внедрены.

Лично я бы сказал нет: объект, в который вводится зависимость, впоследствии будет очищен. Попытка сделать это с помощью компоновщика означает, что компоновщик должен будет жить дольше, чем как зависимость, так и объект, в который он введен. По моему мнению, это вызывает больше проблем, чем решает, потому что компоновщик не служит более полезной цели после завершения построения с внедрением зависимостей.

1 голос
/ 10 августа 2010

Вы также можете проверить FFEAD Dependency Injection . Он предоставляет DI на линии Spring для JAVA и имеет ненавязчивый способ иметь дело с вещами. Он также имеет много других важных функций, таких как встроенная поддержка AJAX, рефлексия, сериализация, интерпретатор C ++, бизнес-компоненты для C ++, ORM, обмен сообщениями, веб-службы, потоки и пул приложений и сервер приложений , которые поддерживает все эти функции.

...