Может ли 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();
}
}