Google Guice之牛刀小試
來源:程序員人生 發布時間:2014-11-26 08:11:02 閱讀次數:3062次
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);
...
}
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈