how to solve this - different instances of same interface

339 views
Skip to first unread message

cpea

unread,
Aug 21, 2009, 2:08:28 PM8/21/09
to google-guice
I've seen various flavors of this question from other people but still
don't feel like I understand how to solve it. Any input would be
greatly appreciated.

How can I use Guice to create the different instances of the
calculators? Or in this case, do I even NEED to use Guice to create
the calculator instances since this is a factory. Guice would just
inject the needed dependencies to the factory. I'm confused about
"Guice replacing all instances of 'new'" in our code. Does that hold
true everywhere or are there places where you would expect to 'new' a
class?

public class CalculatorFactory {

private final Map<UserGroup, ICalculator> m_calculators;

public CalculatorFactory(SomeObject dependency1, SomeObject
dependency2, SomeObject dependency3) {
m_calculators = new HashMap<UserGroup, ICalculator>();
m_calculators.put(UserGroup.A, new ACalculator(dependency1,
dependency2, dependency3);
m_calculators.put(UserGroup.B, new BCalculator(dependency1);
m_calculators.put(UserGroup.C, new CCalculator(dependency1);
}

// Depending on which group a user belongs to,
// a different type of calculator will be returned
public ICalculator loadCalculator(UserGroup userGroup) {
return m_calculators.get(userGroup);
}

}

Craig McClanahan

unread,
Aug 21, 2009, 4:52:22 PM8/21/09
to google...@googlegroups.com
On Fri, Aug 21, 2009 at 11:08 AM, cpea<cpri...@currenex.com> wrote:
>
> I've seen various flavors of this question from other people but still
> don't feel like I understand how to solve it. Any input would be
> greatly appreciated.
>
> How can I use Guice to create the different instances of the
> calculators?

When you create your bindings, don't tell Guice that this is a
singleton. Then, Guice will create a new instance for each user.

> Or in this case, do I even NEED to use Guice to create
> the calculator instances since this is a factory. Guice would just
> inject the needed dependencies to the factory. I'm confused about
> "Guice replacing all instances of 'new'" in our code. Does that hold
> true everywhere or are there places where you would expect to 'new' a
> class?

Think about things from the perspective of your calling user for a
moment, and you realize:

* There is no need for the calling user to know whether
the calculator is a singleton or not.

* There is no need for the calling user to know
how the calculator got created (factory, service
lookup, or some other mechanism).

In addition, from the calculator implementor's point of view, you
don't have to waste the effort to create a factory if you don't need
one. You can change your mind later (singleton to per request,
instance to provider) later as well, with zero impact on the calling
users. Plus, it's much easier to set up unit tests without having to
mock both a calculator implementation and a factory.

>
> public class CalculatorFactory {
>
>   private final Map<UserGroup, ICalculator> m_calculators;
>
>   public CalculatorFactory(SomeObject dependency1, SomeObject
> dependency2, SomeObject dependency3) {
>         m_calculators = new HashMap<UserGroup, ICalculator>();
>         m_calculators.put(UserGroup.A, new ACalculator(dependency1,
> dependency2, dependency3);
>         m_calculators.put(UserGroup.B, new BCalculator(dependency1);
>         m_calculators.put(UserGroup.C, new CCalculator(dependency1);
>   }
>
>   // Depending on which group a user belongs to,
>   // a different type of calculator will be returned
>   public ICalculator loadCalculator(UserGroup userGroup) {
>           return m_calculators.get(userGroup);
>   }
>
> }

One way you could make this either-or kind of choice would be to
provide a "binding annotation" that a caller can also use to tell
Guice what kind of calculator it needs. For example, if you have two
groups ("Mechanical" and "Electric") which determines which kind they
need, one caller might say:

@Inject @Mechanical Calculator calculator;

while another could say:

@Inject @Electric Calculator calculator;

(Sorry, couldn't resist :-).

The other way to deal with conditional creation would be to bind
Calculator to a Provider<Calculator> (a kind of factory) instead. The
provider would have some way to figure out what group the user is
(perhaps through getting a User object injected to it), Then, Guice
will call the provider's get() method when it needs an instance
(optionally injecting things into the provider class as needed).

Again, note that the *user* of a Calculator need not understand how
the right instance was picked -- it just gets to worry about actually
using it.

Craig McClanahan

cpea

unread,
Sep 1, 2009, 5:41:29 PM9/1/09
to google-guice
This is how I ended up solving the problem:

public CalculatorModule {
@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention
(RUNTIME)
public @interface TypeA {}

@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention
(RUNTIME)
public @interface TypeB {}

@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention
(RUNTIME)
public @interface TypeC {}

@Override
protected void configure() {
bind(ICalculator.class).annotatedWith(TypeA.class).to
(ACalculator.class);
bind(ICalculator.class).annotatedWith(TypeB.class).to
(BCalculator.class);
bind(ICalculator.class).annotatedWith(TypeC.class).to
(CCalculator.class);
}
}

@Singleton
public class CalculatorFactory {

private final Provider<ICalculator> a;
private final Provider<ICalculator> b;
private final Provider<ICalculator> c;

@Inject
public CalculatorFactory(@CalculatorModule.TypeA
Provider<ICalculator> AProvider,
@CalculatorModule.TypeB
Provider<ICalculator> BProvider,
@CalculatorModule.TypeC
Provider<ICalculator> CProvider) {
a = AProvider;
b = BProvider;
c = CProvider;
}

public ICalculator create(UserGroup group) {
if (group.getType.equals(Types.A) {
return a.get();
} else if (group.getType.equals(Types.B) {
return b.get();
} else if (group.getType.equals(Types.C) {
return c.get();
}
}

}

and in my code.........at the top level
Guice.createInjector(new CalculatorModule());

and my class that uses the calculator factory looks like this....
public class MyClass {
private final CalculatorFactory calcFactory;

@Inject
public MyClass(CalculatorFactory factory) {
calcFactory = calcFactory;
}

public doSomething(User user) {
....
ICalculator calc = calcFactory.create(user.getGroup());
calc.doStuff();
}
}

cpea

unread,
Sep 1, 2009, 5:55:41 PM9/1/09
to google-guice
I don't see how this could have been done without the
CalculatorFactory part. The User (and their group) is dynamic
information at runtime. The "caller" doesn't know what type of
calculator it needs. Am I missing something here?

Dan Godfrey

unread,
Sep 2, 2009, 4:58:57 AM9/2/09
to google-guice
If there's a one-to-one mapping between the Types and calculators and
the calculators can be singletons, you could have used a MapBinder.
However you still need the CalculatorFactory or a CalculatorRegistry
(as it's not actually creating anything anymore), it's a bit smaller
and a lot less boilerplate to maintain.

public class CalculatorModule extends AbstractModule {
protected void configure() {
MapBinder<Types, ICalculator> mapbinder
= MapBinder.newMapBinder(binder(), Types.class,
ICalculator.class);
mapbinder.addBinding(Types.A).to(ACalculator.class);
mapbinder.addBinding(Types.B).to(BCalculator.class);
mapbinder.addBinding(Types.C).to(CCalculator.class);
}
}

@Singleton
public class CalculatorFactory {
private final Map<Types, Calculator> calculators;

@Inject
public CalculatorFactory(Map<Types, Calculator> calculators) {
this.calculators = calculators;
}

public ICalculator create(UserGroup group) {
calculators.get(group.getType);

cpea

unread,
Sep 2, 2009, 11:44:24 AM9/2/09
to google-guice
That's great, thank-you! I'm going to try that... it would greatly
simplify the amount of code.

Dan Godfrey

unread,
Sep 3, 2009, 5:10:46 AM9/3/09
to google-guice
I forgot to add this link for further info:
http://code.google.com/p/google-guice/wiki/Multibindings

rdcyash

unread,
Sep 8, 2009, 10:34:16 AM9/8/09
to google-guice
I know you have mentioned but is there any possibility for each get
from the map I can have a new instance of the class instead of
singleton?

Thanks!

On Sep 3, 10:10 am, Dan Godfrey <daniel.godf...@gmail.com> wrote:
> I forgot to add this link for further info:http://code.google.com/p/google-guice/wiki/Multibindings
>
> On Sep 2, 4:44 pm, cpea <cprim...@currenex.com> wrote:
>
>
>
> > That's great, thank-you! I'm going to try that... it would greatly
> > simplify the amount of code.
>
> > On Sep 2, 1:58 am, Dan Godfrey <daniel.godf...@gmail.com> wrote:
>
> > > If there's a one-to-one mapping between the Types and calculators and
> > > the calculators can be singletons, you could have used aMapBinder.
> > > However you still need the CalculatorFactory or a CalculatorRegistry
> > > (as it's not actually creating anything anymore), it's a bit smaller
> > > and a lot less boilerplate to maintain.
>
> > > public class CalculatorModule extends AbstractModule {
> > >    protected void configure() {
> > >      MapBinder<Types, ICalculator>mapbinder
> > >          =MapBinder.newMapBinder(binder(), Types.class,
> > > ICalculator.class);
> > >      mapbinder.addBinding(Types.A).to(ACalculator.class);
> > >      mapbinder.addBinding(Types.B).to(BCalculator.class);
> > >      mapbinder.addBinding(Types.C).to(CCalculator.class);
> > >    }
> > >  }
>
> > > @Singleton
> > > public class CalculatorFactory {
> > >     private final Map<Types, Calculator> calculators;
>
> > >    @Inject
> > >    public CalculatorFactory(Map<Types, Calculator> calculators) {
> > >         this.calculators = calculators;
> > >     }
>
> > >     public ICalculator create(UserGroup group) {
> > >          calculators.get(group.getType);
> > >     }
>
> > > }- Hide quoted text -
>
> - Show quoted text -

Dan Godfrey

unread,
Sep 9, 2009, 5:27:20 AM9/9/09
to google-guice
Not that I know of. You'd need to either push a factory into the map
which creates the instances, although then you're losing the injection
into the calculators, or something like the following. (written
inline, so may well have some typos). Issue here, I think, is that if
you're missing a dependency binding, you won't find out about it till
you call CalculatorFactory#create()

public class CalculatorModule extends AbstractModule {
protected void configure() {
MapBinder<Types, Class<ICalculator>> mapbinder
= MapBinder.newMapBinder(binder(), Types.class,
ICalculator.class);
mapbinder.addBinding(Types.A).toInstance(ACalculator.class);
mapbinder.addBinding(Types.B).toInstance(BCalculator.class);
mapbinder.addBinding(Types.C).toInstance(CCalculator.class);
}
}

@Singleton
public class CalculatorFactory {
private final Injector injector;
private final Map<Types, Class<Calculator>> calculators;

@Inject
public CalculatorFactory(Injector injector, Map<Types,
Class<Calculator>> calculators) {
this.injector = injector;
this.calculators = calculators;
}

public ICalculator create(UserGroup group) {
injector.getInstance(calculators.get(group.getType));

rdcyash

unread,
Sep 10, 2009, 4:39:34 AM9/10/09
to google-guice
Thanks!

I used a MapFinder to bind different instance for the same interface
distinguished by key.

//rdcyash
> > > - Show quoted text -- Hide quoted text -
Reply all
Reply to author
Forward
0 new messages