Classes are glued together by interfaces, and implemented classes are injected in run-time.
---Compile time: interface calls interface, run time: implementation calls implementation. In between taken care by Guice.
- we don't need implemented classes to get compiled.
- we could change to different implemented classes on the fly.
- No need to rewrite test client.
Guice
- What to inject: map interface to real implementation, this is the place we'll make the change. Keep mind, bind interface to implementation is the best practice, but you could also bind any class to its sub class, as long as they are the same type or implements the same interface.
public class BillingModule extends AbstractModule { @Override protected void configure() { bind(TransactionLog.class).to(DatabaseTransactionLog.class); bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class); bind(BillingService.class).to(RealBillingService.class); } }
- Where to inject: mark the location to tell Guice where to replace interface with implementation
public class RealBillingService implements BillingService { private final CreditCardProcessor processor; private final TransactionLog transactionLog; @Inject public RealBillingService(CreditCardProcessor processor, TransactionLog transactionLog) { this.processor = processor; this.transactionLog = transactionLog; } .... }
- How to inject: Usage of Guice, create injector based on module, injector is like a map.
public static void main(String[] args) { Injector injector = Guice.createInjector(new BillingModule()); BillingService billingService = injector.getInstance(BillingService.class); ... }