Итак, я сейчас перепроектирую приложение для Android для использования Dagger. Мое приложение большое и сложное, и я недавно натолкнулся на следующий сценарий:Использование кинжала для инъекций зависимостей для конструкторов
Для объекта A требуется специальный экземпляр DebugLogger, который является идеальным кандидатом для инъекций. Вместо того, чтобы проходить вокруг регистратора, я могу просто ввести его через конструктор A. Это выглядит примерно так:
class A
{
private DebugLogger logger;
@Inject
public A(DebugLogger logger)
{
this.logger = logger;
}
// Additional methods of A follow, etc.
}
До сих пор это имеет смысл. Тем не менее, должна быть построена по другому классу B. Несколько экземпляров должны быть построены, так следующим образом кинжала делать вещи, я просто впрыснуть 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
...
}
}
Даже до сих пор, я не уверен по поводу времени, так как я m не уверен, что кинжал будет вводить поставщика перед вызовом моего конструктора.
Есть ли лучший способ? Я просто что-то пропустил о том, как работает Кинжал?
Благодарим за быстрый ответ. Заводская модель, которую вы описали, кажется наилучшим подходом. Я также понял, что если бы Кинжал поддерживал частную полевую инъекцию, это легко разрешило бы то, что я пытаюсь выполнить. Интересно, почему это не было частью оригинального дизайна Даггера? Я предполагаю, что Кинжал вводит рефлексию. Если это так, частные поля не должны вызывать проблем, верно? – Alex
Кинжал возвращается к отражению, но это не является его основным средством инъекции. Он генерирует код, который непосредственно устанавливает поля или вызывает ваши конструкторы. По сути, он работает как любой другой фрагмент кода в исходном дереве и не может получить доступ к «частным» членам. –
И некоторые менеджеры по безопасности нарушат размышления, которые зависят от меняющейся доступности в ваших классах. –