Цепочка параметров ввода с помощью guice-assistedinject

Я использую библиотеку Guice Assisted Inject, чтобы создать для себя фабрику. У меня сейчас настроено так:

class MyObject {
  @Inject public MyObject(@Assisted FirstDep first, @Assisted SecondDep second, ThirdDep third) { /**/ }
}

class FirstDep { /* nothing to see here */ }
class SecondDep {
  @Inject public SecondDep(@Assisted FirstDep first) { /**/ }
}
class ThirdDep { /* nothing to see here either */ }

class MyModule extends AbstractModule {
  @Override public void configure() {
    bind(ThirdDep.class);
    install(new FactoryModuleBuilder().build(MyObjectFactory.class));
  }
}

interface MyObjectFactory {
  SecondDep createSecond(@Assisted FirstDep first);
  MyObject createMyObject(@Assisted FirstDep first, @Assisted SecondDep second);
}

Это вынуждает меня явно создавать SecondDep с помощью factory.createController(first, factory.createSecond(first)). Можно ли изменить мои привязки, чтобы я мог просто выполнить factory.createController(first), который автоматически использует привязку SecondDep и аргумент, который я передал?


person Jorn    schedule 03.03.2014    source источник
comment
Я уже пытался сделать MyFactory абстрактным классом с методом MyObject createMyObject(@Assisted FirstDep first), который обертывает два сгенерированных метода, но это не сработает. Guice требует, чтобы фабричный тип был интерфейсом.   -  person Jorn    schedule 03.03.2014


Ответы (1)


Рассмотрите API, который вы создаете здесь, особенно в отношении будущих расширений или реализаций. Очевидно, у вас есть несколько экземпляров FirstDep с состоянием, потому что SecondDep зависит от конкретного FirstDep, но будущий SecondDep может не зависеть от того же FirstDep (или вообще от любого FirstDep). Тот факт, что createMyObject(first) может быть сокращением для createMyObject(first, factory.createSecond(first)), зависит от вашего бизнес-кейса, и я не думаю, что в Guice есть сокращение, позволяющее сделать такое предположение.

Тем не менее, у вас есть два варианта. Во-первых, вы можете создать очень маленький помощник:

// Encapsulate the createMyObject(first) shorthand.
class MyObjectHelper {
  @Inject MyObjectFactory rawFactory;

  MyObject createMyObject(FirstDep first) {
    return rawFactory.createMyObject(first, rawFactory.createSecond(first));
  }
}

Или, во-вторых, вы можете использовать @AssistedInject< /a> чтобы Guice эффективно перегружал конструктор:

class MyObject {
  // Multiple @AssistedInject constructors, selected based on which parameters
  // are marked with @Assisted.

  @AssistedInject public MyObject(@Assisted FirstDep first,
       SecondFactory secondFactory, ThirdDep third) {
    this(first, secondFactory.createSecond(first), third);
  }

  @AssistedInject public MyObject(@Assisted FirstDep first,
      @Assisted SecondDep second, ThirdDep third) { /**/ }
}

interface SecondFactory {
  // Note that @Assisted annotations are not needed here. Every parameter in
  // these interfaces is for assisted injection, by definition.
  SecondDep createSecond(FirstDep first);
}

interface MyObjectFactory {
  MyObject createMyObject(FirstDep first);
  MyObject createMyObject(FirstDep first, SecondDep second);
}

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

class SecondDep {
  interface Factory {
    SecondDep create(FirstDep first);
  }

  @Inject public SecondDep(@Assisted FirstDep first) { /**/ }
}

... который затем позволяет вам найти и обновить Second.Factory точно рядом с классом, который он поддерживает.

person Jeff Bowman    schedule 03.03.2014