<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    白玉成的專欄

    Eclipse,讓我歡喜讓我憂!

      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      17 隨筆 :: 0 文章 :: 6 評(píng)論 :: 0 Trackbacks
     

    DAO設(shè)計(jì)模式

    DAO(Data Access Object)模式實(shí)際上是兩個(gè)模式的組合,即Data Accessor 模式和 Active Domain Object 模式,其中 Data Accessor 模式實(shí)現(xiàn)了數(shù)據(jù)訪問(wèn)和業(yè)務(wù)邏輯的分離,而Active Domain Object 模式,其中Data Accessor模式實(shí)現(xiàn)了數(shù)據(jù)訪問(wèn)和業(yè)務(wù)邏輯的分離,而Active Domain Object 模式實(shí)現(xiàn)了業(yè)務(wù)數(shù)據(jù)的對(duì)象化封裝,一般我們將這兩個(gè)模式組合使用,因此,考慮到這些因素,這里將其作為同一個(gè)主題加以討論。如圖展示了DAO模式的實(shí)現(xiàn)層次。

    DAO模式通過(guò)對(duì)業(yè)務(wù)層提供數(shù)據(jù)抽象層接口,實(shí)現(xiàn)了以下目標(biāo):

    1. 數(shù)據(jù)存儲(chǔ)邏輯的分離
    通過(guò)對(duì)數(shù)據(jù)訪問(wèn)邏輯進(jìn)行抽象,為上層機(jī)構(gòu)提供抽象化的數(shù)據(jù)訪問(wèn)接口。業(yè)務(wù)層無(wú)需關(guān)心具體的select,insert,update操作,這樣,一方面避免了業(yè)務(wù)代碼中混雜JDBC調(diào)用語(yǔ)句,使得業(yè)務(wù)落實(shí)實(shí)現(xiàn)更加清晰,另一方面,由于數(shù)據(jù)訪問(wèn)幾口語(yǔ)數(shù)據(jù)訪問(wèn)實(shí)現(xiàn)分離,也使得開發(fā)人員的專業(yè)劃分成為可能。某些精通數(shù)據(jù)庫(kù)操作技術(shù)的開發(fā)人員可以根據(jù)接口提供數(shù)據(jù)庫(kù)訪問(wèn)的最優(yōu)化實(shí)現(xiàn),而精通業(yè)務(wù)的開發(fā)人員則可以拋開數(shù)據(jù)曾德繁瑣細(xì)節(jié),專注于業(yè)務(wù)邏輯編碼。

    2. 數(shù)據(jù)訪問(wèn)底層實(shí)現(xiàn)的分離
    DAO
    模式通過(guò)將數(shù)據(jù)訪問(wèn)計(jì)劃分為抽象曾和實(shí)現(xiàn)曾,從而分離了數(shù)據(jù)使用和數(shù)據(jù)訪問(wèn)的地稱實(shí)現(xiàn)細(xì)節(jié)。這意味著業(yè)務(wù)層與數(shù)據(jù)訪問(wèn)的底層細(xì)節(jié)無(wú)關(guān),也就是說(shuō),我們可以在保持上層機(jī)構(gòu)不變得情況下,通過(guò)切換底層實(shí)現(xiàn)來(lái)修改數(shù)據(jù)訪問(wèn)的具體機(jī)制,常見(jiàn)的一個(gè)例子就是,我們可以通過(guò)僅僅替換數(shù)據(jù)訪問(wèn)曾實(shí)現(xiàn),將我們的系統(tǒng)部署在不同的數(shù)據(jù)庫(kù)平臺(tái)之上。

    3. 資源管理和調(diào)度的分離
    在數(shù)據(jù)庫(kù)操作中,資源的管理和調(diào)度是一個(gè)非常值得關(guān)注的主題。大多數(shù)系統(tǒng)的性能瓶頸往往并非集中于業(yè)務(wù)邏輯處理本身。在系統(tǒng)涉及的各種資源調(diào)度過(guò)程中,往往存在著最大的性能黑洞,而數(shù)據(jù)庫(kù)作為業(yè)務(wù)系統(tǒng)中最重要的系統(tǒng)資源,自然也成為關(guān)注的焦點(diǎn)。DAO模式將數(shù)據(jù)訪問(wèn)邏輯從業(yè)務(wù)邏輯中脫離開來(lái),使得在數(shù)據(jù)訪問(wèn)層實(shí)現(xiàn)統(tǒng)一的資源調(diào)度成為可能,通過(guò)數(shù)據(jù)庫(kù)連接池以及各種緩存機(jī)制(Statement Cache,Data Cache等,緩存的使用是高性能系統(tǒng)實(shí)現(xiàn)的一個(gè)關(guān)鍵所在)的配合使用,往往可以保持上層系統(tǒng)不變的情況下,大幅度提升系統(tǒng)性能。

    4.?dāng)?shù)據(jù)抽象
    在直接基于JDBC調(diào)用的代碼中,程序員面對(duì)的數(shù)據(jù)往往是原始的RecordSet數(shù)據(jù)集,誠(chéng)然這樣的數(shù)據(jù)集可以提供足夠的信息,但對(duì)于業(yè)務(wù)邏輯開發(fā)過(guò)程而言,如此瑣碎和缺乏寓意的字段型數(shù)據(jù)實(shí)在令人厭倦。
    DAO
    模式通過(guò)對(duì)底層數(shù)據(jù)的封裝,為業(yè)務(wù)曾提供一個(gè)面向?qū)ο蟮慕涌冢沟脴I(yè)務(wù)邏輯開發(fā)員可以面向業(yè)務(wù)中的實(shí)體進(jìn)行編碼。通過(guò)引入DAO模式,業(yè)務(wù)邏輯更加清晰,且富于形象性和描述性,這將為日后的維護(hù)帶來(lái)極大的便利。試想,在業(yè)務(wù)曾通過(guò)Customer.getName方法獲得客戶姓名,相對(duì)于直接通過(guò)SQL語(yǔ)句訪問(wèn)數(shù)據(jù)庫(kù)表并從ResultSet中獲得某個(gè)字符型字段而言,哪種方式更加易于業(yè)務(wù)邏輯的形象化和簡(jiǎn)潔化?

    空洞地談些理論固然沒(méi)有什么價(jià)值,我們需要看到的是通過(guò)對(duì)應(yīng)用設(shè)計(jì)模式之后,我們的代碼到底有怎樣的改觀,進(jìn)而才能對(duì)設(shè)計(jì)帶來(lái)的優(yōu)劣有所感悟。下面讓我們來(lái)看看代碼:

    代碼

    1. Public BigDecimal calcAmount(String customerID,BigDecimal amount){   
    2.             //根據(jù)客戶ID獲得客戶記錄   
    3.         Customer customer  = CustomerDAO.getCustomer(customerID);   
    4.      
    5.            //根據(jù)客戶登記獲得打折規(guī)則   
    6.          Promotion promotion = PromotionDAO.getPromotion(customer.getLevel());   
    7.   
    8.            //累積客戶總消費(fèi)額,并保存累計(jì)結(jié)果   
    9.          Customer.setSumAmount(customer.getSumAmount().add(amount));   
    10.          CustomerDAO.save(customer);   
    11.   
    12.           //返回打折后金額   
    13.           Return  amount.multiply(promotion.getRatio());   
    14. }  

    這樣的代碼相信已經(jīng)足夠明晰,即使對(duì)于缺乏數(shù)據(jù)庫(kù)技術(shù)基礎(chǔ)的讀者也可以輕松閱讀。

    從上面這段代碼中,我們可以看到,通過(guò)DAO模式對(duì)各個(gè)數(shù)據(jù)庫(kù)對(duì)象進(jìn)行封裝,我們對(duì)業(yè)務(wù)層屏蔽了數(shù)據(jù)庫(kù)訪問(wèn)的底層實(shí)現(xiàn),業(yè)務(wù)曾僅包含與本領(lǐng)域相關(guān)的邏輯對(duì)象和算法,這樣對(duì)于業(yè)務(wù)邏輯開發(fā)人員(以及日后專注于業(yè)務(wù)邏輯的代碼閱讀者)而言,面對(duì)的是一個(gè)簡(jiǎn)潔明快的邏輯實(shí)現(xiàn)結(jié)構(gòu)。業(yè)務(wù)層的開發(fā)和維護(hù)將變得更加簡(jiǎn)單。
    DAO
    模式中,數(shù)據(jù)庫(kù)訪問(wèn)層實(shí)現(xiàn)被隱藏到Data Accessor中,前面說(shuō)過(guò),DAO模式實(shí)際上是兩個(gè)模式的組合,即Data Accessor Domain Object模式。
    何謂 Data Accessor?即將數(shù)據(jù)訪問(wèn)的實(shí)現(xiàn)機(jī)制加以封裝,與數(shù)據(jù)的使用代碼相分離,從外部來(lái)看,Data Accessor 提供了黑盒式的數(shù)據(jù)存取接口。

    Domain Object則提供了對(duì)所面向領(lǐng)域內(nèi)對(duì)象的封裝。
    從某種意義上,我們可以這么理解:

    代碼

    1. Data Accessor object (DAO) =Data +Accessor + domain object  

    這個(gè)等式自左向右,形象地描述了設(shè)計(jì)分離的3個(gè)層次。
    現(xiàn)在,對(duì)于上面的例子,來(lái)看看業(yè)務(wù)層后所隱藏的實(shí)現(xiàn)細(xì)節(jié):
    首先,我們這個(gè)計(jì)算打折后金額的業(yè)務(wù)過(guò)程中,涉及了兩個(gè)業(yè)務(wù)對(duì)象,即客戶對(duì)象Customer,和促銷規(guī)則對(duì)象Promotion。自然,這兩個(gè)對(duì)象也就成為了此業(yè)務(wù)領(lǐng)域(Business Domain)中的Domain Object,所謂Domain Object,簡(jiǎn)單來(lái)講就是對(duì)領(lǐng)域內(nèi)(Domain)涉及的各個(gè)數(shù)據(jù)對(duì)象,反映到代碼,就是一個(gè)擁有相關(guān)屬性的getter,setter方法的JavaClass(Java Bean)

    CustomerCustomerDao為例,實(shí)現(xiàn)代碼如下(Promotion PromotionDAO的實(shí)現(xiàn)代碼與此類似)

    DAO 模式的進(jìn)一步改良

    上面的例子中我們通過(guò)DAO模式實(shí)現(xiàn)了業(yè)務(wù)路基與數(shù)據(jù)邏輯的分離。對(duì)于專項(xiàng)開發(fā)(為特定客戶環(huán)境指定的特定業(yè)務(wù)系統(tǒng))而言,這樣的分離設(shè)計(jì)差不多已經(jīng)可以實(shí)現(xiàn)開發(fā)過(guò)程中業(yè)務(wù)層面與數(shù)據(jù)層面的相對(duì)獨(dú)立,并且在實(shí)現(xiàn)復(fù)雜性與結(jié)構(gòu)清晰性上達(dá)到較好的平衡。

    然而,對(duì)于一個(gè)產(chǎn)品化的業(yè)務(wù)系統(tǒng)而言,目前的設(shè)計(jì)卻仍稍顯不足。相對(duì)專項(xiàng)原發(fā)的軟件項(xiàng)目而言,軟件產(chǎn)品往往需要在不同客戶環(huán)境下及時(shí)部署。一個(gè)典型情況就是常見(jiàn)的論壇系統(tǒng),一個(gè)商業(yè)論壇系統(tǒng)可能會(huì)部署在廠前上萬(wàn)個(gè)不同的客戶環(huán)境中。誠(chéng)然,由于java良好的跨平臺(tái)支持,我們?cè)诓僮飨到y(tǒng)之間大可輕易遷移,但在另外一個(gè)層面,數(shù)據(jù)庫(kù)層,卻仍然面臨著平臺(tái)遷移的窘境。客戶可能已經(jīng)購(gòu)買了Oracle,SQLServer,Sybase 或者其他類型的 數(shù)據(jù)庫(kù)。這就意味著我們的產(chǎn)品必須能部署在這些平臺(tái)上,才能滿足客戶的需求。

    對(duì)于我們現(xiàn)有的設(shè)計(jì)而言,為了滿足不同客戶的需求,我們可以實(shí)現(xiàn)針對(duì)不同類型數(shù)據(jù)庫(kù)的
    Data Accessor,
    并根據(jù)客戶實(shí)際部署環(huán)境,通過(guò)類文件的靜態(tài)替換來(lái)實(shí)現(xiàn)。顯然,這樣的實(shí)現(xiàn)方式在面對(duì)大量客戶和復(fù)雜的部署環(huán)境時(shí),將大大增加部署和維護(hù)工作的難度和復(fù)雜性。回憶一下開閉原則”(Open-Close Principle) –對(duì)擴(kuò)展開放,對(duì)修改封閉。我們應(yīng)該采取適當(dāng)?shù)脑O(shè)計(jì),將此類因素帶來(lái)的變動(dòng)(類的靜態(tài)替換)屏蔽在系統(tǒng)之外。

    為了實(shí)現(xiàn)跨數(shù)據(jù)庫(kù)平臺(tái)移植,或者展開來(lái)說(shuō),為了支持不同數(shù)據(jù)訪問(wèn)機(jī)制之間的可配置切換,我們需要在目前的DAO層引入Factory模式和Proxy模式。

    這里所謂的不同數(shù)據(jù)訪問(wèn)機(jī)制,包括了不同數(shù)據(jù)庫(kù)本身的訪問(wèn)實(shí)現(xiàn),同時(shí)也包括了對(duì)于同一數(shù)據(jù)庫(kù)德不同訪問(wèn)機(jī)制的兼容。例如我們的系統(tǒng)部署在小客戶環(huán)境中,可能采用了基于JDBC的實(shí)現(xiàn),而在企業(yè)環(huán)境中部署時(shí),可能采用CMP作為數(shù)據(jù)訪問(wèn)的底層實(shí)現(xiàn),以獲得服務(wù)器集群上的性能優(yōu)勢(shì)(CMP具體怎樣還有待商榷,這里暫且將其作為一個(gè)理由)

    Factory模式的引入

    由于需要針對(duì)不同的數(shù)據(jù)庫(kù)訪問(wèn)機(jī)制分別提供各自版本的Data Accessor實(shí)現(xiàn),自然我們會(huì)想通過(guò) Java Interface 定義一個(gè)調(diào)用接口,然后對(duì)這個(gè)調(diào)用接口實(shí)現(xiàn)不同數(shù)據(jù)庫(kù)的 Data Accessor。通過(guò)以接口作為調(diào)用界面和實(shí)現(xiàn)規(guī)范,我們就可以避免代碼只能給對(duì)具體實(shí)現(xiàn)的依賴。
    對(duì)于例子中的CustomerDAO而言,我們可以抽象出如下的接口:

    代碼

    1. Public interface CustomerDAO{   
    2.     Public Customer getCustomer(String custID);   
    3.     Puboic void save (Customer customer);   
    4. }  

    這里作為示例,提供了兩個(gè)實(shí)現(xiàn),一個(gè)基于MySql數(shù)據(jù)庫(kù),一個(gè)基于Oracle,對(duì)這里的簡(jiǎn)單示例而言,基于OracleMySql的實(shí)現(xiàn)并沒(méi)有什么太大區(qū)別,只是為了說(shuō)明系統(tǒng)設(shè)計(jì)的結(jié)構(gòu)。

    作為最常用的創(chuàng)建模式,Factory模式在這里起到來(lái)接接口和實(shí)現(xiàn)的橋梁作用。通過(guò)Factory模式,我們可以根據(jù)具體需要加載相應(yīng)得實(shí)現(xiàn),并將此實(shí)現(xiàn)作為所對(duì)應(yīng)接口的一個(gè)實(shí)例提供給業(yè)務(wù)層使用:

    代碼

    1. CustomerDAO custDAO =(CustomerDAO)DAOFactory.getDAO(CustomerDAO.class);   
    2. Customer customer = custDAO.getCustomer(customerID);  

    通過(guò)上面的代碼我們可以看到,通過(guò)接口我們將具體的DAO實(shí)現(xiàn)從代碼中分離。
    也就是說(shuō),業(yè)務(wù)層通過(guò)接口調(diào)用底層實(shí)現(xiàn),具體的DAO實(shí)現(xiàn)類不會(huì)出現(xiàn)在我們的業(yè)務(wù)代碼中。而具體實(shí)現(xiàn)類在配置文件中加以配置,之后DAOFactory.getDAO方法通過(guò)讀取配置文件獲得當(dāng)前我們期望使用的視線類的類名,再通過(guò)Java Class動(dòng)態(tài)加載機(jī)制加載后返回。

    從而我們的代碼并不依賴于某個(gè)特定的實(shí)現(xiàn)類,只需要在部署的時(shí)候在配置文件中指定當(dāng)前采用的實(shí)現(xiàn)類即可。
    本例中,為了提高性能,避免每次調(diào)用都讀取配置文件所引起的大量磁盤操作,采用了HashMap作為DAO緩存實(shí)現(xiàn)示例:

    代碼

    1. package net.wanjin.lab.persistence.dao;   
    2.   
    3. import java.util.HashMap;   
    4.   
    5. public class DAOFactory {   
    6.        
    7.     private static HashMap daoMap = null;   
    8.        
    9.     /**  
    10.      * Return a implemetation instance of the specified DAO Interface  
    11.      * @return the DAO Implemmenation Class Instance  
    12.      */  
    13.     public static Object getDAO(Class daoInterface){   
    14.         initial();   
    15.         Object dao = daoMap.get(daoInterface);   
    16.         if(null ==dao){   
    17.             throw new DAOException("No Implementation found of DAO interface =>"  
    18.                     +daoInterface.getName());   
    19.         }   
    20.         return dao;   
    21.     }   
    22.     /**  
    23.      * Initial the DAOFactory  
    24.      * Load DAO Interface and Implementation In daoMap for later use  
    25.      */  
    26.     public static synchronized void initial(){   
    27.         if(null==daoMap){   
    28.             daoMap =DAOConfig.load();//根據(jù)配置文件加載DAO實(shí)現(xiàn)配置   
    29.         }   
    30.     }   
    31.   
    32. }  

    代碼

    1. package net.wanjin.lab.persistence.dao;   
    2.   
    3. import java.util.Enumeration;   
    4. import java.util.HashMap;   
    5. import java.util.Properties;   
    6.   
    7. import org.apache.log4j.LogManager;   
    8. import org.apache.log4j.Logger;   
    9.   
    10. /**   
    11.  * DAOConfig 類實(shí)現(xiàn)了配置文件的讀取功能,并根據(jù)配置文件中的內(nèi)容加載制定的接口和實(shí)現(xiàn)類;   
    12.  * @author Administrator   
    13.  */   
    14.   
    15. public class DAOConfig {   
    16.        
    17.     private static Logger logger = LogManager.getLogger(DAOConfig.class);   
    18.        
    19.     private static final String DAO_CONFIG_FILE="dao.xml";   
    20.     private static final String DAO_CONFIG_SECTION="DAO";   
    21.        
    22.     /**   
    23.      * Load the DAO Interface_Implementation into a HashMap   
    24.      * @return   
    25.      */   
    26.        
    27.     public static synchronized HashMap load(){   
    28.         HashMap map = new HashMap();   
    29.            
    30.         JFigLocator jfigLocator = new JFigLocator(DAO_CONFIG_FILE);   
    31.         JFigIF daoConfig = JFig.getInstance(jfigLocator);   
    32.         Properties prop = daoConfig.getSectionAsProperties(DAO_CONFIG_SECTION);   
    33.            
    34.         Enumeration enumSection = prop.keys();   
    35.         while(enumSection.hasMoreElements()){   
    36.             String daoIface =(String)enumSection.nextElement();   
    37.             String daoImpl = prop.getProperty(daoIface);   
    38.             try{   
    39.                 Class iface = ClassToolKit.loadClass(daoIface);   
    40.                 Class impl = ClassToolKit.loadClass(daoImpl);   
    41.                 //將接口作為HashMap索引,實(shí)現(xiàn)類作為值   
    42.                 map.put(iface, impl);   
    43.             }catch(ClassNotFoundException e){   
    44.                 logger.debug("No Class Found"+e);   
    45.             }   
    46.         }//while enumSection   
    47.         return map;   
    48.     }   
    49. }[/code[code]]//dao.xml 文件   
    50. <?xml version="1.0" encoding="UTF-8"?>  
    51. <configuration>  
    52.     <section name="DAO">  
    53.         <entry key="net.wanjin.lab.persistence.dao.iface.CustomerDAO"  
    54.                value="net.wanjin.lab.persistence.dao.impl.CustomerDAOImp_Mysql"/>  
    55.             
    56.          <entry key="net.wanjin.lab.persistence.dao.iface.PromotionDAO"  
    57.                 value="net.wanjin.lab.persistence.dao.impl.PromotionDAOImp_Mysql"/>  
    58.     </section>  
    59. </configuration>  


    DAOConfig
    中使用了JFig讀取XML配置文件(dao.xml),關(guān)于JFig的具體信息請(qǐng)參見(jiàn)http://jfig.sourceforge.net.

    代碼

    1. package net.wanjin.lab.persistence.dao;   
    2.   
    3. public class ClassToolKit {   
    4.        
    5.     public static Class loadClass(String className)   
    6.                 throws ClassNotFoundException{   
    7.            
    8.         Class cls = null;   
    9.         try{   
    10.             //首先嘗試用當(dāng)前ClassLoader加載   
    11.             cls = Thread.currentThread().getContextClassLoader().loadClass(className);   
    12.         }catch(Exception e){   
    13.             e.printStackTrace();   
    14.         }   
    15.         if(cls == null){   
    16.             //如果通過(guò)當(dāng)前ClassLoader加載失敗,使用系統(tǒng)ClassLoader加載   
    17.             cls = Class.forName(className);   
    18.         }   
    19.         return cls;   
    20.     }   
    21. }  

    這樣,通過(guò)接口與實(shí)現(xiàn)相分離,并結(jié)合DAOFactory動(dòng)態(tài)加載實(shí)現(xiàn)類,我們實(shí)現(xiàn)了底層訪問(wèn)實(shí)現(xiàn)的參數(shù)化配置功能。從而為增強(qiáng)產(chǎn)品的部署能力提供了強(qiáng)有力的支持。

    經(jīng)過(guò)Factory模式的改造,我們業(yè)務(wù)層代碼也進(jìn)行了相應(yīng)得修改:

    代碼

    1. package net.wanjin.lab.persistence.dao;   
    2.   
    3. import java.math.BigDecimal;   
    4.   
    5. import net.wanjin.lab.persistence.domain.Customer;   
    6.   
    7. public class Customers {   
    8.        
    9.     public BigDecimal calcAmount(String customerID,BigDecimal amount){   
    10.            
    11.         //根據(jù)客戶ID獲得客戶記錄   
    12.         CustomerDAO customerDAO = (CustomerDAO)DAOFactory.getDAO(CustomerDAO.class);   
    13.         Customer customer = customerDAO.getCustomer(customerID);    
    14.            
    15.         //根據(jù)客戶等級(jí)獲得打折比率   
    16.         PromotionDAO promoDAO = (PromotionDAO)DAOFactory.getDAO(PromotionDAO.class);   
    17.         Promotion promotion = promoDAO.getPromotion(customer.getLevel());   
    18.            
    19.         //累計(jì)客戶總消費(fèi),并更新數(shù)據(jù)庫(kù)   
    20.         customer.setSumAmount(customer.getSumAmount().add(amount));   
    21.         customerDAO.save(customer);   
    22.            
    23.         //返回打折后金額   
    24.         return amount.multiply(promotion.getRatio());   
    25.            
    26.     }   
    27.   
    28. }  


    似乎出現(xiàn)了一些Bad Smell,相對(duì)于改造前的calcAmount方法,這段代碼里混雜了一些數(shù)據(jù)訪問(wèn)層的內(nèi)容,如DAOFactory.getDAO方法的調(diào)用。雖然有眾多的理由解釋引入DAOFactory.getDAO所帶來(lái)的好處,但事實(shí)是,無(wú)論有多好的理由新的設(shè)計(jì)必須避免影響業(yè)務(wù)邏輯代碼的可讀性。沒(méi)有哪家公司能說(shuō)服你在自己的房屋中增加一條穿堂而過(guò)的管道,而理由是為了實(shí)施更好的供暖設(shè)計(jì),我們軟件也一樣。

    Proxy 模式的引入

    為了保持業(yè)務(wù)代碼的簡(jiǎn)潔,將Factory模式帶來(lái)的Bad Smell排除在系統(tǒng)之外。我們引入了結(jié)構(gòu)模式中的Proxy模式。

    Proxy模式的作用事通過(guò)提供一個(gè)中間層(Proxy),將上層調(diào)用接口與下層實(shí)現(xiàn)相銜接,其標(biāo)準(zhǔn)實(shí)現(xiàn)如下。

    代碼

    1. package net.wanjin.lab.persistence.dao;   
    2.   
    3. import java.math.BigDecimal;   
    4.   
    5. public class DecoupleByDesign {   
    6.   
    7.        
    8.     public BigDecimal calcAmount(String customerID,BigDecimal amount){   
    9.            
    10.         //根據(jù)客戶ID獲得客戶記錄   
    11.         Customer customer = CustomerProxy.getCustomer(customerID);   
    12.            
    13.         //根據(jù)客戶等級(jí)獲得打折比率   
    14.         Promotion promotion = PromotionProxy.getPromotion(customer.getLevel());   
    15.            
    16.         //累計(jì)客戶消費(fèi)額,并更新數(shù)據(jù)庫(kù)   
    17.         customer.setSumAmount(customer.getSumAmount().add(amount));   
    18.         CusromerProxy.save(customer);   
    19.            
    20.         //返回打折后金額   
    21.         return amount.multiply(promotion.getRatio());   
    22.     }   
    23. }  


    Bad Smell
    消失了,業(yè)務(wù)層在次變得干凈簡(jiǎn)潔。而CustomerProxy PromotionProxy做了些什么呢?其實(shí)很簡(jiǎn)單:

    代碼

    1. package net.wanjin.lab.persistence.dao;   
    2.   
    3. import java.math.BigDecimal;   
    4.   
    5. import net.wanjin.lab.persistence.domain.Customer;   
    6.   
    7. public class DecoupleByDesign {   
    8.   
    9.        
    10.     public BigDecimal calcAmount(String customerID,BigDecimal amount){   
    11.            
    12.         //根據(jù)客戶ID獲得客戶記錄   
    13.         Customer customer = CustomerProxy.getCustomer(customerID);   
    14.            
    15.         //根據(jù)客戶等級(jí)獲得打折比率   
    16.         Promotion promotion = PromotionProxy.getPromotion(customer.getLevel());   
    17.            
    18.         //累計(jì)客戶消費(fèi)額,并更新數(shù)據(jù)庫(kù)   
    19.         customer.setSumAmount(customer.getSumAmount().add(amount));   
    20.         CustomerProxy.save(customer);   
    21.            
    22.         //返回打折后金額   
    23.         return amount.multiply(promotion.getRatio());   
    24.     }   
    25. }  

    代碼

    1. package net.wanjin.lab.persistence.dao;   
    2.   
    3. public class PromotionProxy {   
    4.     /**  
    5.      * Get Promotion Object by Promotion Level  
    6.      * @param level  
    7.      * @return  
    8.      */  
    9.        
    10.     public static Promotion getPromotion(int level){   
    11.         PromotionDAO promoDAO = (PromotionDAO)DAOFactory.getDAO(PromotionDAO.class);   
    12.         return promoDAO.getPromotion(level);   
    13.            
    14.     }   
    15.   
    16. }  


    至此,通過(guò)FactoryProxy模式的應(yīng)用,我們對(duì)原有的DAO模式進(jìn)行了改造,在不影響業(yè)務(wù)曾清晰性的前提下,提供了底層實(shí)現(xiàn)的參數(shù)配置化實(shí)現(xiàn)。
    最后,讓我們通過(guò)下面這個(gè)Sequence Diagram再整體考察一下改造后的成果。

    18:48  |   永久鏈接  |   瀏覽 (2309)  |   評(píng)論 (2)  |    收藏  |   Hibernate  |  

     

     

    永久鏈接

     

    http://lincoln.javaeye.com/blog/33727

     

    評(píng)論     2

    發(fā)表評(píng)論

    barryzhong     2007-09-07 14:51

    受益匪淺啊!謝謝!

    barryzhong     2007-09-07 18:01

    今日看了兄臺(tái)的http://lincoln.javaeye.com/blog/33727文章,受益匪淺,不過(guò)將您的代碼加入到工程中無(wú)法運(yùn)行,經(jīng)過(guò)調(diào)試發(fā)現(xiàn)在仁兄的DAOFactory.getDAO()方法中,僅僅是將xml文件中定義的影射中的一個(gè)接口和一個(gè)實(shí)現(xiàn)類的兩個(gè)Class對(duì)象存入daoMap中,在daoMap中找到該實(shí)現(xiàn)類的Class對(duì)象以后,直接將該Class對(duì)象返回,而不是該類對(duì)象實(shí)例化以后的對(duì)象返回。故程序運(yùn)行到PromotionDAO promoDAO = (PromotionDAO)DAOFactory.getDAO(PromotionDAO.class); 時(shí)發(fā)生class cast Exception.

    倘若在getDAO方法中加入實(shí)例化”“實(shí)現(xiàn)類Class對(duì)象的代碼以后(見(jiàn)后文修改后源代碼),程序運(yùn)行正常。

    請(qǐng)問(wèn),是不是我哪里用的不正確,還是該程序僅僅是為了顯示設(shè)計(jì)模式的思想,而忽略這個(gè)存在這個(gè)bug呢?

    ##################################################################
    文中貼的
    ##################################################################
    package net.wanjin.lab.persistence.dao;

    import java.util.HashMap;

    public class DAOFactory {

    private static HashMap daoMap = null;

    /**
    * Return a implemetation instance of the specified DAO Interface
    * @return the DAO Implemmenation Class Instance
    */
    public static Object getDAO(Class daoInterface){
    initial();
    Object dao = daoMap.get(daoInterface);
    if(null ==dao){
    throw new DAOException("No Implementation found of DAO interface =>"
    +daoInterface.getName());
    }
    return dao;
    }
    /**
    * Initial the DAOFactory
    * Load DAO Interface and Implementation In daoMap for later use
    */
    public static synchronized void initial(){
    if(null==daoMap){
    daoMap =DAOConfig.load();//
    根據(jù)配置文件加載DAO實(shí)現(xiàn)配置
    }
    }

    }

    ################################################################

    ################################################################################################################################
    ################################################################
    修改后的
    ################################################################################################################################

    public static Object getDAO(Class daoInterface){
    initial();
    //Object dao = daoMap.get(daoInterface);
    Object daoClass= daoMap.get(daoInterface);
    Object dao=null;
    try {
    dao = ((Class)daoClass).newInstance();
    if(null ==dao){
    throw new DAOSystemException("No Implementation found of DAO interface =>"
    +daoInterface.getName());
    }
    } catch (InstantiationException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } catch (IllegalAccessException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    return dao;
    }

    posted on 2007-09-19 08:30 白玉成 閱讀(1941) 評(píng)論(2)  編輯  收藏 所屬分類: JEE

    評(píng)論

    # re: DAO設(shè)計(jì)模式 2008-05-29 13:27 larry
    獲益良多,謝謝;
    個(gè)人感覺(jué)調(diào)用getDao的時(shí)候,每次都要執(zhí)行initial(),重復(fù)對(duì)配置文件讀取,本身就比較浪費(fèi)資源,為什么不用單例模式管理這個(gè)配置文件,這樣運(yùn)行時(shí)只需要對(duì)initial()操作一次就夠了  回復(fù)  更多評(píng)論
      

    # re: DAO設(shè)計(jì)模式 2012-09-19 21:18 李根
    寫的不錯(cuò),值得學(xué)習(xí)  回復(fù)  更多評(píng)論
      


    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 亚洲国产激情一区二区三区| 亚洲AV性色在线观看| 免费无码又爽又刺激网站直播| 国产成人在线观看免费网站| youjizz亚洲| 永久免费在线观看视频| 亚洲国产精品嫩草影院在线观看 | 亚洲精品乱码久久久久久中文字幕| 色网站在线免费观看| 日韩毛片免费在线观看| 亚洲国产区男人本色在线观看| 亚洲免费二区三区| 亚洲美女色在线欧洲美女| 久久综合九色综合97免费下载| 亚洲成AV人片在线观看无| 好湿好大好紧好爽免费视频| 亚洲人成色7777在线观看不卡| 鲁啊鲁在线视频免费播放| 日本免费网站在线观看| 亚洲国产欧洲综合997久久| 成人五级毛片免费播放| 亚洲色偷偷综合亚洲AV伊人蜜桃| 99热在线精品免费全部my| 亚洲五月综合缴情婷婷| 国拍在线精品视频免费观看| 亚洲av无码片在线观看| 国产又黄又爽又猛免费app| 亚洲AV无码久久久久网站蜜桃| 99久久99久久精品免费看蜜桃 | 天天综合亚洲色在线精品| 日本久久久免费高清| 国产精品无码亚洲一区二区三区| 日本牲交大片免费观看| 看全免费的一级毛片| 亚洲av无码专区在线观看素人| 一边摸一边桶一边脱免费视频| 亚洲精品亚洲人成在线观看| 免费观看91视频| 亚洲精品美女久久久久| 日韩av无码成人无码免费| 亚洲国产精品无码第一区二区三区|