葛萌+歐陽宏基



摘要:為了提高傳統JDBC框架的復用性,分析了工廠設計模式的三種具體形式:簡單工廠、工廠方法和抽象工廠。闡述了三者之間的優缺點,從進化和退化兩個方面分析了三者之間的轉換關系。將工廠設計模式與JDBC相結合,設計了一個數據持久層模型,給出了該模型的設計思想與若干核心代碼。通過相關分析與測試表明:將工廠設計模式應用到持久層的設計中能夠減少代碼的冗余度、提高復用性和擴展性。
關鍵詞:簡單工廠;工廠方法;抽象工廠;JDBC
Abstract:In order to improve the reusability of the traditional JDBC framework,this paper analyzes three concrete forms of the factory design pattern,which are simple factory, factory method and abstract factory. expounds the advantages and disadvantages of the three,the transformation relationship between the three is analyzed from two aspects of evolution and degradation.designs a data persistence layer model with combining factory design pattern and JDBC,gives the design idea and some core codes of the model.Through the correlation analysis and test, it is indicated that the factory design pattern can be applied to the design of persistent layer, which can reduce the redundancy of the code, improve the reusability and expansibility.
Key words:simple factory;factory method;abstract factory;JDBC
0. 引言
工廠設計模式屬于創建型模式中使用最為頻繁的一種,它的主要思想是將對象的創建封裝到一種稱為“工廠”的類中,從調用方角度來看,需要“產品”時,不需要親自new出來,通過調用工廠對象的方法就可以得到對象。因此,合理的使用工廠設計模式能夠將對象的創建和使用相分離,從而減少類之間的耦合度,提高復用性。本文首先介紹了工廠設計模式中的三種具體形式:簡單工廠模式、工廠方法模式和抽象工廠模式,詳細描述了每種形式的組成以及各角色在模式中承擔的功能,從進化和退化兩個方面分析了它們三者之間的轉換關系。最后,以JDBC作為Java EE應用持久層解決方案的背景下,將工廠模式的三種具體形式應用到持久層的設計過程中,提出了一個數據持久層模型,對該模型的設計過程進行了分析,通過相關測試證明了它的有效性。
1.工廠設計模式分析
1.1簡單工廠模式
簡單工廠模式包含三個角色[1]:抽象產品、具體產品和工廠。抽象產品角色是工廠所創建的所有對象的共同父類,描述了所有產品的公共接口。具體產品是該模式的創建目標,所有創建的對象都充當這個角色的某個具體類的實例。工廠角色對外提供一個靜態的工廠方法用來創建所有的具體產品,通過參數動態決定所創建產品的類型,方法內部針對參數形成判斷邏輯。當產品類型發生變化時會導致判斷邏輯的變化,所以簡單工廠模式不滿足“開閉原則”。
1.2 工廠方法模式
工廠方法模式[2]一共包含4個角色:抽象產品、具體產品、抽象工廠和具體工廠。抽象產品是產品對象的共同父類或接口。具體產品由某種類型的具體工廠所創建。抽象工廠用來聲明工廠方法并返回產品。具體工廠用來創建一個具體的產品類對象,其中包含了與應用程序密切相關的邏輯。具體工廠與具體產品一一對應。相對于簡單工廠,工廠方法在工廠這一側進行了抽象,將具體產品的創建延遲到工廠子類中進行。如果系統中引入了新的產品,那么只需要創建新的產品和新的工廠即可,系統中原來的產品和工廠類不需要修改,所以工廠方法很好的滿足了“開閉原則”。
1.3 抽象工廠模式
工廠方法模式中只能生產一種類型的產品,當產品種類多于一種時,工廠方法模式就不滿足“開閉原則”,此時只能使用抽象工廠模式。理解抽象工廠模式首先要明確兩個概念:產品等級結構和產品族[3-4]。前者表示產品一側的泛化關系,后者表示同一個工廠所生產的、位于不同等級結構中的一組不同種類的產品。抽象工廠定義一組生成抽象產品的方法,每個方法對應一個產品等級結構。具體工廠生產一組具體產品形成一個產品族,每一個產品都位于某個產品等級結構中。抽象產品用于定義產品的抽象業務。具體工廠生產具體產品對象。
1.4 三者的優缺點及轉換
工廠設計模式的核心在于將對象的創建和對象本身業務處理相分離,降低系統的耦合度,使兩者的修改變得簡單。簡單工廠模式將所有產品的創建過程封裝到工廠類的靜態方法中,通過傳入正確的參數即可獲得所需對象。但是工廠類的任務相對繁重,尤其是在產品類過多的情況下,工廠類會有繁瑣的判斷邏輯;而且增加新產品的同時需要修改判斷邏輯。
工廠方法模式在工廠一側引入了泛化關系,它的實現依賴于工廠角色與產品角色的多態性。把原來集中創建產品對象的方式改為分散式創建,每一個具體工廠創建每一種具體產品。如果有新產品的加入,只需增加具體產品類和對應的具體工廠類即可,原來的代碼無需更改。
但是工廠方法模式只能創建類型單一的產品,當產品類型增多時,系統中類的個數成對增加,提高了系統的復雜度和編譯開銷。
抽象工廠模式解決了工廠方法模式所創建產品種類單一的問題,它提供一個創建一系列相關或相互依賴對象的接口,而無須指定它們具體的類[5-6]。產品等級結構決定了產品種類的個數,產品族決定了具體工廠的個數。從產品族的角度而言,增加新的具體工廠時無須修改原有代碼,滿足開閉原則;從產品等級結構的角度而言,增加新的產品類型時需要修改其中的抽象工廠角色代碼,同時還要修改各個具體的工廠類。所以,抽象工廠模式對于開閉原則具有半傾斜性[7]。它們三者的優缺點及轉換關系見表1。
2. 工廠設計模式在Java EE持久層的應用
Java EE的持久層用來封裝數據持久化邏輯并為業務層提供訪問數據源的接口,提供諸如數據源連接、查詢、存儲過程、數據格式修正和錯誤處理等功能[8]。其目的是為了解耦合業務處理和數據存取,為企業應用形成一個高效、穩定的數據訪問環境。由于關系型數據庫在數據存儲方面仍然占據主導地位,所以圍繞SQL產生出了很多數據持久層解決方案:包括JDBC、全自動化的ORM(例如Hibernate)、半自動化的ORM(例如MyBatis)以及JDO等。其中JDBC是最原生態的SQL解決方案,具有執行效率最高、易于掌握等特點,但也有復用率低、不易擴展等缺點。本節主要討論如何將工廠設計模式應用到JDBC中并設計一個數據持久層模型。
2.1 抽象工廠模式的應用
結合應用背景,分析出產品等級結構和產品族是應用抽象工廠模式的關鍵。為了隔離業務邏輯與持久化邏輯,Java EE規范推薦采用DAO模式。通常的做法是在DAO接口中定義相關的持久化方法,DAO實現類中應用某種具體的持久化技術來完成持久化方法[9]。由于一個系統中存在多個不同的實體對象,它們所對應的DAO可以看作產品等級結構;由于不同數據庫具有SQL“方言”,在執行相同的持久化邏輯時SQL語句會有所差別,因此在某個特定數據庫下的各種DAO的實現類可以看作一個產品族。業務層要對實體對象進行持久化操作必須通過工廠獲取對應的DAO對象。以MySQL數據庫為例,部分角色的代碼如下:
public interface DAOFactory
{ //定義系統中所有實體對象的DAO
UserDAO createUserDAO();
DepartmentDAO createDepartmentDAO();
……………………
}
每個具體的數據庫對應一個具體工廠,代碼如下:
public class MySQLDAOFactory implements DAOFactory
{
public UserDAO createUserDAO() {
return new MySQLUserDAOImp();
}
public DepartmentDAO createDepartmentDAO() {
return new MySQLDepartmentDAOImp();
}
……………………………………………
}
2.2 工廠方法模式的應用
JDBC的操作一般包括4個步驟[10]:(1)加載驅動;(2)獲取Connection;(3)創建相關Statement對象并執行SQL語句;(4)釋放資源。為了減少代碼的冗余度,定義抽象類JDBCUtil用來執行步驟(1)、(2)和(4),將該類對象看作工廠模式中的唯一抽象產品,定義JDBCUtilFactory當作工廠方法模式中的抽象工廠,每種具體數據庫對應一個JDBCUtil和JDBCUtilFactory的實現類,分別當作具體產品和具體工廠。根據上述分析,具體產品角色代碼如下:
public class MySQLJDBCUtil extends JDBCUtil
{ static{
Class.forName("com.mysql.jdbc.Driver");
……………. }
public Connection getConnection() throws SQLException{
return DriverManager.getConnection("jdbc:mysql://localhost:3306/dbName",”root”,”root”);
}
具體工廠角色代碼如下:
public class MySQLJDBCUtilFactory implements JDBCUtilFactory
{
public JDBCUtil createJDBCUtil() {
return new MySQLJDBCUtil();
}
}
2.3 簡單工廠模式的應用
通過對2.3節的代碼分析可以看出:不同數據庫所對應的具體產品和具體工廠的代碼結構相同,不同之處在于JDBC驅動的名稱和創建Connection對象時傳入的URL參數。為了進一步減少冗余度,將這些參數定義到配置文件中,在程序運行時通過讀取配置文件動態傳入。這樣的話,對于工廠方法模式而言,產品和工廠就不存在抽象層,從而退化成為簡單工廠模式。
首先,定義讀取配置文件的類-JDBCConfigReader,其中關聯一個Properties對象,該對象用于讀取properties類型的配置文件。properties類型的配置文件具有易于理解、讀寫簡單等特點,以
然后,定義簡單工廠模式中的產品和工廠類。產品類的代碼如下:
public class JDBCUtil
{ static{
Class.forName(JDBCConfigReader.getInstance().getProperties().getProperty("DriverClass");
}
public Connection getConnection() throws SQLException{
String url=JDBCConfigReader.getInstance().getProperties().getProperty("DBURL");
String userName=JDBCConfigReader.getInstance().getProperties().getProperty("DBUserName");
String password=JDBCConfigReader.getInstance().getProperties().getProperty("DBPassword");
return DriverManager.getConnection(url,userName,password);
} }
工廠類的代碼如下:
public class JDBCUtilFactory
{
public static JDBCUtil createJDBCUtil(){
return new JDBCUtil(); }
}
與2.3節的代碼對比可以看出:將不同數據庫的相關JDBC參數存儲到配置文件后,工廠方法模式退化成了簡單工廠模式,產品和工廠兩個角色都變成了一個對象,不但減少了產品類和具體工廠類的個數,而且工廠類在創建產品對象時也避免了邏輯判斷。
2.4 業務層對持久層的調用
假定當前系統中的一個實體對象是User,它對應的DAO實現類的代碼如下:
public class MySQLUserDAOImp implements UserDAO
{
//通過JDBCUtil工廠得到JDBCUtil產品
private JDBCUtil jdbcUtil=new JDBCUtilFactory().createJDBCUtil();
//相關實體類的持久化方法
public boolean addUser(User user)
{
Connection con=jdbcUtil.getConnection();
………………………
}
}
將當前實際使用的數據庫所對應的DAO工廠類信息寫到配置文件中,將抽象工廠模式中的具體工廠當作簡單工廠模式中的具體產品,通過反射機制創建出具體的DAO工廠,如下代碼所示:
public class DAOFactory
{
public static DAOFactory getDAOFactory()
{ DAOFactory factory=null;
String DAOFactoryName=JDBCConfigReader.getInstance().getProperties().getProperty("DAOFactory"); factory=(DAOFactory)Class.forName(DAOFactoryName).newInstance(); }
return factory;
} }
當業務層要獲取相關實體的DAO對象時,執行下面代碼:
DAOFactory factory=DAOFactoryConfig.getDAOFactory();
UserDAO userDAO=factory.getUserDAO();
通過上述分析可以看出:業務層對于持久層方法的調用,首先通過簡單工廠讀取配置文件得到抽象工廠模式的具體DAO工廠,然后將具體DAO工廠生產的DAO產品賦值給抽象DAO,通過抽象DAO調用相關實體的持久化方法。這時與業務層進行通信的只是抽象DAO工廠和抽象DAO產品。業務層不需要知道當前DAO對象由哪個具體的工廠創建。因此,不論使用哪種數據庫,對業務層的調用來說沒有任何影響,滿足持久層支持多數據庫的要求。綜合上述,本文設計的持久層模型如圖1所示。
2.5 工廠設計模式應用評價
將本文設計的數據持久層模型與傳統JDBC進行比較,觀測點為執行效率與復用率。其中不包括DAO,因為DAO的代碼與具體業務相關,測試工具為JUnit和JDK的Executor并發框架。在單機環境下采用單線程和多線程(并發量為50)兩種形式,執行時間為多次執行的平均值,測試結果見表2和表3。可以看出工廠設計模式的應用提高了代碼的復用率,尤其是簡單工廠模式+反射讀取配置文件來創建對象的方式,完全可以復用。在執行效率方面,本文設計的持久層模型與傳統JDBC的執行開銷差別很小。在多線程情況下,本文模型效率略有提高。這表明工廠設計模式在提高復用率的情況下,雖然增了的類與對象的調用開銷,但對性能的影響可以忽略,因此本模型是有效、可靠的。
3. 結論
本文對工廠設計模式進行了研究,分析了他們的優缺點和轉換關系。將工廠設計模式與JDBC相結合,提出了一種數據持久化模型。通過實際測試表明工廠設計模式能夠很好的將對象的創建和使用相分離,向調用方屏蔽對象的創建過程,在不增加過多額外開銷的情況下,提高了代碼的復用率、擴展性和維護性。為開發人員在設計過程中合理使用工廠設計模式提供了一定的參考。
參考文獻
[1]薛桂香,任女爾,閆世峰,林濤. 基于簡單工廠模式的SSH+ExtJs架構泛型化研究[J].河北工業大學學報,2015,44(3):65-69.
[2]華銓平,龐倩超,謝穎.抽象工廠設計模式在3 層結構開發中的應用[J].大慶石油學院學報,2009,33(3):112-115.
[3]郭永平,劉淑娟.工廠方法模式在軟件開發中的應用—以監控數據接收服務程序為例[J].寶雞文理學院學報(自然科學版),2015,35(4):58-62.
[4]歐建斌.工廠設計的模式研究[J].微型電腦應用,2010,26(12):15-17.
[5]歐陽宏基,葛萌,陳偉.一種改進的建造者設計模式[J].咸陽師范學院學報,2014,29(6):43-46.
[6]程裕強.抽象工廠模式探討[J].玉林師范學院學報,2014,35(2):82-86.
[7]劉偉.設計模式[M].北京:清華大學出版社,2011:92-103.
[8]尚鮮連.設計模式在數據持久層設計中的應用[J].重慶科技學院學報(自然科學版),2008,10(6):18-111.
[9]周寧,苗放,周麗.在DAO 模式中實現數據庫間差異消除及數據庫操作的移植[J].計算機工程與科學,2006,28(10):111-113.
[10]歐陽宏基,葛萌,趙薔.基于JDBC與設計模式的數據庫連接池實現方法[J].計算機技術與發展,2011,21(1):84-87.