Passare un contesto con un'iniezione di dipendenza

5

Nel progetto a cui sto lavorando, sto usando Guice e sto cercando di fare il più possibile con Dependency Injection. Tuttavia, c'è un piccolo intoppo; molti dei miei oggetti si basano su un oggetto Context . Questo è un oggetto immutabile ma ha alcune informazioni che la maggior parte degli altri oggetti devono funzionare correttamente. Non cambia mai per le classi che lavorano tutte all'interno dello stesso Context , ma la mia applicazione deve essere in grado di supportare diversi Context s allo stesso tempo, nello stesso ambito. Posso pensare a tre modi per risolvere il mio problema:

  1. Attualmente mi sono trovato a scrivere un sacco di codice con AssistedInject, che ha quasi sempre metodi come Factory.create(Context c) . Tutto il mio codice che dipende da Context viene creato in questo modo, anche se sono altrimenti semplici.

  2. Sto iniziando a considerare l'utilizzo di Provider s e la modifica di ogni riga di factory.create(context) in obj = provider.get(); obj.setContext(c); ma non voglio fare affidamento sul mio codice per effettuare entrambe le chiamate, mi piace che il il compilatore si assicura che ogni oggetto abbia il suo context e che context non possa cambiare (non lo farà mai).

  3. La mia ricerca mi ha indirizzato a questa discussione: link L'idea di associare ogni Context a un childInjector che ha una riga bind(Context.class).toInstance(ContextFactory.makeInstance(args)) , ma non sono sicuro di come garantire che tutto stia usando l'iniettore figlio corretto. Sembra che potrebbe essere il migliore, ma a parte quel thread (che non è il mio scenario) non sono sicuro di come farlo, o se è giusto per la mia situazione.

Ecco un sscce di ciò che potrebbe essere fatto dall'opzione 3:

public class GuiceChildTest {
    public static void main(String... args) {
        Injector inj = Guice.createInjector(new ParentModule());
        Injector fooChildOne = inj.createChildInjector(new FooModule(new Foo(10)));
        Injector fooChildTwo = inj.createChildInjector(new FooModule(new Foo(20)));

        Bar bar = fooChildOne.getInstance(Bar.class);
        System.out.println(bar.baz());

        bar = fooChildTwo.getInstance(Bar.class);
        System.out.println(bar.baz());
    }

    private static class ParentModule extends AbstractModule {
        @Override
        protected void configure() {
        }
    }

    private static class Bar {
        private MyInterface intf;
        @Inject
        public Bar(MyInterface in) {
            this.intf = in;
        }

        public int baz() {
            return intf.doSomething();
        }
    }

    private static class FooModule extends AbstractModule {
        private Foo f;
        public FooModule(Foo f) {
            this.f = f;
        }

        @Override
        protected void configure() {
            bind(MyInterface.class).toInstance(f);
        }   
    }   

    private static class Foo implements MyInterface {
        private int i;
        public Foo(int i) {
            this.i = i;
        }
        @Override
        public int doSomething() {
            return i;
        }
    }

    private static interface MyInterface {
        int doSomething();
    }
}
    
posta durron597 17.06.2013 - 23:23
fonte

1 risposta

3

Esaminiamo i fondamenti di Iniezione delle dipendenze con una breve rassegna di principio dell'iniezione di dipendenza

When following this principle, the conventional dependency relationships established from high-level, policy-setting modules to low-level, dependency modules are inverted (i.e. reversed), thus rendering high-level modules independent of the low-level module implementation details. The principle states

  • High-level modules should not depend on low-level modules. Both should depend on abstractions.
  • Abstractions should not depend on details. Details should depend on abstractions.

Sembra che i primi due approcci menzionati potrebbero non seguire completamente i principi DI, perché gli oggetti di ordine superiore sembrano aver bisogno di sapere quale tipo di Context dovrebbero avere.

E prima che io continui, dovrei ammettere un pregiudizio che potenzialmente offenderà i sostenitori di DI fanatici:

DI can get over-emphasized and zealous adherence to the principles can get in the way of writing maintainable code that's appropriate for the environment that it operates within. And that's not necessarily a dig against DI, but rather against any technique that ends up being blindly applied. I'm not saying that you are blindly applying DI, but there are many who fall into that category.

Spoiler utilizzato per proteggere coloro che sono eccessivamente sensibili a tali affermazioni

Quindi prima di chiamare i metodi 1 & 2 "male", dobbiamo chiedere se soddisfano le tue attuali esigenze. Questi approcci implicano una certa misura del codice boilerplate che viene copiato in giro. Questo può andar bene se non ti trovi a dover cambiare quel modello su base frequente.

Allo stesso modo, se gli oggetti possono usare un solo tipo di contesto, allora non è necessario a preoccuparsi di DI in questo caso, poiché non c'è necessariamente qualcos'altro da iniettare in l'oggetto. "Ma per quanto riguarda test ?!" è la solita replica a quella affermazione pragmatica, alla quale si risponde con una risposta ugualmente pragmatica di "Possono quegli oggetti usare / hanno bisogno di un Contesto diverso per i test?" Se sì, quindi utilizzare DI. Se no, allora ... continua.

La terza opzione che proponi ti allontana ulteriormente dal codice boilerplate e ti avvicina a un approccio DI. La tua esitazione sembra essere:

but I'm not sure how to ensure that everything is using the correct child injector

Penso che la soluzione a questa sfida sia quella di avvolgere la creazione del contesto in un Factory e assolve gli oggetti di ordine superiore dalla conoscenza di ciò di cui hanno bisogno per chiamare.

Quindi, se ho capito correttamente il tuo codice, invece di questo:

Bar bar = fooChildOne.getInstance(Bar.class);

avresti qualcosa di simile:

Bar bar = new Bar(ContextFactory.getInstance(Bar.class));

Nel suggerimento, stiamo passando il contesto restituito dal ContextFactory al costruttore di Bar . Potrebbe essere 1 banale per semplificare quell'esempio con:

public class Bar(){
    Bar(){
        return Bar(ContextFactory.getInstance(Bar.class));
    }
}

o

public class Bar(){
    Bar(){
        this.Context = ContextFactory.getInstance(Bar.class);
    }
}

1 Il mio Java è arrugginito, quindi non so se è possibile concatenare costruttori come quel primo esempio.

E potresti potenzialmente stringere un po 'le cose usando la riflessione all'interno di ContextFactory per determinare il Contesto corretto per Bar o facoltativamente passare altri parametri a ContextFactory se Bar può gestire più tipi di Contesto nel normale funzionamento.

TL; DR

Utilizza la terza opzione che hai proposto e avvolgi la creazione del contesto in un Factory .

    
risposta data 17.12.2014 - 19:37
fonte

Leggi altre domande sui tag