Quindi, al momento sto riprogettando una mia app Android per utilizzare Dagger. La mia app è ampia e complicata e di recente mi sono imbattuto nel seguente scenario:Utilizzo di Dagger per l'iniezione delle dipendenze sui costruttori
L'oggetto A richiede un'istanza speciale DebugLogger che è un candidato ideale per l'iniezione. Invece di passare attorno al registratore, posso semplicemente iniettarlo attraverso il costruttore di A. Assomiglia a questo:
class A
{
private DebugLogger logger;
@Inject
public A(DebugLogger logger)
{
this.logger = logger;
}
// Additional methods of A follow, etc.
}
Finora questo ha senso. Tuttavia, A ha bisogno di essere costruita da un'altra classe B. più istanze di un devono essere costruite in modo seguente modo di fare le cose di Dagger, io semplice iniettare un Provider<A>
in B:
class B
{
private Provider<A> aFactory;
@Inject
public B(Provider<A> aFactory)
{
this.aFactory = aFactory;
}
}
Ok, bene finora. Ma aspetta, improvvisamente A ha bisogno di ulteriori input, come un intero chiamato "quantità" che è vitale per la sua costruzione. Ora, il mio di costruzione per una ha bisogno di guardare in questo modo:
@Inject
public A(DebugLogger logger, int amount)
{
...
}
Improvvisamente questo nuovo parametro interferisce con l'iniezione. Inoltre, anche se questo ha funzionato, non ci sarebbe stato modo per me di passare "importo" quando si recupera una nuova istanza dal provider, a meno che non mi sbagli. Ci sono molte cose che potrei fare qui, e la mia domanda è qual è la migliore?
Ho potuto refactoring A aggiungendo un metodo setAmount()
che si prevede di essere chiamato dopo il costruttore. Questo è brutto, comunque, perché mi costringe a ritardare la costruzione di A fino a quando "importo" è stato compilato. Se avessi due parametri quali "importo" e "frequenza", allora avrei due setter, il che significherebbe sia complicato il controllo per garantire che la costruzione di a riprende dopo due setter sono chiamati, o avrei dovuto aggiungere ancora un terzo metodo nella miscela, in questo modo:
(Somewhere in B):
A inst = aFactory.get();
inst.setAmount(5);
inst.setFrequency(7);
inst.doConstructionThatRequiresAmountAndFrequency();
l'altra alternativa è che io non uso costruttore iniezione basata su base e andare con l'iniezione basata sul campo. Ma ora, devo rendere pubblici i miei campi. Questo non mi sta bene, perché ora sono obbligato a rivelare i dati interni delle mie lezioni ad altre classi.
Finora, l'unica soluzione un po 'elegante mi viene in mente è quello di utilizzare l'iniezione di campo-base per i fornitori, in questo modo:
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
...
}
}
Ancora oggi, sono incerto sui tempi, da quando ho' Non sono sicuro che Dagger inietterà il provider prima che venga chiamato il costruttore.
C'è un modo migliore? Mi sto perdendo qualcosa su come funziona Dagger?
Grazie per la rapida risposta. Il modello di fabbrica che hai descritto sembra l'approccio migliore. Mi sono anche reso conto che se Dagger avesse supportato l'iniezione in campo privato, avrebbe facilmente permesso ciò che sto cercando di realizzare. Mi chiedo perché non fosse parte del design originale di Dagger? Presumo che Dagger inietti utilizzando la riflessione. Se è così, i campi privati non dovrebbero porre problemi, giusto? – Alex
Il pugnale ricade nel riflesso ma non è il suo principale mezzo di iniezione. Genera codice che imposta direttamente i campi o chiama i tuoi costruttori. Come tale funziona come qualsiasi altro pezzo di codice nella struttura dei sorgenti e non può accedere ai membri 'private'. –
E alcuni responsabili della sicurezza interromperanno la riflessione che si basa sulla modifica dell'accessibilità nelle classi. –