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

简介:
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);
    ...
  }
相关文章
|
SQL Java 应用服务中间件
Java项目防止SQL注入的四种方案
Java项目防止SQL注入的四种方案
476 0
|
数据采集 存储 数据挖掘
如何编写一个完整的量化交易程序源码
如何编写一个完整的量化交易程序源码
|
开发工具 git
Git回滚代码到某个commit
回退命令:$ git reset --hard HEAD^ 回退到上个版本$ git reset --hard HEAD~3 回退到前3次提交之前,以此类推,回退到n次提交之前 $ git reset --hard commit_id 退到/进到 指定commi...
11269 0
|
存储 Oracle NoSQL
阿里云存储系统盘或数据盘性能级别PL0、PL1、PL2和PL3什么意思?
阿里云块存储ESSD云盘性能级别PL0、PL1、PL2和PL3,性能级别PL不同云盘容量、单盘最大IOPS、吞吐量及使用业务场景也不同,阿里云百科分享ESSD云盘性能级别PL详解
1045 0
阿里云存储系统盘或数据盘性能级别PL0、PL1、PL2和PL3什么意思?
|
存储 SQL 分布式计算
Docker容器化急速部署ClickHouse
Docker容器化急速部署ClickHouse
CDR2023中文最新版本安装下载教程
CDR2023是最新发布的一款强大且专业的图像设计软件,适用于平面设计、装饰设计、产品包装设计等领域。coco玛奇朵其主要功能如下:
768 0
|
运维
分享一些个人总结的阿里云产品使用和运维的经验
个人最近三年阿里云使用和运维经验的总结分享。年底我终于把它写成了一个文档,希望分享给大家。我做的都是基础的运维,没什么高深的内容。可能还会有错误,请大家批评指正!
535 0
|
数据采集 机器学习/深度学习 数据可视化
学会MATLAB到底能做什么?
  MATLAB是Mathworks公司于1984年推出的一套高性能的数值计算的可视化软件,它集数值分析、矩阵运算、信号处理和图形显示于一体,可方便地应用于数学计算、算法开发、数据采集、系统建模和仿真、数据分析和可视化、科学和工程绘图、应用软件开发等方面。MATLAB之所以能够被广泛应用,是因为它将科研工作者从乏味的Fortran、C编程中解放出来,使他们真正把精力放在科研和设计的核心问题上,从而大大提高了工作效率。在MATLAB环境中描述问题及编制求解问题的程序时,用户可以按照符合人们的科学思维方式和数学表达习惯的语言形式来书写程序。   MATLAB这个词代表“矩阵实验室”(matrix
4073 0
|
存储 弹性计算 网络协议
NAS支持IPv6访问的使用指南
阿里云文件存储(Network Attached Storage,简称 NAS) 提供VPC内的Ipv4和Ipv6的双栈访问,助力企业平滑升级到Ipv6架构。
9222 0
下一篇
oss教程