Come utilizzare Guice per un'efficace progettazione dell'API?

1

Sto creando un'API di base in JavaSE, che include moduli come l'architettura MVP, Service & Livello del repository, Modello di eventi per attivare eventi tra relatore ecc.

Sto cercando di implementare tutte le migliori pratiche di progettazione del software. Recentemente, ho letto su Dependency Injection & i suoi vantaggi e ha deciso di utilizzare Guice come quadro DI.

Dopo aver passato due giorni ad imparare Guice, sono ancora confuso con alcuni problemi di progettazione.

  1. Se creo un injector utilizzando Guice.createInjector(modules) con i moduli principali della mia API,

    • Come rendere questa istanza di injector disponibile per le altre mie classi API? Si può iniettare un iniettore ed è una buona pratica?
    • Come rendere questa istanza di injector disponibile per l'utente API, in modo che possano utilizzare questo injector per creare istanze?
    • Dovrei renderlo disponibile anche all'utente API, perché potrebbe essere una cattiva pratica?
    • Quindi utilizzarlo per un approccio AOP come questo e iniezioni personalizzate
  2. La creazione di iniettori è un'operazione pesante, se così fosse ci dovrebbero essere solo pochi iniettori per applicazione?

  3. Gli iniettori dovrebbero essere creati nella fase di avvio dell'applicazione o potrebbero essere creati in mezzo senza influire sul rendimento dell'applicazione?

Modifica

Per comprendere l'utilizzo di Provider invece di iniettare injector, ecco un breve esempio di MVP:

interface ProductModel {

  // Some method declarations

}

interface ProductView {

  // Some method declarations

}

class ProductPresenter {

  // Using Guice for constructor injection
  @Inject
  ProductPresenter(ProductModel model, ProductView view) {
    this.model = model;
    this.view = view;
  }

}

class ProductViewImpl implements ProductView {

  // Method Implementations

}

class ProductModelImpl implements ProductModel {

  // Method Implementations

}

Ora, come inteso dalle risposte in basso per usare Provider<X> invece di iniettare injector, ecco la classe Provider .

class ProductPresenterProvider implements Provider<ProductPresenter> {

  ProductPresenter get() {
    // But, How to provide the ProductPresenter without model and view instances!
  }

}
    
posta Akshat 27.06.2015 - 19:47
fonte

3 risposte

1

È una cattiva pratica iniettare un iniettore, o altrimenti passarlo in un modo che lo rende accessibile al codice al di fuori dell'avvio. Questo confonde cosa sta succedendo; fai questo e quando qualcosa va storto, devi eseguire il debug del tuo codice tramite il debugging dello stato di iniezione delle dipendenze.

Il modo giusto e statico di fare le cose è iniettare un oggetto fabbrica scritto a mano o un Provider . La differenza è che il secondo imposterà l'oggetto creato come un oggetto DI, cioè l'elaborazione delle annotazioni @Inject.

Internamente, l'implementazione di un provider utilizza presumibilmente l'iniettore, ma poiché lo fa solo per una singola cosa è più facile ragionare e fare il debug. E se disegna il grafico delle dipendenze per il tuo sistema, poiché Provider è Provider<X> , dovresti ottenere il grafico effettivo delle dipendenze logiche con la tua classe a seconda di X . Non è un casino con un nodo 'magic happens' in un singolo iniettore usato per tutto.

Si noti che quando si esegue questa operazione, non è necessario fornire un'associazione o un'implementazione per il Provider; ha incorporato predefinito .

    
risposta data 28.06.2015 - 12:08
fonte
0

Ok, sono molte le domande che hai e immagino che alcuni dei punti possano essere affrontati in più di un modo.

Creare l'iniettore non è economico. Guice farà alcune analisi per rilevare errori e creare tutti i singleton. Quindi dovresti creare l'iniettore all'avvio.

Di solito ho un iniettore singolo per la mia applicazione.

Per quanto riguarda la tua domanda 1:

Sì, l'iniettore può essere iniettato. Ma la vedo come una cattiva pratica. Il motivo è che le chiamate a injector.getInstance() e injector.injectMembers() rendono difficile ragionare sulle dipendenze di una classe.

  • se è necessario creare più di un'istanza o è necessario controllare l'ora in cui viene creata un'istanza iniettare un Provider invece di X.
  • se l'oggetto che si desidera creare ha bisogno sia di parametri di runtime che di dipendenze che utilizzano l'iniezione assistita.
  • per molti tipi di oggetti (come DTO, POJO e simili) è assolutamente ok per crearli chiamando nuove o una fabbrica convenzionale.

In questo senso l'injector è implicitamente disponibile per qualsiasi istanza creata da Guice che include l'utente API.

E infine AOP. Gli aspetti sono una funzionalità potente ma possono anche nascondere alcune delle logiche di business che suggerisco di usarle solo per compiti molto ripetitivi che tutti gli sviluppatori conoscono bene.

Prendi anche in considerazione che gli aspetti funzionano solo su:

  • istanze create da Guice
  • metodi non privati, non statici, non privati

Infine, Guice non ti informerà se aggiungi un'annotazione a uno dei due precedenti. Il tuo aspetto verrà ignorato in silenzio

    
risposta data 28.06.2015 - 08:13
fonte
0

Darò una seconda risposta, ma avresti dovuto aprire una seconda domanda ...

Prima di tutto: raramente si implementa un fornitore. Guice li creerà automaticamente per te. Per spiegare questo: Quando lego una classe Foo come segue:

bind(Foo.class).to(FooImpl.class)

Puoi inserire nella tua classe Foo o Provider<Foo> . Guice creerà il fornitore per te.

Il motivo per cui non dovresti implementare il provider tu stesso, è che AOP non sta lavorando su istanze che sono state create con una chiamata a new . Quando si utilizza un debugger per esaminare l'istanza di runtime di un oggetto iniettato che utilizza gli intercettori, si noterà che Guice ha creato una sottoclasse al volo che consente di intercettare le chiamate al metodo.

Per il tuo esempio:

Il problema principale è che ProductModel non deve essere iniettato. È un oggetto dominio e contiene dati. Questo tipo di oggetti non dovrebbero essere creati / gestiti da DI. Invece il codice è responsabile di creare / recuperare e memorizzare il modello dal suo stato permanente. Il presentatore ha ora due diverse dipendenze. Uno è il modello che vive al di fuori di DI e uno è la vista che può essere gestita da DI (proprio come una nota a margine: se hai bisogno della stessa istanza della vista per essere iniettata su più di un oggetto devi metterla in un ambito (personalizzato). Ma le risposte agli ambiti meritano una nuova domanda).

Per risolvere il dilemma sopra riportato Guice offre iniezione assistita . Questo ti permette di scrivere un codice simile al seguente:

public class ProductPresenter {

    public interface Factory {
        createPresenter(ProductModel model);
    }

    @Inject
    ProductPresenter(@Assisted ProductModel model, ProductView view) {
        this.model = model;
        this.view = view;
    }
}

quindi esegui il bind di fabbrica come segue:

install(new FactoryModuleBuilder().build(ProductPresenter.Factory.class));

Guice creerà immediatamente un'implementazione dell'interfaccia ProductPresenter.Factory . L'istanza creata otterrà gli argomenti passati al metodo factory inserito nel costruttore. Anche AOP sta lavorando sull'istanza creata. In questo modo puoi mischiare DI con oggetti non DI.

    
risposta data 28.06.2015 - 23:02
fonte

Leggi altre domande sui tag