程序员人生 网站导航

Google Guice之牛刀小试

栏目:互联网时间:2014-11-26 08:11:02
Google Guice由google推出的1开源软件,是超轻量级的,下1代的,为Java 5及后续版本设计的依赖注入容器,其功能类似于如日中天的Spring。

下面我们就来了解1下Guice,在此之前,先看1个官方例子:在利用程序中,要把所有的东西装配起来是1件很乏味的事件,这要触及到连接数据,服务,表现层类等方面,这是1个比萨饼订购网站的计费代码例子用于这些方面的对照。

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); }

BillingService的实现类,我们会用单元测试进行测试,剩下的我们需要1个FakeCreditCardProcessor来避免其直接与CreditCard打交道,这是面向对象中封装的表现。
第1种实现方式:直接调用构造方法:

public class RealBillingService implements BillingService { public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) { CreditCardProcessor processor = new PaypalCreditCardProcessor();//构造方法创建CreditCardProcessor TransactionLog transactionLog = new DatabaseTransactionLog();//构造方法创建TransactionLog对象 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()); } } }

这样的代码缺少模块性与可测试性,由于这在编译期就直接依赖了CreditCardProcessor实现类,耦合性太强。

第2种实现方式:使用工厂模式:
使用1个工厂类可使客户端与实现解耦,1个简单工厂使用1静态方法来获得或设置接口实现,下面是1样版:


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; } }

在客户端代码中,只需要使用工厂类把new关键字替换就好了:


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()); } } }

在使用了工厂模式后的单元测试:

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()); } }

这样代码还是有点笨拙,1个全局变量保存了实现实例,这样我们要非常谨慎该变量的赋值与值释放,如果tailDown方法失败了,
全局变量依然有效,这可能就会给其它的测试带来问题,这样还不能并行运行多个测试用例。最大的问题在于,随着利用的扩大,
有新的依赖的时候就会出现愈来愈多的工厂类,使利用效力降落。

第3种方式:依赖注入
像工厂模式1样,依赖注入也是1种设计模式,其主要原则是将行动与依赖分离开来,在上面的例子中RealBillingService不负责TransactionLog与CreditCardProcessor对象的创建,换之的是这两个对象在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()); } } }

这样,我们不需要任何的工厂类,还可以移除setUp与tearDown方法来简化单元测试:

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()); } }

现在不幸的是,BillingService的客户端需要创建它的依赖,现在最好是有1框架来自动创建这些依赖,不然我们就要手动地去创建这些循环依赖。


现在到Guice出场的时候,使用Guice进行依赖注入

依赖注入模式可让代码更具模块性,更容易于测试,而且Guice使其易于编写。在上面的计费例子中,我们第1步要告知Guice怎样映照接口与实现类,这是通过Guice的Module进行配置的,它可以是任何1个实现了Module接口的Java类。

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); } }

当进行依赖注入的时候,对象在它们的构造参数中接收依赖。要创建1个对象,必须先创建出它的依赖,但是要创建每个依赖,就要创建依赖的每个依赖,如此往复。所以当你创建1个对象的时候真正要创建的是1张对象图。手动创建1张对象图是费劳力的,趋于毛病的,而且使测试变得困难。好在Guice可以为我们创建这张对象图,而我们要做的就是进行配置告知它如果去准确地创建这张对象图。

在RealBillingService的构造方法中添加@Inject注解,Guice会检查添加了注解的构造方法,并为每个参数查找值。
添加@Inject注解就是在进行配置式作,告知Guice如果创建对象图,固然@Inject注解不但可以放置于构造方法上,也能够放置于setter方法与字段上。


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()); } } }

最后,我们将这些整合在1起以下,Injector类用于获得任何绑定类的实例:

public static void main(String[] args) { Injector injector = Guice.createInjector(new BillingModule()); BillingService billingService = injector.getInstance(BillingService.class); ... }



------分隔线----------------------------
------分隔线----------------------------

最新技术推荐