应用抽象工厂模式自己动手写一个ioc
本文的作者Danny hui似乎是TTS上的新人,我從Google搜不出一點關(guān)于本人的信息。從通過本文可以看出他對模式與IoC有自己獨到的見解,本文在TTS上引發(fā)很多網(wǎng)友回帖,反響不一。那么我們現(xiàn)在來看看作者的IoC之路吧。
?
?原文:http://www.theserverside.com/tt/articles/article.tss?l=InjectionwithAbstractFactory
?
簡介
?
本文重點討論的是DI(依賴注入)結(jié)合設(shè)計模式中的Abstract Factory(抽象工廠模式)的優(yōu)勢與弊端。該方式尤其適合以下場合:
l?????????通過dynamic parameters(動態(tài)參數(shù))來建立一個local stateful對象
l?????????當創(chuàng)建對象時拋出了checked exception時進行相應(yīng)處理,
l?????????動態(tài)的封裝對象
像Spring IoC容器,PicoContainer以及Guice都無法圓滿的解決這些問題或者說它們幾乎做不到!!!
?
用Abstractory Factory模式來實現(xiàn)DI
?
?
?
現(xiàn)在通過下面兩種途徑來改進經(jīng)典GoF中的Abstract Factory:
l?????????一個工廠接口來代替抽象工廠類(可行)
l?????????每一個工廠方法的職責就是創(chuàng)建對象,并為其注入依賴
?
?
來看一個簡單的例子吧:
?
?
在這里,ComponentA依賴于ComponentB。為方便進行單元測試ComponentAImpl類,接口ComponentB的實現(xiàn)必須注入到ComponentAImpl中去。下面看看采用Abstract Factory模式完成依賴注入的Java實現(xiàn)代碼:
?
?//Abstract Factory for Dependency Injection
//Factory interface
public interface Module1ServiceFactory {
?ComponentA getComponentA();
?ComponentB getComponentB();
}
//Concrete factory
public class Module1ServiceFactoryImpl implements Module1ServiceFactory {
?private ComponentA componentA;
?private ComponentB componentB;
?private Module1Servicefactory instance;
?private Module1ServicefactoryImpl() {}
?public static synchronized Module1ServiceFactory getInstance() {
??if (null == instance) {
???instance = new Module1ServicefactoryImpl();
???componentA = new ComponentAImpl();
???componentB = new ComponentBImpl();
???componentA.setComponentB(componentB);
??}
??return instance;
?}
?public ComponentA getComponentA() {
??return componentA;
?}
?public ComponentB getComponentB() {
??return componentB;
?}
}
//Client
public class Client {
?public static void main(String[] args) {
??Module1ServiceFactory m1sf =
???Module1ServicefactoryImpl.getInstance();
??ComponentA componentA = m1sf.getComponentA();
??componentA.operationA1();
?}
}
?
?
?
?
?
延遲加載
?
延遲加載對象可以通過改變方法來實現(xiàn),比如說我現(xiàn)在要稍微對getComponentA()進行改動,請看:
?
?public synchronized ComponentA getComponentA() {
??if (null == componentA) {
???componentA = new ComponentAImpl();
??}
??return componentA;
?}
?
當然我們的Module1ServiceFactoryImpl.getInstance()方法也要進行相應(yīng)的改動了,我們可以通過傳遞一個參數(shù)來判斷Module1ServiceFactoryImpl.getInstance()是否需要創(chuàng)建對象。
?
?
?
非Singleton作用域
?
上面的代碼僅僅只是建立singleton對象。如果需要在每次調(diào)用getComponentA()和getComponentB()的時,都返回新創(chuàng)建的對象的話,我們可以對我們的Abstract Factory進行下面的改動:
?
//Concrete factory
public class Module1ServiceFactoryImpl {
?private Module1ServiceFactory instance;
?private Module1ServiceFactoryImpl() {}
?public static synchronized Module1ServiceFactory getInstance() {
??if (null == instance) {
???instance = new Module1ServiceFactoryImpl();
??}
return instance;
?}
?public ComponentA getComponentA() {
??ComponentA componentA = new ComponentAImpl();
??ComponentB componentB = getComponentB();
??componentA.setComponentB(componentB);
??return componentA;
?}
?public ComponentB getComponentB() {
??return new ComponentBImpl();
?}
}
?
?
?
?
?
類似的,我們還可以將一個singleton對象注入到非singleton對象中去。比如說,我們假設(shè)ComponentB此時是singleton,ComponentA為非singleton,那么我們可以這樣:
?
//Concrete factory
public class Module1ServiceFactoryImpl {
?private Module1ServiceFactory instance;
?private ComponentB componentB;
?private Module1ServicefactoryImpl() {}
?public static synchronized Module1ServiceFactory getInstance() {
??if (null == instance) {
???instance = new Module1ServiceFactoryImpl();
???componentB = new ComponentBImpl();
??}
return instance;
?}
?public ComponentA getComponentA() {
??ComponentA componentA = new ComponentAImpl();
??componentA.setComponentB(componentB);
??return componentA;
?}
?public ComponentB getComponentB() {
??return componentB;
?}
}
?
將一個非singleton對象注入到singleton對象也不是做不到,但這種應(yīng)用場合在現(xiàn)實世界中是非常罕見的。盡管如此,但在使用dynamic parameters賦級一個locallocal變量時,創(chuàng)建一個非singleton對象卻很普遍。接下的話題,我們就談?wù)勥@個。
?
?
?
用dynamic parameters為singleton創(chuàng)建一個local stateful對象
?
這是所有IoC框架所面臨的問題。下面的代碼中,仍然假定ComponentA為singletion:
?
?
??public void operationA2() {
??String s = aPrivateMethod();
??int i = anotherMethod();
??ComponentC componentC = new ComponentCImpl(s, i);
??//do something else.
?}
?
?
這里,ComponentAImpl用到了ComponentC接口。雖然,為了更IoC,我們需要將它注入ComponentC的實現(xiàn),而不是直接硬編碼在ComponentAImpl.operationA2()方法中去,這樣做還有一個好處就是,方便單元測試。但問題來了,我們不能將ComponentC作為一個實例變量,因為它是有狀態(tài)的(stateful),它維持著某一個特定的客戶端狀態(tài),不能與其它客戶端進行共享。因此,不能使用常用的setter或construtctor注入方法來實現(xiàn)。
?
使用Abstract Factory模式的話,有兩個方法可以解決這個問題。不過,都得改動Module1ServiceFactory接口,添加下面方法:
?
??ComponentC getComponentC(String s, int i);
?
?
請看我在Module1ServiceFactoryImpl中的實現(xiàn)代碼:
?
public ComponentC getComponentC(String s, int i) {
??return new ComponentCImpl(s, i);
?}
??
?
第一種方法就是將包含它的“工廠”注入到所需的local stateful對象中去:
?
?private Module1ServiceFactory factory;
?public void setModule1ServiceFactory(Module1ServiceFactory factory) {
??this.factory = factory;
?}
//ComponentAImpl.operationA2() becomes:
?public void operationA2() {
??String s = aPrivateMethod();
??int i = anotherMethod();
??ComponentC componentC = factory.getComponentC(s, i);
??//do something else.
?}
?
?
?
?
缺點顯而易見:ComponentAImpl現(xiàn)在與Module1ServiceFactory綁定在一起了,如果要對ComponentAImpl進行單元測試的話,我們不得不mock一個Module1ServiceFactory實現(xiàn)??v然有這個缺陷,但直接為stateful對象注入“工廠”對象的方法也最為簡單。類似的技術(shù)廣泛的在J2EE領(lǐng)域采用,比如說JPA,將我們將entity manager factory可以注入到應(yīng)用程序代碼后,它就專門負責管理自身創(chuàng)建的application-managed entity。(注:如果熟悉Hibernate的話,也可以將entity manager factory想象成HibernateSessionFacory,?application-managed entity想象成Session)
?
?
第二種方法就是將方法抽出,移到抽象類中去,便于單元測試:
?
?
?
?public abstract class AbstractComponentA implements ComponentA {
public void operationA2() {
??String s = aPrivateMethod();
??int i = anotherMethod();
??ComponentC componentC = getComponentC(s, i);
??//do something else.
?}
?public abstract ComponentC getComponentC(String s, int i) ;
}
public class ComponentAImpl extends AbstractComponentA {
?public ComponentC getComponentC(String s, int i) {
??return new ComponentCimpl(s, i);
?}
}
?
?
這種方式類似于Springframework的方法注入(Metod Injection),不過Spring不需要傳遞dynamic parameter也能創(chuàng)建stateful對象。此時,單元測試可以不需要再實現(xiàn)任何mock工廠。但是,這仍然是一個笨拙的辦法。設(shè)想一下,我們的類里如果有10個這樣的local stateful對象,那么我們需要提供10個抽象方法,才能再次讓單元測試變得簡單,可是代價就造成是更加混亂的應(yīng)用程序代碼。
?
Springframework還可以通過使用Java反射機構(gòu)還解決類似問題。但這更加復(fù)雜了,并且不適合正常應(yīng)用程序編碼工作。
?
?
處理創(chuàng)建對象時拋出的checked exceptions
?
這個問題也是讓IoC容器頭痛的。如果checked exception在對象創(chuàng)建時拋出,應(yīng)用程序可能希望是能捕獲并且能夠恢復(fù)。我們來看一下這個關(guān)于Web Service的需求實例:當客戶端嘗試建立web service的stub時,并且此時服務(wù)端web service還不可用,那么客戶端是能夠捕獲stub所拋出的異常,然后顯示相應(yīng)信息,并詢問用戶是否稍后繼續(xù)再次連接。如果單純用IoC容器的話,拋出具體指定的checked exception是很困難的。而手工代碼卻可以很輕松的解決這個問題——我們可以簡單的將“工廠”的checked exceptions拋出,留給應(yīng)用程序代碼去手工處理或者恢復(fù)它們。
?
?
動態(tài)封裝對象
很多場合下,一個接口對應(yīng)著多個不同的實現(xiàn)類,類型實例就是設(shè)計模式中的策略模式。那么,用一個參數(shù)就可以決定具體哪個實現(xiàn)類應(yīng)該被注入到相應(yīng)的封裝對象中去。然而使用基于XML和IoC容器是靜態(tài)封裝對象,很難實現(xiàn)此功能。也許編程式的IoC容器可以解決動態(tài)依賴問題,但我要說的是我們的Abstract Factory則更簡單直接,看看下面的代碼就知道了:
?
?
?
?
?//Concrete factory
public class Module1ServiceFactoryImpl {
?...
?public ComponentA getComponentA(int strategy) {
??ComponentA componentA = new ComponentAImpl();
??ComponentB componentB = getComponentB(strategy);
??componentA.setComponentB(componentB);
??return componentA;
?}
?public ComponentB getComponentB(int strategy) {
??switch(strategy) {
???case STRATEGYA:
????return new StrategyA();
???case STRATEGYB:
????return new StrategyB();
???default:
????return null;
??????????????????????? }
?}
}
?
?
?
注意這里StrategyA和StrategyB共享實現(xiàn)了ComponentB接口。
?
?
?
?
結(jié)束語
?
今天我們談到的運用依賴注入和Absratct Factory設(shè)計模式來解決下列問題:
1.???通過動態(tài)參數(shù),創(chuàng)建local stateful對象
2.???處理創(chuàng)建對象時拋出的checked exceptions
3.???動態(tài)封裝注入對象
?
除此以外,該方法與其它IoC容器相比,性能更好,畢竟是直接硬編碼嘛。那么最大的缺點自然就是要手工寫很多基礎(chǔ)代碼了,并且如果要延遲加載與主動加載之間來回切換的話,代碼的改動量是很可觀的。不過呢,這樣的需求幾乎是不存在的。
?
可單元測試的關(guān)鍵點是基于接口而非實體類編程。這樣的話mock對象可以注入到任何需要注入的地方去。
?
不管怎么樣,有時候在我們的應(yīng)用程序中,依賴注入是一個不得不解決的問題。所有IoC容器以及手工的依賴注入解決方案都是專注于各自的領(lǐng)域——Spring IoC使用XML配置,Google Guice使用特殊的Java,我們的Abstract Factory也是如此。通過這些解決方案,我們可以避免應(yīng)用程序中到處顯現(xiàn)依賴的編碼,類與類之間耦合度降低,使用Mock對象就可以正常進行單元測試。
對所有的IoC容器來說,無論是聲明式的還是編程式的,它們的目的就是作為一個對象創(chuàng)建和封裝工廠。同樣我們的Abstract Factory也是如此,只不過這是一個可定制的依賴注入解決方案。
最后,使用IoC容器,我們可以消除類與類之間的依賴,從而讓單元測試變得更加簡單。但代價就是你不得不再次依賴于第三方Jar包或XML配置文件。IoC容器并沒有一個統(tǒng)一的標準,每個框架所提供的特性和功能都是不同的,因為一旦你使用了某個IoC框架,就意味
總結(jié)
以上是生活随笔為你收集整理的应用抽象工厂模式自己动手写一个ioc的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 抽象工厂+反射+依赖注入 实现对数据访问
- 下一篇: 利用抽象工厂创建DAO、利用依赖注入去除