Инъекция Guice с помощью шаблона Builder для клиентской библиотеки

1

Я новичок в Google Guice, и я пытаюсь оборачивать голову тем, как использовать его для моего конкретного сценария. Я создаю клиентский сервис, который довольно сложный и что (я считаю) действительно требует правильного создания шаблона Builder. Поскольку этот клиент в конечном итоге будет упакован в свою собственную JAR-библиотеку, я бы хотел, чтобы Guice обрабатывал DI под капотом. Ниже приведена очень упрощенная версия моего кода:

public interface MyClient {
    public FizzResource getFizzResource();
    public BuzzResource getBuzzResource();
    public FooResource getFooResource();
}

public class MyClientImpl implements MyClient {
    // See below
}

public class GetFizzCommand {
    // omitted for brevity
}

public class GetBuzzCommand {
    // omitted for brevity
}

public class GetFooCommand {
    // omitted for brevity
}

public interface FizzResource {
    Fizz getFizz(Long fizzId);
}

public class FizzResourceImpl implements FizzResource {
    private GetFizzCommand getFizzCommand;

    @Override
    Fizz getFizz(Long fizzId) {
        return getFizzCommand.doGetFizz(fizzId);
    }
}

public interface BuzzResource {
    Buzz getBuzz(Long buzzId);
}

public class BuzzResourceImpl implements BuzzResource {
    private GetBuzzCommand getBuzzCommand;

    @Override
    Buzz getBuzz(Long buzzId) {
        return getBuzzCommand.doGetBuzz(buzzId);
    }
}

public interface FooResource {
    Foo getFoo(Long fooId);
}

public class FooResourceImpl implements FooResource {
    private GetFooCommand getFooCommand;

    @Override
    Foo getFoo(Long fooId) {
        return getFooCommand.doGetFoo(fooId);
    }
}

Таким образом, вы можете видеть, что график иерархии/депо выглядит следующим образом:

  • MyClient следует вводить *ResourceImpl s
  • Каждому *ResourceImpl следует вводить экземпляр *Command

Предполагаемый прецедент - сделать создание MyClient impl максимально простым:

MyClient myClient = MyClientImpl.Builder("myservice.example.org", 8080L, getWidget())
    .withAuth("user", "password")
    .withHttps()
    .withFailureStrategy(someFailureStrategy)
    // ...etc.
    .build();

Итак, вот моя лучшая попытка в MyClientImpl, его внутреннем застройщике и модуле Guice:

public class BaseClient {
    private String uri;
    private long port;
    private Widget widget;

    // ctor, getters and setters
}

public class MyClientImpl extends BaseClient implements MyClient {
    @Inject
    private FizzResource fizzResource;

    @Inject
    private BuzzResource buzzResource;

    @Inject
    private FooResource fooResource

    public MyClientImpl(String uri, long port, Widget widget) {
        super(uri, port, widget);
    }

    public static class Builder {
        private String uri;
        private long port;
        private Widget widget;

        Builder(String uri, long port, Widget widget) {
            super();

            setUri(uri);
            setPort(port);
            setWidget(widget);
        }

        // Lots of methods on the builder setting lots of MyClient-specific properties
        // that I have omitted here for brevity.

        MyClient build() {
            Injector injector = Guice.createInjector(new MyClientModule(this));
            return injector.getInstance(MyClient.class);
        }
    }
}

public class MyClientModule extends AbstractModule {
    private MyClientImpl.Builder builder;

    public MyClientModule(MyClientImpl.Builder builder) {
        super();

        setBuilder(builder);
    }

    @Override
    protected void configure() {
        MyClientImpl myClientImpl = new MyClientImpl(builder.getUri(), builder.getPort(), builder.getWidget());

        bind(MyClient.class).toInstance(myClientImpl);
    }
}

Но для моей жизни я не вижу, как и где:

  • Привяжите *Command к *ResourceImpl s; а также
  • Привязать *ResourceImpl к экземпляру MyClientImpl

Есть идеи?

  • 1
    Я думаю, что вы можете быть смущены целью связывания. Привязка помогает Guice определить, как предоставить вам экземпляр типа, например, какую реализацию интерфейса создать. Нет необходимости связывать Command с Resource , и при этом это не имеет смысла, поскольку они не являются частью одной иерархии типов. Кроме того, у вас нет Command @Injected поэтому ее не нужно привязывать. То же самое касается вашей другой точки пули.
  • 1
    Я думаю, что вы можете быть смущены целью связывания. Привязка помогает Guice определить, как предоставить вам экземпляр типа, например, какую реализацию интерфейса создать. Нет необходимости связывать Command с Resource , и при этом это не имеет смысла, поскольку они не являются частью одной иерархии типов. Кроме того, у вас нет Command @Injected поэтому ее не нужно привязывать. То же самое касается вашей другой точки пули.
Показать ещё 1 комментарий
Теги:
dependency-injection
guice
builder-pattern

1 ответ

3
Лучший ответ

Это не совсем ясно, что вы пытаетесь выполнить с помощью ваших инъекций. Возможно, вы путаете этот вопрос, сделав его о строителях и других аспектах вашего дизайна. Я предлагаю вам разбить код на SSCCE, который функционирует, но не включает в себя инъекцию, а затем увеличит ваш дизайн, введя, например, одно поле. Прямо сейчас все ваши показания кода - это обрезанный скелет сложного дизайна с инъекциями, который не имеет смысла, и нет способа запустить код и посмотреть, что произойдет. Guice работает во время выполнения, поэтому важно, чтобы мы видели ваш main метод, чтобы увидеть, что на самом деле делает ваш код приложения.

Ниже следует очень простой пример того, что я описал выше.

Интерфейс с двумя разными типами:

public interface Service {
  void useService();
}

public class FooService implements Service {
  public void useService() {
    System.out.println("foo service used");
  }
}

public class BarService implements Service {
  public void useService() {
    System.out.println("bar service used");
  }
}

Пользователь ванили (не-Guice) этого интерфейса:

public class ServiceUser {
  Service service = new FooService(); // change impl to be used here

  public static void main(String[] args) {
    new ServiceUser().service.useService();
  }
}

Указанный пользователь этого интерфейса:

public class ServiceUserGuicified {
  @Inject
  Service service;

  public static void main(String[] args) {
    Injector injector = Guice.createInjector(new FooServiceModule());
    ServiceUserGuicified user = injector.getInstance(ServiceUserGuicified.class);
    user.service.useService();
  }
}

public class FooServiceModule extends AbstractModule {
  @Override
  protected void configure() {
    bind(Service.class).to(FooService.class);
  }
}

В версии Guicified определение impl кодируется в модуле Guice, а не во время выполнения кода. Единственное, что включает код выполнения, это использование модуля при создании инжектора. Вы можете создать новый модуль для обслуживания бара и использовать его для создания инжектора или изменить привязку в своем основном сервисном модуле.

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню