Итак, я в настоящее время перепроектировал приложение для Android для использования Dagger. Мое приложение большое и сложное, и я недавно натолкнулся на следующий сценарий:
Для объекта A требуется специальный экземпляр DebugLogger, который является идеальным кандидатом для инъекций. Вместо того, чтобы проходить вокруг регистратора, я могу просто ввести его через конструктор. Это выглядит примерно так:
class A
{
private DebugLogger logger;
@Inject
public A(DebugLogger logger)
{
this.logger = logger;
}
// Additional methods of A follow, etc.
}
До сих пор это имеет смысл. Тем не менее, A необходимо построить другим классом B. Множество экземпляров A должно быть построено, поэтому, следуя кинжалу, я просто вставляю a Provider<A>
в B:
class B
{
private Provider<A> aFactory;
@Inject
public B(Provider<A> aFactory)
{
this.aFactory = aFactory;
}
}
Хорошо, хорошо. Но подождите, неожиданно A нуждается в дополнительных входах, таких как целое число, называемое "сумма", которое имеет жизненно важное значение для его построения. Теперь мой конструктор для A должен выглядеть так:
@Inject
public A(DebugLogger logger, int amount)
{
...
}
Внезапно этот новый параметр мешает инъекции. Более того, даже если это действительно сработало, мне не удастся передать "сумму" при получении нового экземпляра от провайдера, если я не ошибаюсь. Там я могу сделать несколько вещей, и мой вопрос в том, какой из них лучше?
Я мог бы реорганизовать A, добавив метод setAmount()
, который, как ожидается, будет вызываться после конструктора. Это уродливо, однако, потому что это заставляет меня отложить построение А до тех пор, пока не будет заполнено "количество". Если бы у меня было два таких параметра: "сумма" и "частота", тогда у меня было бы два сеттера, что означало бы либо сложную проверку, чтобы гарантировать, что построение резюме возобновляется после вызова обоих сеттеров, или мне нужно будет добавить еще третий метод в микс, например:
(Somewhere in B):
A inst = aFactory.get();
inst.setAmount(5);
inst.setFrequency(7);
inst.doConstructionThatRequiresAmountAndFrequency();
Другой альтернативой является то, что я не использую инъекцию на основе конструктора и не использую инъекцию на основе полей. Но теперь я должен сделать свои поля общедоступными. Это не очень хорошо со мной, потому что теперь я обязан раскрыть внутренние данные моих классов другим классам.
До сих пор единственным элегантным решением, которое я могу придумать, является использование полевой инъекции для поставщиков, например:
class A
{
@Inject
public Provider<DebugLogger> loggerProvider;
private DebugLogger logger;
public A(int amount, int frequency)
{
logger = loggerProvider.get();
// Do fancy things with amount and frequency here
...
}
}
Я все еще не уверен в сроках, так как я не уверен, что Кинжал будет вводить провайдера перед вызовом моего конструктора.
Есть ли лучший способ? Я просто что-то пропустил о том, как работает Кинжал?