引言
之前总是在使用别人搭建好的架构来实现具体的业务,没有真正的思考架构设计的原因,基于什么样的原因要采用这样的架构,为什么在底层使用了那么多的接口、抽象类等等,现在在搭建OA系统的DAO时候意识到了这方面的原因,下面就来总结一下。
首先来看一下最后的设计类图:
原来我们在设计的时候都知道面向接口编程,所以在DAO层都有一个接口,这样我们就不在需要将dao层的具体实现暴露给调用者,但是我们并没有考虑抽象将公共的方法放在一个父接口中,因为我们有很多的公共的方法,比方说:对于每一个实体我们都至少有四个方法——增、删、该、查等,在上面我们抽取了6个公共的方法。这样的话我们就很好的避免在每一个接口中都定义增删该查的方法。这样每一个具体业务的接口都去继承这个父接口就好了。
下面我们就来分析一下抽象类的这个问题,我们为什么会增加了一个抽象类?这就需要我们了解接口和抽象类概念和解决什么样的问题,在数据库的操作我们采用的hibernate来实现的,所以在实现增删该查的代码是是一样的,只是我们操作的实体不同,所以这里就要用到反射的技术来拿到我们具体操作的实体的类型。如果我们比较了解反射技术的话,这就非常好理解了。然后具体的实现类继承这个抽象类,这样对于公共方法的实现就可以抽象到这个类里面来实现,所有的公共的方法的只有一个实现。
也就是我们具体的实现类在继承了抽象类以后,还要实现一个接口?这是因为那些和具体业务逻辑有关的方法需要在具体的接口里面声明,我们只是将公共的方法放在了baseDao里面。
下面来看一下怎样利用泛型+反射来实现抽象类的中方法的实现?
接口中的代码:
package cn.itcast.oa.base.dao; import java.util.List; public interface DaoSupport<T> { /** * 保存实体 * * @param entity */ void save(T entity); /** * 删除实体 * * @param entity */ void delete(Long id); /** * 更新实体 * * @param entity */ void update(T entity); /** * 通过id查询 * * @param id * @return */ T getById(Long id); /** * 根据ids 返回实体集合 * * @param ids * @return */ List<T> getByIds(Long[] ids); /** * 获得所有数据集合 * * @return */ List<T> findAll(); }
抽象类中的代码?
package cn.itcast.oa.base.dao; import java.lang.reflect.ParameterizedType; import java.util.Collections; import java.util.List; import javax.annotation.Resource; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.transaction.annotation.Transactional; @SuppressWarnings("unchecked") @Transactional public abstract class DaoSupportImpl<T> implements DaoSupport<T> { @Resource protected SessionFactory sessionFactory; private Class<T> clazz; public DaoSupportImpl() { // 使用反射技术得到T的真实类型 ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass(); // 获取当前new的对象的 // 泛型的父类 // 类型 this.clazz = (Class<T>) pt.getActualTypeArguments()[0]; // 获取第一个类型参数的真实类型 System.out.println("clazz ---> " + clazz); } /** * 获取当前可用的Session * * @return */ protected Session getSession() { return sessionFactory.getCurrentSession(); } public void save(T entity) { getSession().save(entity); } public void update(T entity) { getSession().update(entity); } public void delete(Long id) { Object obj = getById(id); if (obj != null) { getSession().delete(obj); } } public T getById(Long id) { if (id == null) { return null; } else { return (T) getSession().get(clazz, id); } } public List<T> getByIds(Long[] ids) { if (ids == null || ids.length == 0) { return Collections.EMPTY_LIST; } else { return getSession().createQuery(// "FROM " + clazz.getSimpleName() + " WHERE id IN (:ids)")// .setParameterList("ids", ids)// .list(); } } public List<T> findAll() { return getSession().createQuery(// "FROM " + clazz.getSimpleName())// .list(); } }
上面也是对于反射的一个典型的应用了,其实反射就是一种动态的生成过程,有的时候在编译期间,并不知道应该去impot那一个class。只能通过一一些方法拿到一个名字的时候,就需要考虑用反射类做了。
小结
其实我们在很多的源码中都是这么设计的,对于抽象类和接口应用的非常的灵活,这样就可以使我们的代码非常的简介并且灵活,这些也是需要我们在平常学习过程中非常值得研究的一块内容。