1

I have a simple question, and I'm not even sure it has an answer but let's try. I'm coding in C++, and using dependency injection to avoid global state. This works quite well, and I don't run in unexpected/undefined behaviours very often.

However I realise that, as my project grows I'm writing a lot of code which I consider boilerplate. Worse : the fact there is more boilerplate code, than actual code makes it sometimes hard to understand.

Nothing beats a good example so let's go :

I have a class called TimeFactory which creates Time objects.

For more details (not sure it's relevant) : Time objects are quite complex because the Time can have different formats, and conversion between them is neither linear, nor straightforward. Each "Time" contains a Synchronizer to handle conversions, and to make sure they have the same, properly initialized, synchronizer, I use a TimeFactory. The TimeFactory has only one instance and is application wide, so it would qualify for singleton but, because it's mutable, I don't want to make it a singleton

In my app, a lot of classes need to create Time objects. Sometimes those classes are deeply nested.

Let's say I have a class A which contains instances of class B, and so on up to class D. Class D need to create Time objects.

In my naive implementation, I pass the TimeFactory to the constructor of class A, which passes it to the constructor of class B and so on until class D.

Now, imagine I have a couple of classes like TimeFactory and a couple of class hierarchies like the one above : I loose all the flexibility and readability I'm suppose to get using dependency injection.

I'm starting to wonder if there isn't a major design flaw in my app ... Or is this a necessary evil of using dependency injection ?

What do you think ?

4

2 回答 2

4

在我的幼稚实现中,我将 TimeFactory 传递给 A 类的构造函数,后者将其传递给 B 类的构造函数,依此类推,直到 D 类。

这是依赖注入的常见误用。除非 A 类直接使用 TimeFactory,否则它永远不会看到、知道或访问 TimeFactory。D 实例应使用 TimeFactory 构造。然后应该用你刚刚构建的 D 实例来构建 C 实例。然后是 B 和 C,最后是 A 和 B。现在您有一个 A 实例,它间接拥有一个可以访问 TimeFactory 的 D 实例,而 A 实例从未看到 TimeFactory 直接传递给它。

Miško Hevery 在此视频中谈到了这一点。

于 2012-12-21T16:53:07.357 回答
1

全球状态并不总是邪恶的,如果它是真正的全球性的话。通常存在工程权衡,并且您对依赖注入的使用已经引入了比使用单例接口或全局变量更多的耦合:A 类现在知道 B 类需要 a TimeFactory,这通常比 A 类更详细地描述 B 类需要。B类和C类以及C类和D类也是如此。

考虑以下使用单例模式的解决方案:

  1. TimeFactory(抽象)基类提供对应用程序的`TimeFactory 的单例访问:

  2. 将该单例设置一次,设置为一个具体的子类TimeFactory

  3. 拥有使用该单例的所有访问TimeFactory权限。

这会创建全局状态,但会使该全局状态的客户端与其实现的任何知识分离。

这是一个潜在实现的草图:

class TimeFactory
{
  public:
  // ...
  static TimeFactory* getSingleton(void) { return singleton; }

  // ...
  protected:
  void setAsSingleton(void)
  {
    if (singleton != NULL) {
      // handle case where multiple TimeFactory implementations are created
      throw std::exception();  // possibly by throwing
    }
    singleton = this; 
  }

  private:
  static TimeFactory* singleton = NULL;
};

每次TimeFactory实例化 的子类时,您都setAsSingleton可以在其构造函数或其他地方调用它。

于 2012-08-05T05:28:02.323 回答