Можно ли элегантно предоставить экземпляры с инжиниризованных фабрик Guice, которые не содержат аргументов или эквивалента?
Итак, следующий код не работает. Однако, если существует несколько потенциальных экземпляров View, то вводить большое количество провайдеров-провайдеров туда, где вы хотите их использовать, кажется неуклюжим. Возможно, это возможно в моем случае, однако я могу представить, что в других случаях это не очень хорошо... поэтому я решил задать вопрос, пока он был свежим в моей памяти. Одно из решений, которое я еще не пробовал, - это добавление в метод фиктивных аргументов @Assisted и вызов их как factory.getView1(null), хотя это тоже не очень хорошо.
Обратите внимание, я могу понять, почему для разработчиков guice это было бы угловым случаем, поскольку guice должен был знать, что не нужно вызывать конструктор (как это кажется), а вместо этого использовать провайдера (о котором он технически осведомлен). Тем не менее, лучше спросить, есть ли решение, чем предположить, что его нет.:-)
import com.google.inject.*;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import junit.framework.Assert;
import org.junit.Test;
public class GuiceTests {
static class View1 implements Presenter.View {
String name = null;
@Override
public String getName() { return null; }
public void setName(String name) { this.name = name ;}
}
static class View2 extends View1 {}
static class Presenter {
interface View {public String getName();}
@Inject
public Presenter(@Assisted View view //, and other injected services
){}
}
interface Factory{
Presenter getPresenter(Presenter.View view);
View1 getView1();
View2 getView2();
}
static class Module extends AbstractModule
{
@Provides View1 getView1()
{
View1 view1 = new View1(); //or possibly get this from an xml generator
view1.setName("buddy");
return view1;
}
@Override
protected void configure() {
install(new FactoryModuleBuilder().implement(Presenter.class, Presenter.class)
.build(Factory.class));
}
}
/**
* We're testing the ability here for assisted injected factories
* to be used to produce entities provided by the module with NO
* assisted arguments. This way they can conveniently be used in
* conjuction with other factory assisted methods as shown below.
* This fails!
*/
@Test
public void testAssisted()
{
Injector injector = Guice.createInjector(new Module());
Factory factory = injector.getInstance(Factory.class);
View1 view1 = factory.getView1();
Assert.assertEquals("buddy", view1.getName());
Presenter presenter = factory.getPresenter(view1);
}
}
Дополнительный контекст
Алан спросил ниже: "Можете ли вы привести пример того, как будет выглядеть реальный код, который использует это (не вызывая инжектор)? Я не понимаю, почему бы вам просто не внедрить фабрику и соответствующий вид вместе (или определить аннотированный обеспечивает метод для каждого вкуса ведущего, который сделал это)
Итак, у меня есть докладчик, который визуализируется в виде карты в своего рода плавающем макете. У этого докладчика есть определенная бизнес-логика, которая настраивается с помощью набора служб. В приложении есть кнопка "новый", которая дает вам представление о карте, которая проведет вас через новый процесс (конфигурация на карте). Как только карта построена, существует другое представление для представления карты... но она разделяет большую часть той же бизнес-логики... поэтому в идеале я хочу повторно использовать презентатор, который уже настроен с моделью... но теперь присоединить построенный вид. Сохраненные карты воссоздаются со встроенным видом. Пожалуйста, не читайте, если вы не следите за этим обсуждением.
Обратите внимание, что приведенный выше код устраняет проблемы, которые у меня возникают адекватно. Ниже все усложняется, так как дает более полный контекст.
//----------------
//on Add new entity
cardLayout.add(factory.getPresenterWithWizardView());
//-----------
//then later in the finish of the wizard
thePresenter.setView(factory.getConstructedView());
//I would prefer not to create a new presenter here, as the presenter also has layout
//state and logic that maintains and interacts with cardLayout to . Allowing for removing
//and adding a different presenter would trigger stuff affecting the state.
//--------------
//however elsewhere cards are loaded with
cardLayout.add(factory.getPresenterWithBuiltView(cardFromDb));
2 ответа
Если вы знаете, что существуют конкретные представления, для которых вам нужны докладчики, я бы просто добавил в ваш модуль что-то вроде этого:
@Provides @Named("PresenterForView1")
public Presenter forView1(Factory factory, View1 view1) {
return factory.getPresenter(view1);
}
(Только с правильной аннотацией, а не с именем, и повторяя для различных постоянных представлений.)
Когда вы позже захотите изменить представление докладчика, я просто добавлю это представление:
@Inject Constructor(@Named("PresenterForView1") Presenter presenter, View2 view2) {
...
presenter.setView(view2);
...
}
(Или возможно ввести Provider<View2>
если вы не хотите, чтобы он был построен заранее.)
Поэтому я предполагаю, что помимо непосредственного использования большого количества провайдеров (впрыскивая их там, где это необходимо), другим методом было бы создание класса SanerFactory, который имел бы методы, подобные factory.getPresenterWithView1(). Тогда это будет единственное место, где мне, возможно, придется вводить несколько провайдеров, изолируя использование реализаций View1 и View2 напрямую. Просто жаль, что будет больше котельной плиты для обслуживания.