使用模板方法设计模式处理DAO中的查询方法

来源:互联网 发布:比价源码 编辑:程序博客网 时间:2024/05/16 05:29
摸板方法(Template Method)模式是一种非常简单而又经常使用的设计模式.先创建一个父类,把其中的一个或多个方法留给子类去实现,这实际上就是在使用摸板模式.所谓的摸板模式可以这样来理解:"在一个类中定义一个算法,但将此算法的某些细节留到子类中去实现.换句话说,基类是一个抽象类,那么你就是在使用一种简单形式的摸板模式."

更近一步可以这样来理解:"准备一个抽象类,将部分逻辑以具体方法的形式实现,然后申明一些抽象方法来迫使子类实现剩余的逻辑.不同的子类可以以不同的方法实现这些抽象方法,从而对剩余的逻辑有不同的实现."

将一个类的基本部分抽取出来放到一个基类中,这样它就不必重复出现在几个派生类里.

抽象摸板角色:(抽象父类)
1. 定义了一个或多个抽象操作,以便让子类实现.
2. 定义并实现了一个摸板方法.

具体摸板角色:(具体实现类)
1. 实现父类所定义的一个或多个抽象方法.
2. 每一个抽象摸板角色都可以有任意多个具体摸板角色与之对应.
3. 每一个具体摸板角色都可以给出这些抽象方法的不同实现.

模式中的方法种类

1. 抽象模板角色里提供完整的方法,它完成了所有派生类都要用到的一些基本功能.

2. 抽象模板角色里只提供空方法,把功能全部留给派生类去实现.

3. 抽象模板角色里只包含某些操作的默认实现,派生类里可以重新定义这些方法的实现.

4. 抽象模板角色里模板方法,他是一个调用抽象方法,钩子方法以及具体方法的各种组合.

在dao中,我们经常要做增删改查操作,如果每个对每个业务对象的操作都写一遍,代码量非常庞大.
因此,我们可以将dao中增删改查分开为两个部分,
一些是不变的代码,比如创建局部变量Connection conn,PreparedStatement ps,ResultSet rs等等
public int update(String sql, Object[] args) {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {conn = DbUtils.getConnection();ps = conn.prepareStatement(sql);//参数设置return ps.executeUpdate();} catch (SQLException e) {throw new DaoException(e.getMessage(), e);} finally {DbUtils.close(rs);DbUtils.close(ps);DbUtils.close(conn);}}

上面代码,在每个操作中基本一样,我们可以将其提取出来

另外一部分是变化的代码
即我们传入的参数如sql,以及参数列表Object[] params等等..... 对vo的增删改查可以使用下面方法

public int update(String sql, Object[] args) {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {conn = DbUtils.getConnection();ps = conn.prepareStatement(sql);for (int i = 0; i < args.length; i++)ps.setObject(i + 1, args[i]);return ps.executeUpdate();} catch (SQLException e) {throw new DaoException(e.getMessage(), e);} finally {DbUtils.close(rs);DbUtils.close(ps);DbUtils.close(conn);}}


对vo的查询关系到对ResultSet的处理,我们可以在抽象父类中定义一个抽象的方法
abstract Object rowMapper(ResultSet rs) throws SQLException;
这样子类继承了抽象父类后必须实现rowMapper这个方法

这样我们就可以构造出一个将方法抽象到一个父类abstractDao中
abstractDao.java
package com.royzhou.dao;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import com.royzhou.db.DbUtils;import com.royzhou.exception.DaoException;public abstract class abstractDao {public Object find(String sql, Object[] args) {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {conn = DbUtils.getConnection();ps = conn.prepareStatement(sql);for (int i = 0; i < args.length; i++)ps.setObject(i + 1, args[i]);rs = ps.executeQuery();Object obj = null;if (rs.next()) {obj = rowMapper(rs);}return obj;} catch (SQLException e) {throw new DaoException(e.getMessage(), e);} finally {DbUtils.close(rs);DbUtils.close(ps);DbUtils.close(conn);}}abstract protected Object rowMapper(ResultSet rs) throws SQLException;public int update(String sql, Object[] args) {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {conn = DbUtils.getConnection();ps = conn.prepareStatement(sql);for (int i = 0; i < args.length; i++)ps.setObject(i + 1, args[i]);return ps.executeUpdate();} catch (SQLException e) {throw new DaoException(e.getMessage(), e);} finally {DbUtils.close(rs);DbUtils.close(ps);DbUtils.close(conn);}}}


然后在userDaoImpl中我们继承abstractDao这个类并实现rowMapper这个抽象方法

@Overrideprotected Object rowMapper(ResultSet rs) throws SQLException {User user = new User();user.setId(rs.getString("id"));user.setUserName(rs.getString("userName"));user.setLoginId(rs.getString("loginId"));user.setPassword(rs.getString("password"));return user;}


这样dao实现类中的代码就变得简单明了很多了
UserDaoImpl.java
package com.royzhou.dao.impl;import java.sql.ResultSet;import java.sql.SQLException;import com.royzhou.dao.abstractDao;import com.royzhou.vo.User;public class UserDaoImpl extends abstractDao {public User findUser(String id) {String sql = "select id, userName, loginId, password  from user where id=?";Object[] args = new Object[] { id };Object user = super.find(sql, args);return (User) user;}@Overrideprotected Object rowMapper(ResultSet rs) throws SQLException {User user = new User();user.setId(rs.getString("id"));user.setUserName(rs.getString("userName"));user.setLoginId(rs.getString("loginId"));user.setPassword(rs.getString("password"));return user;}public void delete(User user) {String sql = "delete from user where id=?";Object[] args = new Object[] { user.getId() };super.update(sql, args);}public void update(User user) {String sql = "update user set userName=?, loginId=?, password=? where id=? ";Object[] args = new Object[] { user.getUserName(), user.getLoginId(),user.getPassword(), user.getId() };super.update(sql, args);}}


不过上面代码查询的时候存在一个问题:假如我只需要查找username这一列的数据,我们必须重写rowMapper这个实现,而且不方便,程序不够灵活
@Overrideprotected Object rowMapper(ResultSet rs) throws SQLException {return rs.getString("userName");}


这显然不是我们想要看到的
为了解决这个问题,我们可以使用策略模式来改进我们的程序

GOF《设计模式》一书对Strategy模式是这样描述的:
定义一系列的算法,把他们一个个封装起来,并且使它们可相互替换。Strategy模式使算法可独立于使用它的客户而变化。
Strategy模式以下列几条原则为基础:
1) 每个对象都是一个具有职责的个体。
2) 这些职责不同的具体实现是通过多态的使用来完成的。
3) 概念上相同的算法具有多个不同的实现,需要进行管理。

如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态的让一个对象在许多行为中选择一种行为。

如果系统需要动态地在几种算法中选择一种。那么这些算法可以包装到一个个的具体算法类里面,而这些算法类都是一个抽象算法类的子类。换言之,这些具体算法类均有统一的接口,由于多态性原则。客户端可以选择使用任何一个具体算法类,并只持有一个数据类型是抽象算法类的对象。

设计原则是把一个类中经常改变或者将来可能改变的部分提取出来,作为一个接口,然后在类中包含这个对象的实例,这样类的实例在运行时就可以随意调用实现了这个接口的类的行为。通常,策略模式适用于当一个应用程序需要实现一种特定的服务或者功能,而且该程序有多种实现方式时使用。

在处理ResultSet结果集的时候,我们可以使用接口变成,将结果集的操作交给一个接口来处理
public Object find(String sql, Object[] args, RowMapper rowMapper)
其中RowMapper 这个接口里面只有一个方法
public Object mapRow(ResultSet rs) throws SQLException,用它来处理我们查询到的结果集

MyDaoTemplate.java
package com.royzhou.dao;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import com.royzhou.db.DbUtils;import com.royzhou.exception.DaoException;public class MyDaoTemplate {public Object find(String sql, Object[] args, RowMapper rowMapper) {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {conn = DbUtils.getConnection();ps = conn.prepareStatement(sql);for (int i = 0; i < args.length; i++)ps.setObject(i + 1, args[i]);rs = ps.executeQuery();Object obj = null;if (rs.next()) {obj = rowMapper.mapRow(rs);}return obj;} catch (SQLException e) {throw new DaoException(e.getMessage(), e);} finally {DbUtils.close(rs);DbUtils.close(ps);DbUtils.close(conn);}}}


这样我们就可以根据不同的需要使用实现了RowMapper这个接口的类来处理我们的结果集(在这里我们使用的是匿名类)
UserDaoImpl1.java
package com.royzhou.dao.impl;import java.sql.ResultSet;import java.sql.SQLException;import com.royzhou.dao.MyDaoTemplate;import com.royzhou.dao.RowMapper;import com.royzhou.vo.User;public class UserDaoImpl1 {private MyDaoTemplate template = new MyDaoTemplate();public User findUser(String id) {String sql = "select id, userName, loginId, password  from user where id=?";Object[] args = new Object[] { id };Object user = this.template.find(sql, args, new RowMapper(){public Object mapRow(ResultSet rs) throws SQLException {User user = new User();user.setId(rs.getString("id"));user.setUserName(rs.getString("userName"));user.setLoginId(rs.getString("loginId"));user.setPassword(rs.getString("password"));return user;}});return (User) user;}public String findUserName(String id) {String sql = "select userName from user where id=?";Object[] args = new Object[] {id};Object userName = this.template.find(sql, args, new RowMapper(){public Object mapRow(ResultSet rs) throws SQLException {return rs.getString("userName");}});return (String)userName;}}


通过这样的修改程序变得更加灵活了......对于不同的查询我们只需要用相对的策略写一个匿名类就可以

通过上面例子我们可以总结一下策略模式的优缺点:

策略模式优点:
1.可以很方便的动态改变算法或行为
2.避免使用多重条件转移语句

策略模式缺点:
1.客户端必须知道所有的策略类,并自行决定使用哪一个策略类。

2.造成很多的策略类(实现类)。


原文出处:

http://www.th7.cn/Program/java/201510/611558.shtml

0 0
原创粉丝点击