模板方法与回调函数

还记得在刚开始学习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设计模式书上说的那些关于模板方法模式的其他功能,一看便知,这里就不再说明了。

模板方法与回调函数

时间: 2024-10-09 06:59:44

模板方法与回调函数的相关文章

嵌入式&amp;iOS:回调函数(C)与block(OC)传 参/函数 对比

C的回调函数: callBack.h 1).声明一个doSomeThingCount函数,参数为一个(无返回值,1个int参数的)函数. void DSTCount(void(*CallBack)(int data_i32)); callBack.c 1).在doSomeThingCount函数,对运行次数自增,并调用参数--函数. void DSTCount(void(*CallBack)(int data_i32)) { static int numb = 0; numb++; (*Call

回调函数的本质,就是把函数当作参数(首先要定义函数类型)

//把一个方法当作另一个方法的参数, 就是回调方法, 大家习惯称作回调函数 type   TFunType = function(i: Integer): Integer; {声明一个方法类型} function MyFun(i: Integer): Integer;        {建立类型兼容的函数} begin   Result := i*2; end; {把函数当作参数, 再定义一个函数} function MyTest(x: Integer; F: TFunType): Integer

MFC 定时器 SetTimer 如何使用回调函数

创建工程名TestCallBack 自定义回调函数   定义为全局函数 在TestCallBackDlg.h文件开头定义 #pragma once void CALLBACK EXPORT TimerProc(HWND hWnd,UINT nMsg,UINT nTimerid,DWORD dwTime); 在TestCallBackDlg.cpp文件末尾实现函数 void CALLBACK EXPORT TimerProc(HWND hwnd,UINT message,UINT iTimerID

(转)回调函数

原文:http://blog.csdn.net/callmeback/article/details/4242260 其实回调就是一种利用函数指针进行函数调用的过程. 为什么要用回调呢?比如我要写一个子模块给你用,   来接收远程socket发来的命令.当我接收到命令后,   需要调用你的主模块的函数,   来进行相应的处理.但是我不知道你要用哪个函数来处理这个命令,     我也不知道你的主模块是什么.cpp或者.h,   或者说,   我根本不用关心你在主模块里怎么处理它,   也不应该关心

ajax返回的值有两种方法,一种是把async:true改为false。 另一种是回调函数。

function load_val(callback){//定义一个回调函数 $.getJSON('test.php' , function(dat){ callback(data);//将返回结果当作参数返回 }); } load_val(function(data){ alert(data);//这里可以得到值 }); //否则的话你需要这样用同步ajax来实现了 function load_val2(){ var result; $.ajax({ dataType:'json', url

Python 3 进程池与回调函数

Python 3 进程池与回调函数 一.进程池 在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间.多进程是实现并发的手段之一,需要注意的问题是: 很明显需要并发执行的任务通常要远大于核数 一个操作系统不可能无限开启进程,通常有几个核就开几个进程 进程开启过多,效率反而会下降(开启进程是需要占用系统资源的,而且开启多余核数目的进程也无法做到并行) 例如当被操作对象数目不大时,可以直接利用multiprocessing中的Proces

【Cocos2d-x 3.0 基础系列一】 各类回调函数写法汇总

一.button回调 1. Lambda 表达式,C++11 Lambda 赋予了Cocos2d-x 3.0创建回调函数的灵活性. auto itemNor = Sprite::create("CloseNormal.png"); auto menuItem = MenuItemSprite::create(itemNor,nullptr,nullptr,[](Ref* sender) { log("show this msg."); }); auto menu =

IT小鲜肉 Widgets Tree 单选、多选、相关回调函数、获取选中的节点功能

写一个树控件并没有想象中的那么容易,今天又花了我一个1个多小时,主要为IT小鲜肉 Widgets Tree控件添加了 单选.多选.选择前和选择后两个回调函数.获取选中节点的功能.后面会继续努力完善这个树控件. 1.通过设置初始化时候的选项{select:true}开启单选,通过设置初始化时候的选项{select:{type:'multiple'}}开启多选 使用实例代码如下: 运行效果如下: 2.添加了onBeforeSelect回调函数,用来实现自定义选择,如果该函数返回false会中断默认的

设计一个函数,它接受不定数量的参数,这是参数都是函数。这些函数都接受一个回调函数作为参数,按照回调函数被调用的顺序返回函数名

function acceptFuncs() { var fnNames = []; //定义数组字面量,用来保存函数名称 for (var i = 0; i < arguments.length; i++) { //for循环检测接收到的每个参数是否为函数,是则传递回调函数给它,最后所结果压入数组中 if (typeof arguments[i] === "function") { fnNames.push(arguments[i](callback)); } } for (v