Каков наилучший шаблон для управления несколькими версиями одного и того же дерева зависимостей в Guice? - PullRequest
5 голосов
/ 10 декабря 2010

Я хотел бы создать несколько версий одного и того же вида дерева / цепочки зависимостей, которые используют разные реализации для некоторых интерфейсов в этом дереве / цепочке.Какую практику Guice лучше использовать в таком случае?

Вот конкретный пример моей проблемы.

У меня есть интерфейс Writer, который потенциально может быть средством записи файлов илиписатель std-out, который будет располагаться на листе моей иерархии зависимостей.Примерно так:

interface Writer { ... }
class FileWriter implements Writer { ... }
class StdOutWriter implements 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

Есть ли хороший способ подключения без использования отдельного модуля Guice для1 и 2?

В идеале я хотел бы иметь класс ClientFactory, подобный этому:

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

Может кто-нибудь придумать способ подключить это с помощью этой фабрики, илиЛюбое другое средство?

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

1 Ответ

7 голосов
/ 11 декабря 2010

Это проблема ноги робота . Решение в основном заключается в использовании PrivateModule s для привязки каждого дерева зависимостей и предоставления только корня этого дерева. Есть несколько способов сделать это, но вот пример того, как вы обычно это делаете (есть множество вариантов, которые вы можете сделать в соответствии с вашими потребностями):

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) несколько отличается.

...