设计模式之模板方法
这一节记录一下模板方法,在学模板方法之前,要先学习一下什么是模板,模板其实就是一个例子,例如我们做市场调研时,调研人员会给我们一个表格,我们只需要回答一些答案即可,映射到我们的代码中就是这个样子:
aaaaaaaa bbbbbbbb cccccccc ******** dddddddd eeeeeeee ######## ffffffff gggggggg $$$$$$$$
这是我们第一次写的代码,但是我们在后来的coding中发现,这一段代码我们需要copy一份,只需要该其中的第四行、第七行、第十行就行了,其余的不需要做修改,如果我们copy过去,改一下这么做虽然可以完成,但我们想一想如果我们代码里面一万处这样的代码,难道要copy一万次?这么做是不是很冗余?这时候就需要我们的模板方法上场了,下面看一个我们在实际中的典型应用:JDBC操作数据库,所以我们下面先看一个操作数据库的实际例子:
package cn.bridgeli.demo.dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import cn.bridgeli.demo.entity.User; import cn.bridgeli.demo.util.DBUtil; public class UserDao { public User getUserById(int userId) { Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; User user = null; String sql = "SELECT * FROM user WHERE id = ?"; try { conn = DBUtil.getConn(); pstmt = DBUtil.getPstmt(conn, sql); pstmt.setInt(1, userId); rs = pstmt.executeQuery(); if (rs.next()) { user = new User(); // } } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(rs); DBUtil.close(pstmt); DBUtil.close(conn); } return user; } }
这段代码经过我们分析,很明显就建行不一样:String SQL不一样,pStmt设置参数不一样,if(rs.next())不一样,所以我们可以把它做成模板方法,下面我们就重构着一段代码。在重构这段代码之前有几个问题我们先要明确:
1. 我们返回的不一定是一个user,而且不只是一个,为了解决这个问题,返回改成List(Object);
2. pStmt设置参数不一样,我们可以把这一段改成一个类的一个方法;
3. 同样if(rs.next())这个也可以把这段改成一个类的一个方法。
所以就变成了这样:
package cn.bridgeli.demo.template; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import cn.bridgeli.demo.util.DBUtil; public class JDBCTemplate { public List<Object> query(String sql, JDBCCallBack jdbcCallBack) { Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; List<Object> data = null; try { conn = DBUtil.getConn(); pstmt = DBUtil.getPstmt(conn, sql); jdbcCallBack.setParam(pstmt); rs = pstmt.executeQuery(); if (rs.next()) { data = new ArrayList<Object>(); Object object = jdbcCallBack.rsToObject(rs); data.add(object); } } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(rs); DBUtil.close(pstmt); DBUtil.close(conn); } return data; } public Object queryOne(String sql, JDBCCallBack jdbcCallBack) { List<Object> data = query(sql, jdbcCallBack); if (null != data && !data.isEmpty()) { return data.get(0); } return null; } }
这样的话,我们的UserDao类就变成了这样:
package cn.bridgeli.demo.dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import cn.bridgeli.demo.entity.User; import cn.bridgeli.demo.template.JDBCCallBack; import cn.bridgeli.demo.template.JDBCTemplate; import cn.bridgeli.demo.util.DBUtil; public class UserDao { public User getUserById(int userId) { String sql = "SELECT * FROM user"; return (User) JDBCTemplate.queryOne(sql, new JDBCCallBack() { @Override public void setParam(PreparedStatement pstmt) { // TODO Auto-generated method stub } @Override public Object rsToObject(ResultSet rs) { // TODO Auto-generated method stub return null; } }); } }
这样的话,我们在查询的时候,是不是变得特别简单了,我们只需要重写我们JDBCCallBack中的两个方法就好了,这样的话我们是不是最大限度的实现了代码复用。但是这么一来也有一个问题:我们是不是永远都是需要重写这两个方法呢?举个最简单的例子:如果我们使用:当我们插入数据的时候,是不是就没有rsToObject()方法了?为了解决这个问题我们可以写一个抽象类去实现JDBCCallBack接口,这样的话我们只需要new这个抽象类,重写需要我们重写的方法就可以了,这样的话是不是就解决了这个问题了,其实解决这个问题的方法也是一个设计模式:适配器模式!将来有机会我们在具体讲,这里大家知道就好了。
另外再多说一点,在写模板方法时,其实很多时候我们第一次写的时候,我们并不知道这个方法是可以做成模板方法的,所以你就大胆的写吧,但我们想拷一个方法时,这个时候你就要小心了,是不是可以做成模板方法了,当第三次的时候,肯定是要去重构代码了,当然这么写就完美了吗?既然这么问,肯定还不完美,问题就是:在我们UserDao里面getUserById()我们直接写死了User对象,这样是不是就导致我们我们的这个方法就不能够复用了?有人说这个不能复用很正常啊,因为你是getUserById(),其实我们在深入的想一想,我们要实现的是不是getById(),因为我们如果有一万个需要获取的对象,是不要这个方法又要写一万次?这样的话,代码是不是又要写一万次?代码有重复了,所以我们很自然而然的就想到了可以写成:泛型!下面我们就用泛型试着重构一下我们的代码,因为泛型不支持静态方法,所以肯定就不静态方法了,这样的感兴趣的可以自己试一试(其实说简单不简单,说难也不难)
最后再说两点,1. 其实众所周知,我们的servlet也是一个模板方法,而且还没有template这个类,那么他是怎么实现的呢?servlet是方法级的模板方法,而我们这个实现可以说是对象级别的模板方法,感兴趣的可以自己看一下servlet的源码,实现一点也不难,我相信大家是能够看懂的;2. 我们的JDBCTemplate相信大家看出来了,因为他是无状态的类,是可以做成单例的,这个也交给大家去实现了,因为前面已有介绍单例的文章,当然提到单例,大家肯定可以想的到:工厂模式!
作 者: BridgeLi,https://www.bridgeli.cn
原文链接:http://www.bridgeli.cn/archives/121
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。
近期评论