您的位置:首页 > 产品设计 > UI/UE

程序员的量化交易之路(12)--Guice库的学习

2015-06-02 17:37 387 查看
Google Guice学习

在学习Cointrader的代码时候,要用到Google的guice用于依赖注入(Dependence Injection)。所以,在这里系统的学习一遍。这里我主要是对其官方文档进行有选择性的学习翻译。

动机

写类对象之间的组合关系是非常麻烦的一件事情。这里以一个网站订披萨作为案例:

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

}

直接用构造函数调用

public class RealBillingService implements BillingService {

  public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {

    CreditCardProcessor processor = new PaypalCreditCardProcessor();//

    TransactionLog transactionLog = new DatabaseTransactionLog();//这两个直接在构造函数里面构造,不利于模块化和单元测试

    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 CreditCardProcessorFactory {

  private static CreditCardProcessor instance;

  public static void setInstance(CreditCardProcessor processor) {

    instance = processor;

  }

  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 processor = new FakeCreditCardProcessor();

  @Override public void setUp() {//设置

    TransactionLogFactory.setInstance(transactionLog);

    CreditCardProcessorFactory.setInstance(processor);

  }

  @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, processor.getCardOfOnlyCharge());

    assertEquals(100, processor.getAmountOfOnlyCharge());

    assertTrue(transactionLog.wasSuccessLogged());

  }

}

依赖注入

和工厂方法一样,依赖注入也是一种代码实现上的设计模式,它的原则是“separate behavior from dependency resolution”。

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

    }

  }

}

这时候,不需要任何工厂类:

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 processor = new FakeCreditCardProcessor();

  public void testSuccessfulCharge() {

    RealBillingService billingService

        = new RealBillingService(processor, transactionLog);

    Receipt receipt = billingService.chargeOrder(order, creditCard);

    assertTrue(receipt.hasSuccessfulCharge());

    assertEquals(100, receipt.getAmountOfCharge());

    assertEquals(creditCard, processor.getCardOfOnlyCharge());

    assertEquals(100, processor.getAmountOfOnlyCharge());

    assertTrue(transactionLog.wasSuccessLogged());

  }

}

然后,使用:

  public static void main(String[] args) {

    CreditCardProcessor processor = new PaypalCreditCardProcessor();

    TransactionLog transactionLog = new DatabaseTransactionLog();

    BillingService billingService

        = new RealBillingService(processor, transactionLog);

    ...

  }

使用guice进行依赖注入

首先需要通过Guice的module来配置接口和实现之间的映射。

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

  }

}

我们在RealBillingService的构造函数上添加@Inject标注,它会引导Guice去使用module。

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

    }

  }

}

最后使用:

public static void main(String[] args) {

    Injector injector = Guice.createInjector(new BillingModule());

    BillingService billingService = injector.getInstance(BillingService.class);

    ...

  }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: