Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guice - how to implement a factory that returns different implementations

Lets say I have a service called Guice service and here is its constructor

public GuiceService(IPayment payment) {
    this.payment = payment;
}

And my code used to create it using an Enum

IPayment payment = new PaymentFactory.create(PaymentType.Cash);
NaiveService naiveService = new NaiveService(payment);

And I had to have a factory implementation somewhere. Something like this

public IPayment create(PaymentType paymentType) {
    IPayment cardPayment = null;
    switch (paymentType) {
        case Cash:
            cardPayment = new CashPayment(100);
            break;
        case Card:
            cardPayment = new CardPayment(10, 100);
            break;
    }
    return cardPayment;

Now I want to use Guice and I guess I want to use FactoryModuleBuilder.

  1. What is the way to do it if I have more that one implentation of IPayment.
    (e.g. CardPayment, CashPayment)
    This works for one

    install(new FactoryModuleBuilder()
       .implement(IPayment.class, CashPayment.class)
       .build(IPaymentFactory.class));
    
  2. How do I implement the constructor ?
    will it still get IPayment? or will it get the factoryImpl created by Guice?

Thanks

like image 415
Bick Avatar asked Aug 10 '14 08:08

Bick


1 Answers

Your existing implementation is the best you can get.

Let's write out a general IPaymentFactory for clarity:

public interface IPaymentFactory {
  IPayment create(/* ... */);
}

So instances of IPaymentFactory define one method, that takes in some number of parameters and returns an instance of IPayment. You could write an implementation yourself, and evidently you have, but Guice's FactoryModuleBuilder provides interface implementations like this one automatically. You never need to define anything else about that class: Guice will wire up the constructor for you, and bind it to IPaymentFactory so you can inject IPaymentFactory instances, call create(...) with your parameters, and get IPayment instances.

It looks like what you're going for is a factory that takes an Enum:

public interface IPaymentFactory {
  IPayment create(PaymentType paymentType);
}

...but given that CashPayment takes one arbitrary parameter, and CardPayment takes two arbitrary parameters, and given that the selection between them requires a mapping to an arbitrary PaymentType enum, you haven't given Guice nearly enough information to construct the right object.

Guice FactoryModuleBuilder is designed more for combining constructor parameters with dependencies:

// Constructor:
@Inject public BitcoinPayment(
    @Assisted long value,         // varies by instance as a constructor parameter
    BitcoinService bitcoinService // passed-in dependency satisfied by Guice
    ) { /* ... */ }

// Factory interface:
public IBitcoinPaymentFactory {
  BitcoinPayment create(long value); // users don't need to know about dependencies!
}

// Factory binding...
install(new FactoryModuleBuilder().build(IBitcoinPaymentFactory.class));

// ...which lets Guice write the equivalent of:
public GeneratedBitcoinPaymentFactory implements IBitcoinPaymentFactory {
  @Inject Provider<BitcoinService> bitcoinServiceProvider;

  @Override public BitcoinPayment create(long value) {
    return new BitcoinPayment(value, bitcoinServiceProvider.get());
  }
}

On one hand, the factory is dumber than you think: It just combines parameters with dependencies to get one whole list. On the other, it's handy: you specify the dependency list once, and Guice does the rest.

In summary: FactoryModuleBuilder won't solve your problem, but it COULD help you create factories for CashPayment and CardPayment, which you could then inject into your manual PaymentFactory implementation (which will still need to exist in some form or another).

P.S. In your example, which might be a "toy problem" for demonstration, you may not need to use Guice. Guice is a great solution for service objects that require dependencies, but data objects (like a payment) or other objects that don't seem to need dependencies (like GuiceService or NaiveService) can be constructed directly using constructors. Once they start needing Guice-injected dependencies, it should be pretty easy to make them Guice-aware.

like image 188
Jeff Bowman Avatar answered Oct 20 '22 00:10

Jeff Bowman