解耦合的设计目标:
1. 应用层解耦合--应用逻辑与数据逻辑相分离。
2. 资源层解耦合--逻辑结构与物理结构相分离。
DAO模式:即Data Accessor模式和Active Domain Object模式。
Data Accessor模式:实现数据访问和业务逻辑的分离。
Active Domain Object:实现了业务数据的对象化封装。
Domain Object:简单来讲就是对领域内涉及的各个数据对象,反映到代码,就是一个拥有相关属性的getter,setter方法的java Bean。
DAO模式通过对业务逻辑层提供数据抽象层接口,实现了以下目标:
1. 数据存储逻辑的分离:通过对数据访问逻辑进行抽象,为上层结构提供抽象化的数据访问接口。
2. 数据访问底层实现的分离:数据访问划分为抽象层和实现层,从而分离了数据使用和数据访问的底层实现细节。
3. 资源管理和调用的分离。
4. 数据抽象:DAO模式通过对底层数据的封装,为业务层提供了一个面向对象的接口,使得业务逻辑开发人员可以面向业务中的实体进行编程。
DAO = Data+Accessor+Domain Object
DAO模式的进一步改良
Factory模式的引入:
由于需要针对不同的数据库访问机制分别提供各个版本的Data Accessor实现,自然我们会想到通过java interface定义一个调用接口,然后针对这个调用接口实现不同数据库的Data Accessor。通过接口作为调用界面和实现规范,可以避免对具体实现的依赖。
Public interface CustomerDAO{
Public Customer getCustomer(String custID);
Puboic void save (Customer customer);
}
作为最常见的创建模式,Factory模式在这里起到连接接口和实现的桥梁作用,通过Factory模式,我们可以根据具体的需要加载相应的实现,并将此实现作为所对应接口的一个实例提供给业务层使用。
CustomerDAO custDAO =(CustomerDAO)DAOFactory.getDAO(CustomerDAO.class);
Customer customer = custDAO.getCustomer(customerID);
业务逻辑层通过接口调用底层实现,具体的DAO实现类不会出现在我们的业务代码中。而具体实现类在配置文件中加以配置,之后DAOFactory.getDAO方法通过读取配置文件获得当前我们期望使用的实现类的类名,再通过java Class动态加载机制加载返回。
从而我们的代码不依赖于某个特定的实现类,只需要在部署的时候在配置文件中指定当前采用的实现类即可。
public class DAOFactory {
private static HashMap daoMap = null;
//根据指定的Class来获取DAO实例
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;
}
//初始化DAOFactory,加载DAO interface和
//implementation到daoMap中
public static synchronized void initial(){
if(null==daoMap){
daoMap =DAOConfig.load();//根据配置文件加载DAO实现配置
}
}
}
//DAOConfig类实现了配置文件的读取功能,并根据配文件中的内容加载指定的接口和实现
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);
JFigIF daoConfig = JFig.getInstance(jfigLocator);
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索引,实现类作为值
map.put(iface, impl);
}catch(ClassNotFoundException e){
logger.debug("No Class Found"+e);
}
}//while enumSection
return map;
}
}
ClassToolKit.loadClass方法实现了类文件的动态加载:
public class ClassToolKit {
public static Class loadClass(String className)
throws ClassNotFoundException{
Class cls = null;
try{
//首先尝试用当前ClassLoader加载
cls = Thread.currentThread().getContextClassLoader().loadClass(className);
}catch(Exception e){
e.printStackTrace();
}
if(cls == null){
//如果通过当前ClassLoader加载失败,使用系统ClassLoader加载
cls = Class.forName(className);
}
return cls;
}
}
这样,通过接口与实现的分离,并结合DAOFactory动态加载实现类,我们就实现了底层访问实现的参数化配置功能。从而为增强产品的部署能力提供了强有力的支持。
经过Factory模式的改造,业务层代码进行相应的修改:
public class Customers {
public BigDecimal calcAmount(String customerID,BigDecimal amount){
//根据客户ID获得客户记录
CustomerDAO customerDAO = (CustomerDAO)DAOFactory.getDAO(CustomerDAO.class);
Customer customer = customerDAO.getCustomer(customerID);
//根据客户等级获得打折比率
PromotionDAO promoDAO = (PromotionDAO)DAOFactory.getDAO(PromotionDAO.class);
Promotion promotion = promoDAO.getPromotion(customer.getLevel());
//累计客户总消费,并更新数据库
customer.setSumAmount(customer.getSumAmount().add(amount));
customerDAO.save(customer);
//返回打折后金额
return amount.multiply(promotion.getRatio());
}
}
这段代码中混杂了数据访问层的内容,如DAOFactory.getDAO方法的调用。
1. 应用层解耦合--应用逻辑与数据逻辑相分离。
2. 资源层解耦合--逻辑结构与物理结构相分离。
DAO模式:即Data Accessor模式和Active Domain Object模式。
Data Accessor模式:实现数据访问和业务逻辑的分离。
Active Domain Object:实现了业务数据的对象化封装。
Domain Object:简单来讲就是对领域内涉及的各个数据对象,反映到代码,就是一个拥有相关属性的getter,setter方法的java Bean。
DAO模式通过对业务逻辑层提供数据抽象层接口,实现了以下目标:
1. 数据存储逻辑的分离:通过对数据访问逻辑进行抽象,为上层结构提供抽象化的数据访问接口。
2. 数据访问底层实现的分离:数据访问划分为抽象层和实现层,从而分离了数据使用和数据访问的底层实现细节。
3. 资源管理和调用的分离。
4. 数据抽象:DAO模式通过对底层数据的封装,为业务层提供了一个面向对象的接口,使得业务逻辑开发人员可以面向业务中的实体进行编程。
DAO = Data+Accessor+Domain Object
DAO模式的进一步改良
Factory模式的引入:
由于需要针对不同的数据库访问机制分别提供各个版本的Data Accessor实现,自然我们会想到通过java interface定义一个调用接口,然后针对这个调用接口实现不同数据库的Data Accessor。通过接口作为调用界面和实现规范,可以避免对具体实现的依赖。
Public interface CustomerDAO{
Public Customer getCustomer(String custID);
Puboic void save (Customer customer);
}
作为最常见的创建模式,Factory模式在这里起到连接接口和实现的桥梁作用,通过Factory模式,我们可以根据具体的需要加载相应的实现,并将此实现作为所对应接口的一个实例提供给业务层使用。
CustomerDAO custDAO =(CustomerDAO)DAOFactory.getDAO(CustomerDAO.class);
Customer customer = custDAO.getCustomer(customerID);
业务逻辑层通过接口调用底层实现,具体的DAO实现类不会出现在我们的业务代码中。而具体实现类在配置文件中加以配置,之后DAOFactory.getDAO方法通过读取配置文件获得当前我们期望使用的实现类的类名,再通过java Class动态加载机制加载返回。
从而我们的代码不依赖于某个特定的实现类,只需要在部署的时候在配置文件中指定当前采用的实现类即可。
public class DAOFactory {
private static HashMap daoMap = null;
//根据指定的Class来获取DAO实例
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;
}
//初始化DAOFactory,加载DAO interface和
//implementation到daoMap中
public static synchronized void initial(){
if(null==daoMap){
daoMap =DAOConfig.load();//根据配置文件加载DAO实现配置
}
}
}
//DAOConfig类实现了配置文件的读取功能,并根据配文件中的内容加载指定的接口和实现
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);
JFigIF daoConfig = JFig.getInstance(jfigLocator);
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索引,实现类作为值
map.put(iface, impl);
}catch(ClassNotFoundException e){
logger.debug("No Class Found"+e);
}
}//while enumSection
return map;
}
}
ClassToolKit.loadClass方法实现了类文件的动态加载:
public class ClassToolKit {
public static Class loadClass(String className)
throws ClassNotFoundException{
Class cls = null;
try{
//首先尝试用当前ClassLoader加载
cls = Thread.currentThread().getContextClassLoader().loadClass(className);
}catch(Exception e){
e.printStackTrace();
}
if(cls == null){
//如果通过当前ClassLoader加载失败,使用系统ClassLoader加载
cls = Class.forName(className);
}
return cls;
}
}
这样,通过接口与实现的分离,并结合DAOFactory动态加载实现类,我们就实现了底层访问实现的参数化配置功能。从而为增强产品的部署能力提供了强有力的支持。
经过Factory模式的改造,业务层代码进行相应的修改:
public class Customers {
public BigDecimal calcAmount(String customerID,BigDecimal amount){
//根据客户ID获得客户记录
CustomerDAO customerDAO = (CustomerDAO)DAOFactory.getDAO(CustomerDAO.class);
Customer customer = customerDAO.getCustomer(customerID);
//根据客户等级获得打折比率
PromotionDAO promoDAO = (PromotionDAO)DAOFactory.getDAO(PromotionDAO.class);
Promotion promotion = promoDAO.getPromotion(customer.getLevel());
//累计客户总消费,并更新数据库
customer.setSumAmount(customer.getSumAmount().add(amount));
customerDAO.save(customer);
//返回打折后金额
return amount.multiply(promotion.getRatio());
}
}
这段代码中混杂了数据访问层的内容,如DAOFactory.getDAO方法的调用。