Google Guice and guice
Google Guice, an open-source software released by google, is a lightweight and next-generation dependency injection container designed for Java 5 and later versions. Its functions are similar to Spring.
Next, let's take a look at Guice. Before that, let's take a look at an official example: in an application, it is very boring to assemble everything, this involves connecting data, services, and presentation layer. This is an example of the billing code of a pizza ordering website for comparison in these aspects.
public interface BillingService { /** * Attempts to charge the order to the credit card. Both successful and * failed transactions will be recorded. * * @return a receipt of the transaction. If the charge was successful, the * receipt will be successful. Otherwise, the receipt will contain a * decline note describing why the charge failed. */ Receipt chargeOrder(PizzaOrder order, CreditCard creditCard);}
For the implementation class of BillingService, we will use unit tests for testing. For the rest, we need a FakeCreditCardProcessor to avoid dealing with CreditCard directly, which is the embodiment of object-oriented encapsulation.
Method 1: Call the constructor directly:
Public class RealBillingService implements BillingService {public receept chargeOrder (PizzaOrder order, CreditCard creditCard) {incluprocessor = new pipeline (); // constructor creates CreditCardProcessor TransactionLog transactionLog = new pipeline (); // constructor creates the TransactionLog object try {ChargeResult result = processor. charge (creditCard, order. getAmount (); transactionLog. l OgChargeResult (result); return result. wasSuccessful ()? Receipt. forSuccessfulCharge (order. getAmount (): receept. forDeclinedCharge (result. getDeclineMessage ();} catch (UnreachableException e) {transactionLog. logConnectException (e); return receept. forSystemFailure (e. getMessage ());}}}
Such Code lacks modularity and testability, because it directly relies on the CreditCardProcessor implementation class during compilation, and the coupling is too strong.
Method 2: Use the factory mode:
A factory class can decouple the client and implementation. A simple factory uses a static method to obtain or set the interface implementation. The following is the same version:
public class CreditCardProcessorFactory { private static CreditCardProcessor instance; public static void setInstance(CreditCardProcessor creditCardProcessor) { instance = creditCardProcessor; } public static CreditCardProcessor getInstance() { if (instance == null) { return new SquareCreditCardProcessor(); } return instance; }}
In the client code, you only need to use the factory class to replace the new Keyword:
public class RealBillingService implements BillingService { public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) { CreditCardProcessor processor = CreditCardProcessorFactory.getInstance(); TransactionLog transactionLog = TransactionLogFactory.getInstance(); try { ChargeResult result = processor.charge(creditCard, order.getAmount()); transactionLog.logChargeResult(result); return result.wasSuccessful() ? Receipt.forSuccessfulCharge(order.getAmount()) : Receipt.forDeclinedCharge(result.getDeclineMessage()); } catch (UnreachableException e) { transactionLog.logConnectException(e); return Receipt.forSystemFailure(e.getMessage()); } }}
Unit Testing after the factory model is used:
public class RealBillingServiceTest extends TestCase { private final PizzaOrder order = new PizzaOrder(100); private final CreditCard creditCard = new CreditCard("1234", 11, 2010); private final InMemoryTransactionLog transactionLog = new InMemoryTransactionLog(); private final FakeCreditCardProcessor creditCardProcessor = new FakeCreditCardProcessor(); @Override public void setUp() { TransactionLogFactory.setInstance(transactionLog); CreditCardProcessorFactory.setInstance(creditCardProcessor); } @Override public void tearDown() { TransactionLogFactory.setInstance(null); CreditCardProcessorFactory.setInstance(null); } public void testSuccessfulCharge() { RealBillingService billingService = new RealBillingService(); Receipt receipt = billingService.chargeOrder(order, creditCard); assertTrue(receipt.hasSuccessfulCharge()); assertEquals(100, receipt.getAmountOfCharge()); assertEquals(creditCard, creditCardProcessor.getCardOfOnlyCharge()); assertEquals(100, creditCardProcessor.getAmountOfOnlyCharge()); assertTrue(transactionLog.wasSuccessLogged()); }}
This code is a bit clumsy. A global variable saves the implementation instance, so we need to be very careful about the value assignment and release of the variable. If the tailDown method fails,
The global variable is still valid, which may cause problems for other tests. In this way, you cannot run multiple test cases in parallel. The biggest problem is that as applications expand,
When there are new dependencies, more and more factory classes will appear, reducing application efficiency.
Method 3: dependency Injection
Like the factory mode, dependency injection is also a design mode. Its main principle is to separate behavior from dependency. In the preceding example, RealBillingService is not responsible for creating TransactionLog and CreditCardProcessor objects, in other words, these two objects are passed in the constructor parameters of RealBillingService.
public class RealBillingService implements BillingService { private final CreditCardProcessor processor; private final TransactionLog transactionLog; public RealBillingService(CreditCardProcessor processor, TransactionLog transactionLog) { this.processor = processor; this.transactionLog = transactionLog; } public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) { try { ChargeResult result = processor.charge(creditCard, order.getAmount()); transactionLog.logChargeResult(result); return result.wasSuccessful() ? Receipt.forSuccessfulCharge(order.getAmount()) : Receipt.forDeclinedCharge(result.getDeclineMessage()); } catch (UnreachableException e) { transactionLog.logConnectException(e); return Receipt.forSystemFailure(e.getMessage()); } }}
In this way, we can remove the setUp and tearDown methods without any factory classes to simplify unit testing:
public class RealBillingServiceTest extends TestCase { private final PizzaOrder order = new PizzaOrder(100); private final CreditCard creditCard = new CreditCard("1234", 11, 2010); private final InMemoryTransactionLog transactionLog = new InMemoryTransactionLog(); private final FakeCreditCardProcessor creditCardProcessor = new FakeCreditCardProcessor(); public void testSuccessfulCharge() { RealBillingService billingService = new RealBillingService(creditCardProcessor, transactionLog); Receipt receipt = billingService.chargeOrder(order, creditCard); assertTrue(receipt.hasSuccessfulCharge()); assertEquals(100, receipt.getAmountOfCharge()); assertEquals(creditCard, creditCardProcessor.getCardOfOnlyCharge()); assertEquals(100, creditCardProcessor.getAmountOfOnlyCharge()); assertTrue(transactionLog.wasSuccessLogged()); }}
Unfortunately, the BillingService client needs to create its Dependencies. Now it is better to have a framework to automatically create these dependencies. Otherwise, we need to manually create these circular dependencies.
When Guice is running, use Guice for dependency injection.
The dependency injection mode makes the code more concise, easier to test, and Guice makes it easy to write. In the preceding billing example, the first step is to tell Guice how to map interfaces and implementation classes, which are configured through the Guice Module, it can be any Java class that implements the Module Interface.
Public class BillingModule extends AbstractModule {@ Override protected void configure () {bind (TransactionLog. class ). to (DatabaseTransactionLog. class); // map the interface and implementation to bind (CreditCardProcessor. class ). to (PaypalCreditCardProcessor. class); bind (BillingService. class ). to (RealBillingService. class );}}
When dependency injection is performed, objects receive dependencies in their construction parameters. To create an object, you must first create its Dependencies. However, to create each dependency, you must create each dependency. Therefore, when creating an object, you must create an object graph. Creating an object graph manually is labor-intensive, tends to be incorrect, and makes the test difficult. Fortunately, Guice can create this object graph for us, and all we need to do is configure it to tell it to create this object graph accurately.
Add the @ Inject annotation to the constructor of RealBillingService. Guice checks the constructor that has added the annotation and finds the value for each parameter.
Adding the @ Inject annotation is performed in the configuration method, telling Guice that if an object graph is created, of course, the @ Inject annotation can be placed not only on the constructor, but also on the setter method and field.
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; } public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) { try { ChargeResult result = processor.charge(creditCard, order.getAmount()); transactionLog.logChargeResult(result); return result.wasSuccessful() ? Receipt.forSuccessfulCharge(order.getAmount()) : Receipt.forDeclinedCharge(result.getDeclineMessage()); } catch (UnreachableException e) { transactionLog.logConnectException(e); return Receipt.forSystemFailure(e.getMessage()); } }}
Finally, we integrate these into the following: the Injector class is used to obtain instances of any bound classes:
public static void main(String[] args) {Injector injector = Guice.createInjector(new BillingModule());BillingService billingService = injector.getInstance(BillingService.class);...}
Android RoboGuice User Guide (12): How to bind a generic type
[Java] class Example {@ Injectvoid setList (List <String list ){...}} class Example {@ Injectvoid setList (List <String list ){...}} you can use TypeLiteral to create this binding. TypeLiteral is a special type that can be used to represent parameterized types. [Java] @ Overridepublic void configure () {bind (new TypeLiteral <List <String (){}). toInstance (new ArrayList <String () ;}@ Override public void configure () {bind (new TypeLiteral <List <String (){}). toInstance (new ArrayList <String ();} or use the @ Provides method: [java] @ ProvidesList <String providesListOfString () {return new ArrayList <String ();} @ Provides List <String providesListOfString () {return new ArrayList <String () ;} So far, I have basically introduced Google Guice usage. The above usage is also applicable to JavaSE and Java EE platforms. For more information, see the English documentation, next we will introduce the Dependency Injection (Roboguice) usage related to the Android platform.
Android RoboGuice User Guide (12): How to bind a generic type
[Java] class Example {@ Injectvoid setList (List <String list ){...}} class Example {@ Injectvoid setList (List <String list ){...}} you can use TypeLiteral to create this binding. TypeLiteral is a special type that can be used to represent parameterized types. [Java] @ Overridepublic void configure () {bind (new TypeLiteral <List <String (){}). toInstance (new ArrayList <String () ;}@ Override public void configure () {bind (new TypeLiteral <List <String (){}). toInstance (new ArrayList <String ();} or use the @ Provides method: [java] @ ProvidesList <String providesListOfString () {return new ArrayList <String ();} @ Provides List <String providesListOfString () {return new ArrayList <String () ;} So far, I have basically introduced Google Guice usage. The above usage is also applicable to JavaSE and Java EE platforms. For more information, see the English documentation, next we will introduce the Dependency Injection (Roboguice) usage related to the Android platform.