原文:使用模板方法设计模式、策略模式 处理DAO中的增删改查html
在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的增删改查能够使用下面方法:sql
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中。app
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()) { //rowMapper实如今子类中,根据子类算法进行处理 //只能有一种算法处理,若是要想对rs有不一样处理要使用下面的策略模式 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这个抽象方法(子类实现父类定义的抽象方法)。ide
@Override protected 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实现类中的代码就变得简单明了不少了。完整dao实现类UserDaoImpl代码以下:this
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; } @Override protected 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这个实现,并且不方便,程序不够灵活。rowMapper实现以下:设计
@Override protected Object rowMapper(ResultSet rs) throws SQLException { return rs.getString("userName"); }
这显然不是咱们想要看到的。为了解决这个问题,咱们能够使用策略模式来改进咱们的程序。code
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
用它来处理咱们查询到的结果集。
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 { //根据传入的策略RowMapper来处理结果集ResultSet 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这个接口的类来处理咱们的结果集(在这里咱们使用的是匿名类) 。
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.形成不少的策略类(实现类)。