Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guice runtime dependency parameters reinjection

Tags:

java

guice

A question about Guice. I'm still learning it, but I can understand the fundamentals.

This question was already asked a couple of times on the net, but never with a concrete answer(none that I could find).

Say I have a situation like on the picture(a similar example was somewere on the net).

enter image description here

public class Dog {}

public class Walk implements Walkable {
    private final Dog dog;
    private final boolean leash;

    @Inject
    public Walk(Dog dog, @Assisted boolean leash) {
        this.dog = dog;
        this.leash = leash;
    }

    public void go() {
    }
}

public interface Walkable {
    void go();
}

public interface WalkFactory {
    Walk create(boolean leash);
}

public class AssistedMain {
    public static void main(String[] args) {
        Injector i = Guice.createInjector(new AbstractModule() {
            protected void configure() {

                install(new FactoryModuleBuilder().
                        implement(Walkable.class, Walk.class).
                        build(WalkFactory.class));
            }
        });

        Walk walk = i.getInstance(WalkFactory.class).create(true);
    }
}

That's all great. But the question is - can I, somehow, reinject that object instance to the "container"(injector) to be used on the classes that rely on this dependency.

So, lets add a interface Person, class PersonImpl.

enter image description here

The new classes source are:

public interface Person {
    void walkDog();
}

public class PersonImpl implements Person {
    private Walkable walkable;

    @Inject
    public PersonImpl(Walkable walkable) {
        this.walkable = walkable;
    }

    public void setWalkable(Walkable walkable) {
        this.walkable = walkable;
    }

    public void walkDog() {
        walkable.go();
    }
}

So, the question is - am I, somehow able to actually inject this particular instance into the added object. This is a simple example, but we can presume there are 10 levels of classes below this one.

The solution I found is not very flexible. Something like:

Injector i = Guice.createInjector(new SimpleModule(false, dog));

And then bind to concrete instance. That's not very dynamic. Basically, every time I need a different runtime/dynamic parameter I have to recreate the injector.

The Provider<T> is nice, the FactoryModuleBuilder helps, but how can I inject the objects back?

Are there more dynamic solutions to this problem?

Thanks.

like image 323
pfh Avatar asked Oct 03 '11 22:10

pfh


1 Answers

MPierce - agreed. Ill try to explain the way i visualized the problem(you can correct me if im wrong).

Being originaly derived from a "service locator" pattern, the idea that it can manage more than services is optimistic to say the least.

We could split the application into Service and Data classes, or you could say that we have application and infrastructure code - "Dependency Injection", a great book.

So, basicly, dependecy injection, and dependency injection frameworks in general are great. For solving infrastructure, or "service" code.

Any dynamic(runtime) parameters being injected into the Container/Injector are basicly forcing you to end the object graph.

For example, we have the folowing design:

enter image description here

EmailMessage is a runtime parameter. It can be "injected" into email service outside the Container/Injector, but it ends the object graph. If we want to request EmailDispatcher, after we injected the EmailMessage into EmailService(which is, I repeat, done outside injector), we could no longer fetch EmailDispatcher from the injector.

Then, you could redesign your model so it "fits" into the Container/Injector concept of dynamic parameters.

enter image description here

But then again, you forced the design, and suddenly, EmailDispatcher has too many responsibilites. It could be used in such a context, where you dont have many infrastructure classes.

enter image description here

And when you have a design like you have in the third example picture, you cannot use the Injector/Container to fetch you a NextService3 instance(nor any below the level of EmailDispatcher).

The problem being - if you have any dynamic(runtime) parameters, you can only use dependency injection for classes above the class that requires a dynamic parameter, you can forget the classes below.

Phew.

Correct?

like image 123
pfh Avatar answered Nov 14 '22 20:11

pfh