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語句:
CreditCardProcessor processor = CreditCardProcessorFactory.getInstance();
TransactionLog transactionLog = TransactionLogFactory.getInstance();//工廠方法
這種工廠方法使得我們可以友善地寫單元測試:
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”。
private final CreditCardProcessor processor;
private final TransactionLog transactionLog;
public RealBillingService(CreditCardProcessor processor,
TransactionLog transactionLog) {
this.processor = processor;
this.transactionLog = transactionLog;
這時候,不需要任何工廠類:
public void testSuccessfulCharge() {
RealBillingService billingService
= new RealBillingService(processor, transactionLog);
然後,使用:
public static void main(String[] args) {
CreditCardProcessor processor = new PaypalCreditCardProcessor();
TransactionLog transactionLog = new DatabaseTransactionLog();
BillingService billingService
...
首先需要通過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。
@Inject
public RealBillingService(CreditCardProcessor processor,
最後使用:
public static void main(String[] args) {
Injector injector = Guice.createInjector(new BillingModule());
BillingService billingService = injector.getInstance(BillingService.class);