在Guice中管理同一依赖关系树的多个版本的最佳模式是什么?

时间:2010-12-10 17:11:38

标签: java dependency-injection guice

我想实现同一种依赖树/链的几个版本,它们对该树/链中的某些接口使用不同的实现。在这种情况下使用的最佳Guice练习/模式是什么?

以下是我的问题的具体示例。

我有一个Writer接口,可能是文件编写器或std-out编写器,它位于我的依赖层次结构的叶子中。像这样:

interface Writer { ... }
class FileWriter implements Writer { ... }
class StdOutWriter implements Writer { ... }

另一个记录器接口用于在writer上添加一个间接层。例如:

interface Logger { ... }

class LoggerImpl{
  @Inject
  public Logger(Writer out){ ... }
  public void log(String message){ out.println(message); }
}

然后有一个使用记录器的客户端。

class Client{
  @Inject
  public Client(Logger logger){ ... }
  public void do(){ logger.log("my message"); }
}

现在,我想在我的程序中使用两种类型的层次结构:

  1. 客户 - > LoggerImpl - > FileWriter的
  2. 客户 - > LoggerImpl - > StdOutWriter
  3. 是否有一种很好的方法可以在不使用1和2的单独Guice模块的情况下进行连接?

    理想情况下,我希望有一个ClientFactory类,如下所示:

    interface ClientFactory{
      public Client stdOutClient();
      public Client fileClient(); 
      //or fileClient(File outputFile) for extra points ;)
    }
    

    有人能想出一种方法来使用这个工厂或任何其他方式连接它吗?

    我还想要一个可以扩展到我有更多种类更长的依赖树/链的情况的解决方案。谢谢!

1 个答案:

答案 0 :(得分:7)

这是robot legs问题。解决方案基本上是使用PrivateModule来绑定每个依赖关系树并仅公开该树的根。有几种方法可以做到这一点,但这里有一个例子,说明你通常如何做(根据你的需要你可以做很多变化):

public class ClientModule extends PrivateModule {
  private final Writer writer;
  private final Class<? extends Annotation> annotationType;

  public ClientModule(Writer writer, Class<? extends Annotation> annotationType) {
    this.writer = writer;
    this.annotationType = annotationType;
  }

  @Override protected void configure() {
    bind(Writer.class).toInstance(writer);
    bind(Logger.class).to(LoggerImpl.class);
    expose(Client.class).annotatedWith(annotationType);
  }
}

public class ClientFactoryModule extends AbstractModule {
  private final File file;

  public ClientFactoryModule(File file) {
    this.file = file;
  }

  @Override protected void configure() {
    install(new ClientModule(new StdOutWriter(), StdOut.class));
    install(new ClientModule(new FileWriter(file), FileOut.class));
    bind(ClientFactory.class).to(ClientFactoryImpl.class);
  }
}

public class ClientFactoryImpl implements ClientFactory {
  private final Client stdOutClient;
  private final Client fileClient;

  @Inject public ClientFactoryImpl(@StdOut Client stdOutClient, 
                                   @FileOut Client fileClient) {
    this.stdOutClient = stdOutClient;
    this.fileClient = fileClient;
  }

  ...
}

方法Client fileClient(File)的方案虽然有点不同。