Data Accessor即將數(shù)據(jù)訪問的實現(xiàn)機制加以封裝,與數(shù)據(jù)的使用代碼相分離,從外部來看,Data Accessor提供了黒盒式的數(shù)據(jù)存取接口。Domain Object則提供了對所有面向領(lǐng)域內(nèi)對象的封裝。
DAO模式實現(xiàn)了業(yè)務(wù)邏輯與數(shù)據(jù)邏輯的分離。對于專項開發(fā)而言,這樣的分離設(shè)計差不多已經(jīng)可以實現(xiàn)開發(fā)過程中業(yè)務(wù)層面和數(shù)據(jù)層面的相對獨立,并且在實現(xiàn)復(fù)雜性與結(jié)構(gòu)清晰性上達(dá)到較好的平衡。
對于一個產(chǎn)品化的業(yè)務(wù)系統(tǒng)而言,軟件產(chǎn)品往往需要在不同客戶環(huán)境下即時部署。由于java良好的跨平臺支持,我們在操作系統(tǒng)之間大可以輕易遷移,但在另外一個層面,數(shù)據(jù)庫層,卻仍然面臨著平臺遷移的窘境。針對不同的數(shù)據(jù)庫,我們可以實現(xiàn)針對不同類型數(shù)據(jù)庫的Data Accessor,并根據(jù)客戶實際部署環(huán)境,通過類文件的靜態(tài)替換來實現(xiàn)。這樣將大大增加部署和維護(hù)工作的難度和復(fù)雜性。我們應(yīng)該將此類因素帶來的變動屏蔽在系統(tǒng)之外。
Factory模式在這里起到連接接口和實現(xiàn)的橋梁作用,通過Factory模式,我們可以根據(jù)具體需要加載相應(yīng)的實現(xiàn),并將此實現(xiàn)作為所對應(yīng)接口的一個實例提供給業(yè)務(wù)層使用:
為了提高性能,避免每次調(diào)用都讀取配置文件所引起的大量磁盤操作,采用了HashMap作為DAO緩存實現(xiàn)示例:
public class DAOFactory{
private static HashMap daoMap = null;
public static Object getDAO(Class daoInterface){
initial();
Object dao = daoMap.get(daoInterface);
if(null == dao){
throw new DAOException();
}
return dao;
}
public static synchronized void initial(){
if(null==daoMap){
daoMap = DAOConfig.load();//根據(jù)配置文件加載DAO實現(xiàn)配置
}
}
}
public class DAOConfig{
private static Logger logger = LogManager.getLogger(DAOConfig.class);
private static final String DAO_CONFIG_FILE = "dao.xml";
private static final String DAO_CONFIG_SECTION = "DAO";
public static synchronized HashMap load(){
HashMap map = new HashMap();
JFigLocator jfigLocator = new JFigLocator(DAO_CONFIG_FILE);
Properties prop = daoConfig.getSectionAsProperties(DAO_CONFIG_SECTION);
Enumeration enumSection = prop.keys();
while(enumSection.hasMoreElements()){
String daoIface = (String)enumSection.nextElement();
String daoImpl = prop.getProperty(daoIface);
try{
Class iface = ClassToolkit.loadClass(daoIface);
Class impl = ClassToolkit.loadClass(daoImpl);
//將接口作為HashMap索引,實現(xiàn)類作為值
map.put(iface,impl);
}catch(ClassNotFoundException e){
logger.debug("No Class Found=>"+e);
}
}
return map;
}
}
private static HashMap daoMap = null;
public static Object getDAO(Class daoInterface){
initial();
Object dao = daoMap.get(daoInterface);
if(null == dao){
throw new DAOException();
}
return dao;
}
public static synchronized void initial(){
if(null==daoMap){
daoMap = DAOConfig.load();//根據(jù)配置文件加載DAO實現(xiàn)配置
}
}
}
public class DAOConfig{
private static Logger logger = LogManager.getLogger(DAOConfig.class);
private static final String DAO_CONFIG_FILE = "dao.xml";
private static final String DAO_CONFIG_SECTION = "DAO";
public static synchronized HashMap load(){
HashMap map = new HashMap();
JFigLocator jfigLocator = new JFigLocator(DAO_CONFIG_FILE);
Properties prop = daoConfig.getSectionAsProperties(DAO_CONFIG_SECTION);
Enumeration enumSection = prop.keys();
while(enumSection.hasMoreElements()){
String daoIface = (String)enumSection.nextElement();
String daoImpl = prop.getProperty(daoIface);
try{
Class iface = ClassToolkit.loadClass(daoIface);
Class impl = ClassToolkit.loadClass(daoImpl);
//將接口作為HashMap索引,實現(xiàn)類作為值
map.put(iface,impl);
}catch(ClassNotFoundException e){
logger.debug("No Class Found=>"+e);
}
}
return map;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<section name="DAO">
<entry key="net.xiaxin.lab.persistence.dao.iface.CustomerDAO" value="net.xiaxin.lab.persistence.dao.impl.CustomerDAOImp_Mysql"/>
<entry key="net.xiaxin.lab.persistence.dao.iface.PromotionDAO" value="net.xiaxin.lab.persistence.dao.impl.PromotionDAOImp_Mysql"
</section>
</configuration>
<configuration>
<section name="DAO">
<entry key="net.xiaxin.lab.persistence.dao.iface.CustomerDAO" value="net.xiaxin.lab.persistence.dao.impl.CustomerDAOImp_Mysql"/>
<entry key="net.xiaxin.lab.persistence.dao.iface.PromotionDAO" value="net.xiaxin.lab.persistence.dao.impl.PromotionDAOImp_Mysql"
</section>
</configuration>
public class ClassToolkit{
public static Class loadClass(String className){
Class cls = null;
try{
cls = Thread.currentThread().getContextClassLoader().loadClass(className);
}catch(Exception e){
e.printStackTrace();
}
if(cls == null){
cls = Class.forName(className);
}
return cls;
}
}
業(yè)務(wù)層通過接口調(diào)用底層實現(xiàn),具體的DAO實現(xiàn)類不會出現(xiàn)在我們的業(yè)務(wù)代碼中。而具體實現(xiàn)類在配置文件中加以配置,之后DAOFactory.getDAO方法通過讀取配置文件獲得當(dāng)前我們期望使用的實現(xiàn)類的類名,在通過Java Class動態(tài)加載機制加載后返回。public static Class loadClass(String className){
Class cls = null;
try{
cls = Thread.currentThread().getContextClassLoader().loadClass(className);
}catch(Exception e){
e.printStackTrace();
}
if(cls == null){
cls = Class.forName(className);
}
return cls;
}
}
通過接口與實現(xiàn)的分離,并結(jié)合DAOFactory動態(tài)加載實現(xiàn)類,我們實現(xiàn)了底層訪問實現(xiàn)的參數(shù)化配置功能。
無論有多好的理由,新的設(shè)計必須避免影響業(yè)務(wù)邏輯代碼的可讀性。沒有哪個物業(yè)公司能說服你在自己的房屋中增加一條穿堂而過的管道,而理由是為了實施更好的供暖設(shè)計,我們的軟件也一樣。