还记得在刚开始学习JDBC时,每一次SQL执行都要经历从连接的获取、执行SQL、获取结果等,这些是一个流程性的动作。在大二时,刚刚学会没多久后,当时我正在学习JSP、Servlet(我的Java是自学的),有一个高中的同学让我帮她做课程设计。现在还依旧记得,那是一个校园一卡通项目,功能很少,整个课程设计涉及到了10多个SQL语句。
我当时写代码时,把所有的SQL语句都写在了一个Java文件中。每一个SQL语句的执行都要经历:
// 获取连接 Connection conn=getConnection();// 自定义的 // 获取语句 PreparedStatement psmt=conn.prepareStatement(sql); // 设置参数 // .... // SQL 执行 psmt.executeUpdate或者psmt.executeQuery // 获取结果 // 从ResultSet中取数 // 关闭连接 close()
当代码写了400多行时,我就感觉到这样写太麻烦了,有没有一个模板来处理这个过程呀,这样写下去太麻烦了,这是我当时的真实想法。
我就在想能不能把这个烦人的过程放到一个模板里呢?不同的地方只有SQL执行和结果是如何处理,前面的过程都是一样的。那么我把SQL执行与结果处理拿出来,整个过程的其他部分不变,应该是可以的吧。可以使用抽象类来完成这个功能。
当时想到的是:
public abstract class JDBCTemplate { private String url; private String driver; private String user; private String password; protected Connection conn; public JDBCTemplate(){ } public JDBCTemplate(String driver, String url, String user, String password){ this.driver=driver; this.url=url; this.user=user; this.password=password; } //// getter, setter就不粘出来了。 public Connection getConnection(){ try { Class.forName(driver); conn=DriverManager.getConnection(url, user, password); } catch (Exception e) { e.printStackTrace(); } return conn; } public void close(PreparedStatement statement) throws Exception{ if (statement!=null){ statement.close(); } if(conn!=null){ conn.close(); } } protected final Object template(String sql, Object[] params){ conn=getConnection(); PreparedStatement psmt=null; try{ psmt=conn.prepareStatement(sql); for (int i=0; i<params.length; i++){ psmt.setObject(i+1, params[i]); } return executeAndGetResult(psmt); }catch (Exception e){ e.printStackTrace(); return e; } finally { try{ close(psmt); }catch (Exception e){ e.printStackTrace(); } }} public abstract Object executeAndGetResult(PreparedStatement psmt); }
这样一来,executeAndGetRresult就由子类来完成,并且也不用再写那些烦人的过程了。当时的我,并不知道,这样写其实就是应用了模板方法模式。
=============================================================
再后来,在学习AJax时,通过网上知道一种函数:callback,回调函数,也成为钩子函数。当时对此并不了解,上网查询学习,多多少少对回调函数有种模糊的概念。当时我想到了我之前写的那个JDBCTemplate,能否使用Callback来调整代码呢?
于是我改造了代码:
// 添加了一个回调接口 public interface ResultSetHandler { public Object handle(PreparedStatement statement); } //对模板方法接口做如下调整: protected final Object template(String sql, Object[] params, ResultSetHandler handler){ Connection conn=getConnection(); PreparedStatement psmt=null; try{ psmt=conn.prepareStatement(sql); for (int i=0; i<params.length; i++){ psmt.setObject(i+1, params[i]); } return handler.handle(psmt); }catch (Exception e){ e.printStackTrace(); return e; } finally { try{ close(psmt); }catch (Exception e){ e.printStackTrace(); } } }
也就是说,整个调整只换了template方法中的一句:由executeAndGetResult(psmt)调整为handler.handle(psmt);
并把tempalte的abstract去掉了。调整后,感觉这种方式比第一种更好,只是当时的我依旧不知道什么是模板方法模式。我只知道,这样写挺好的,感觉心里很爽。于是我把这个类一直保留着,尽管后来没有使用它。
后来在学习设计模式的模板方法模式时,我终于知道,原来我就用过模板方法模式,只是我不知道而已。
=================================================================
在GOF设计模式中是这么描述设计模式的:
模板方法定义了一个算法步骤,并允许子类为一个或者多个步骤提供实现。
模板方法模式的类图如下:
上面的类图中,primitiveOperation1、 primitiveOperation2是抽象方法,就是整个算法中的不确定部分。他们要在模板方法中使用。我第一次写的那个JDBCTemplate中的executeAndGetResult就是这样的。
模板方法模式体系了对抽象编程的设计原则。
使用模板方法模式的实现中,主要是通过继承的方式,让父类在运行期间可以调用子类的方法。在Java开发中,在模板方法与回调函数结合使用时,就不需要通过继承来完成变化部分的代码了,通过Java的匿名内部类,同样可以完成上面的上述功能。
例如我的JDBCTemplate的第二版中使用了接口的方式。
接下来,我在JDBCTemplate内部添加一个方法:
public int save(String sql, Object[] params){ return (Integer)template(sql,params, new ResultSetHandler() { public Object handle(PreparedStatement statement) { int result=0; try{ result= statement.executeUpdate(); }catch (Exception e){ e.printStackTrace(); } return result; } }); }
这是一个save数据的方法。使用过程中调用了template(String sql, Object[] params, ResultSetHandler handler);并不是使用子类来继承JDBCTemplate
而是子啊方法调用时使用了匿名内部类。
这样做(使用Callback)可以说是TemplateMethod实现方式的一个变形。
至于GOF设计模式书上说的那些关于模板方法模式的其他功能,一看便知,这里就不再说明了。
模板方法与回调函数