DAO层与Service业务逻辑层的解耦实现之Factory工厂模式

来源:互联网 发布:域名备案批量查询 编辑:程序博客网 时间:2024/05/02 23:26

在开始阐述DAO与Service层如何实现解耦之前,我先提一个站在学习者角度的问题,为什么在MVC的三层开发架构中会非常推崇接口编程,那么根据已有的解释,接口编程的好处是:可以帮助层与层之间的解耦,让每个部分独立出来,互不影响,更加的利于团队开发合作和提高复用性与扩展性。那么,在数据访问层(DAO)和业务层(SERVICE)之间的解耦是如何做到的呢?下面请先看一张框架简图:




不难发现的是,UserService是通过接口UserDao来间接操作UserDaoImpl访问Domain对象User中的数据的。有些编程经验的童鞋都应该明白,要想拿到具体的实现,那么这种操作(先不管红色部分的UserDaoFactory工厂类),在访问User时就必定要在UserService中有这样的语句:UserDao  userDao = new UserDaoImpl();  那么,问题来了, UserDaoImpl实现类在这里出现并将导致在后期拓展时(比如UserDao的具体实现改换为Hibernate或者其它技术)就无法真正的做到解耦,因为你必须在你的源代码中指定具体的实现类。如果这里,你还没有明白,那么请看以下代码:


首先,我们来构造一个Domain域对象User,此User在此其实并没有多大意义只是为了展示Domain对象的结构:

package com.jasber.jdbc.test1;import java.util.Date;/** * This is Bean that corresponding int Mysql database table tb_test *  * @author Jasber-Yon * */public class User {private Integer id;private String name;private Date birthday;private Double salary;public User() {}public User(Integer id, String name, Date birthday, Double salary) {super();this.id = id;this.name = name;this.birthday = birthday;this.salary = salary;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Date getBirthday() {return birthday;}public void setBirthday(Date birthday) {this.birthday = birthday;}public Double getSalary() {return salary;}public void setSalary(Double salary) {this.salary = salary;}}
然后,定义一个数据访问接口UserDao:

package com.jasber.jdbc.test1;/** *  * @author Jasber-Yon * */public interface UserDao {public boolean getUserInfoById(User user);}
现在,就来实现这个UserDao数据访问接口,实现类UserDaoImpl(在这里就不做真正的实现了,只为说明问题):

package com.jasber.jdbc.test1;public class UserDaoImpl implements UserDao {@Overridepublic boolean getUserInfoById(User user) {//.....代码省略System.out.println("哈哈,我被调用了~");return false;}}

那么,现在,我们可以写一个UserService来模拟一下调用了:

package com.jasber.jdbc.test1;/** *  * @author Jasber-Yon * */public class UserService {public static void main(String[] args) {User user = new User();UserDao userDao = new UserDaoImpl();userDao.getUserInfoById(user);}}
现在,就会发现,UserDaoImpl 在UserService中被“点名”了,这样干,显然是不合理的,没有做到真的解耦,我们的目的是,要让UserService仅仅只需要通过数据访问接口UserDao就做到需要的操作。那么,应该如何来做呢?

一、提供一个工厂类,让他去对具体的实现类进行实例化,并返回UserDao的实现类的实例的引用。

二、这个数据访问接口的实现类在哪儿呢?通过配置文件(XML/Properties,我偷个懒就使用properties文件了)来配置,让工厂类去读取(或者专用的操作类去读取)。

那么,请看这个工厂类UserDaoFactory的实现:

package com.jasber.jdbc.test1;import java.io.IOException;import java.io.InputStream;import java.util.Properties;public class UserDaoFactory {private static UserDao userDao = null;// 注意此句必须放在实例化工厂类的语句之前,否者会在运行时被置为nullprivate static UserDaoFactory userDaoFactory = new UserDaoFactory();private UserDaoFactory() {Properties properties = new Properties();InputStream inputStream = UserDaoFactory.class.getClassLoader().getResourceAsStream("daoConfig.properties");try {properties.load(inputStream);String userDaoImpl = properties.getProperty("userDaoImpl");userDao = (UserDao) Class.forName(userDaoImpl).newInstance();} catch (Throwable e) {throw new ExceptionInInitializerError(e);// 此问题非常严重}finally{try {inputStream.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}public static UserDaoFactory getInstance() {return userDaoFactory;}public UserDao getUserDao() {return userDao;}}

通过类加载器,获得到实现类的位置,然后通过反射技术将其实例化。在此工厂(单例实现,懒汉式直接加载,也可延迟加载)类中将提供返回工厂实例的方法以及数据访问接口UserDao的实现类的实例。

现在再来看看,我们是怎样在UserService中来操作数据访问对象(DAO)的:

package com.jasber.jdbc.test1;/** *  * @author Jasber-Yon * */public class UserService {public static void main(String[] args) {/*User user = new User();UserDao userDao = new UserDaoImpl();userDao.getUserInfoById(user);*/User user = new User();UserDao userDao = UserDaoFactory.getInstance().getUserDao();userDao.getUserInfoById(user);}}
与注释部分对比,不难发现,已经没有了UserDaoImpl的踪影。完全是在操作接口了。是不是就已经解耦了呢?


想必,到这儿,已经明白是怎么回事了。下面在说一说,我在程序中使用到的 daoConfig.properties 文件(我是偷懒了啊,当然这样也是为了简化代码保证阐述问题的单纯性,就没有使用XML来说事),因为我们使用到了类加载器,该配置文件必须要放置在ClassPath路径下(当然位置是随意的,ClassPath路径就是指运行程序时,会被JVM读取加载的目录,也就是bin目录下,Java工程里,使用eclipse的就直接放在src目录下即可,编译时会被自动带过去的),不然就会无法找到该文件而出现空指针异常。

以上就是本篇博客。~博主的肚子好饿~抓狂






0 0
原创粉丝点击