Может ли Guice автоматически создавать экземпляры разных классов на основе параметра?

Стандартная фабрика объектов может выглядеть так:

interface I { ... }
class A implements I { ... }
class B implements I { ... }

class IFactory {
    I getI(int i) {
        switch (i) {
        case 1: return new A();
        default: return new B();
        }
    }
}

Можно ли настроить привязки так, чтобы переключение было выполнено для меня, то есть все, что я делаю, это вызываю getInstance или inject? Я смотрел на вспомогательную инъекцию, но это, кажется, другая тема: https://code.google.com/p/google-guice/wiki/AssistedInject

1 ответ

Решение

Похоже, вы ищете MapBinder, который является частью функции Multibindings. Обратите внимание, что вам все равно нужно будет вставить какой-то IFactory или другой заводской интерфейс, потому что getInstance не принимает параметр, как ваш getI делает, и вам все равно нужно будет установить отображение от целочисленного до реализации класса где-то.

MapBinder стиле

class IModule extends AbstractModule {
  @Override public void configure() {
    MapBinder<Integer, I> myBinder =
        MapBinder.newMapBinder(binder(), Integer.class, I.class);
    myBinder.addBinding(1).to(A.class);
    // Add more here.
  }
}

// You can even split the MapBinding across Modules, if you'd like.
class SomeOtherModule extends AbstractModule {
  @Override public void configure() {
    // MapBinder.newMapBinder does not complain about duplicate bindings
    // as long as the keys are different.
    MapBinder<Integer, I> myBinder =
        MapBinder.newMapBinder(binder(), Integer.class, I.class);
    myBinder.addBinding(3).to(C.class);
    myBinder.addBinding(4).to(D.class);
  }
}

Инжектор, настроенный с этими модулями, обеспечит инъекцию Map<Integer, I> это имеет экземпляр всего связанного; здесь это будет карта с тремя входами от 1 до полностью введенной A Например, от 3 до C экземпляр, и от 4 до D пример. Это на самом деле улучшение по сравнению с вашим примером коммутатора, который использовал new ключевое слово и, следовательно, не вводить какие-либо зависимости в A или же B,

Для лучшего варианта, который не создает так много потерянных экземпляров, введите Map<Integer, Provider<I>> что MapBinder также предоставляет автоматически. Используйте это так:

class YourConsumer {
  @Inject Map<Integer, Provider<I>> iMap;

  public void yourMethod(int iIndex) {
    // get an I implementor
    I i = iMap.get(iIndex).get();
    // ...
  }
}

Чтобы обеспечить реализацию "по умолчанию" (и непрозрачный интерфейс) так, как вы это сделали, вы захотите реализовать свою собственную короткую оболочку поверх MapBinder карта:

class IFactory {
  @Inject Map<Integer, Provider<I>> iMap;
  @Inject Provider<B> defaultI; // Bound automatically for every Guice key

  I getI(int i) {
    return iMap.containsKey(i) ? iMap.get(i).get() : defaultI.get();
  }
}

Проще, в заводском стиле

Если вышеупомянутое выглядит как излишнее, помните, что вы можете ввести Injector и создать местный Map от ключа к реализации. (Вы также можете использовать ImmutableMap как я сделал здесь).

class IFactory {
  @Inject Injector injector; // This is a bad idea, except for times like this
  @Inject Provider<B> defaultI;
  static final ImmutableMap<Integer, Class<? extends I>> map = ImmutableMap.of(
      1, A.class,
      3, C.class,
      4, D.class);

  I getI(int i) {
    return map.containsKey(i)
        ? injector.getInstance(map.get(i))
        : defaultI.get();
  }
}
Другие вопросы по тегам