C ++:依赖注入,循环依赖和回调

时间:2011-01-14 07:32:12

标签: c++ dependency-injection callback dispatcher circular-dependency

考虑(高度简化)以下案例:

class Dispatcher {
public:
    receive() {/*implementation*/};  // callback
}

class CommInterface {
public:
    send() = 0;  // call
}

class CommA : public CommInterface {
public:
    send() {/*implementation*/};
}

系统中的各个类通过调度程序发送消息。调度程序使用comm发送。一旦回答了答案,comm就会将其中继回调度程序,然后调度程序将其发送回适当的原始发件人。 Comm是多态的,可以从设置文件中读取要选择的实现。

Dispatcher依赖于comm以便发送。 Comm依赖于调度程序来回调。因此,这里存在循环依赖,我似乎无法实现dependency injection原则(即使在遇到this好的博客文章之后)。

更新

  1. Comm取决于第三方代码(如 有各种第三方,Comm 是多态的)。 Comm收到了 它自己的功能,它传递它 到调度员的接收功能(在 练习有多种这样的 各种参数的功能 套)。可能的呼叫是:

    CommA::receive_3(/*parameters set a*/) {
        /* some parameters manipulation */
        dispatcher_ptr->receive_5(/*parameters set b*/);
        dispatcher_ptr->receive_6(/*parameters set c*/);
    }
    
  2. 至少当前Dispatcher“知道”哪个Comm使用它在其构造函数中接收的参数来使用,因此它无法在其初始化列表中初始化Comm。它可以很容易地接收到comm的shared_ptr并完成它,但这需要首先初始化Comm,Comm需要一个指向Dispatcher的指针用于回调...当然我可以在Comm中实现一个名为{{的函数1}}但不会违反依赖注入吗?

4 个答案:

答案 0 :(得分:0)

如果Comm在施工中不需要调度员,但每个send接受调度员使用,会怎样?也就是说,

class Comm
{
    virtual void send(Dispatcher *d) = 0;
};

除非Comm由于不同的原因需要绑定到单个Dispatcher,否则这应该消除构造时循环依赖。

答案 1 :(得分:0)

类之间的循环依赖并不总是一个问题,而且往往是不可避免的。博客所讨论的问题是,在应用依赖注入之后,构建A& B变得不可能(因为A需要B和B需要A,两者都是为了构建)。这是一种特殊的循环依赖。

如果A或B在没有另一个的情况下有意义,则避免了问题:不需要另一个的那个类可以在没有它的情况下构建。

例如,假设A是某种调度程序,它将消息发送到任意一组B对象:

struct A
{
  A() {}
  void add_b(B const& b) { bs.push_back(b); }
  void dispatch(int num) { std::for_each(bs.begin(), bs.end(), [num](B & b) { b.message(num); }); }
  void something_b_uses();
};

struct B
{
  B(A* a) : my_a(a) {}
 ...
  void message(int num) { a->something_b_uses(); }
};

有一个没有问题的循环依赖。

答案 2 :(得分:0)

我意识到,在许多类使用Dispatcher实例的情况下,只有Dispatcher实例使用CommA实例。因此,Dispatcher对CommA的依赖不应该外部化,即CommA对象不应该被“注入”Dispatcher,而不是Dispatcher中任何其他内部定义的变量。

有一分钟我认为我的问题可能会产生误导,但后来我意识到这源于对依赖注入的一个非常基本的误解。 只有在无法进行内部管理时,才应将外部化外部化。因此,我将离开这个问题和后代的答案:)

答案 3 :(得分:0)

我认为如果你为Dispatcher提供一个单独的接口类,比如你的名字约定DispatcherInterface,那么循环依赖就应该消失了,因为现在你可以创建第三个组件(比如DispatcherCommProviderInterface)了这个接口可以知道Comm和Dispatcher,但Comm和Dispatcher都不会知道任何关于这样的DispatcherCommProvider实现(最多他们会知道他们的接口)

接口:

// does not know anything about DispatcherCommProviderInterface  or CommInterface 
class DispatcherInterface {
public:
    receive() = 0;  // callback
}

// does not know anything about DispatcherCommProviderInterface  or DispatcherInterface
class CommInterface {
public:
    send() = 0;  // call
}


class DispatcherCommProviderInterface {
public:
    CommInterface* getComm() = 0;  
    DispatcherInterface* getDispatcher() = 0; 
    void setComm(CommInterface*) = 0;  
    void setDispatcher(DispatcherInterface*) = 0;
}

实现:

class CommA : public CommInterface {
public:
    send() {/*implementation using some DispatcherCommProviderInterface  */};
}


class Dispatcher : public DispatcherInterface  {
public:
    receive() {/*implementation using some DispatcherCommProviderInterface  */};  // callback
}

现在你的依赖注入策略只需要创建适当的DispatcherCommProviderInterface实现(并可能将它连接到Comm和Dispatcher实例)