Spring中模板模式和回调模式(一)

来源:互联网 发布:java工程项目log4j2 编辑:程序博客网 时间:2024/05/22 15:11

模板模式

public abstract class TemplatePattern {        //模板方法      public final void templateMethod(){                    method1();          method2();//勾子方法          method3();//抽象方法      }      private void method1(){          System.out.println("父类实现业务逻辑");      }      public void method2(){          System.out.println("父类默认实现,子类可覆盖");      }      protected abstract void method3();//子类负责实现业务逻辑  }  

父类中有三个方法,分别是method1(),method2()和method3()。 
method1()是私有方法,有且只能由父类实现逻辑,由于方法是private的,所以只能父类调用。 
method2()是所谓的勾子方法。父类提供默认实现,如果子类觉得有必要定制,则可以覆盖父类的默认实现。 
method3()是子类必须实现的方法,即制定的步骤。 
由此可看出,算法的流程执行顺序是由父类掌控的,子类只能配合。 

public class TemplatePatternImpl extends TemplatePattern {        @Override      protected void method3() {          System.out.println("method3()在子类TemplatePatternImpl中实现了!!");        }    }  
这个子类只覆盖了必须覆盖的方法
TemplatePattern t1 = new TemplatePatternImpl();  t1.templateMethod();
输出

父类实现业务逻辑  父类默认实现,子类可覆盖  method3()在子类TemplatePatternImpl中实现了!!


接下来模仿spring动手写一个基于模板模式和回调的jdbcTemplate
public List<User> query() {        List<User> userList = new ArrayList<User>();      String sql = "select * from User";        Connection con = null;      PreparedStatement pst = null;      ResultSet rs = null;      try {          con = HsqldbUtil.getConnection();          pst = con.prepareStatement(sql);          rs = pst.executeQuery();            User user = null;          while (rs.next()) {                user = new User();              user.setId(rs.getInt("id"));              user.setUserName(rs.getString("user_name"));              user.setBirth(rs.getDate("birth"));              user.setCreateDate(rs.getDate("create_date"));              userList.add(user);          }          } catch (SQLException e) {          e.printStackTrace();      }finally{          if(rs != null){              try {                  rs.close();              } catch (SQLException e) {                  e.printStackTrace();              }          }          try {              pst.close();          } catch (SQLException e) {              e.printStackTrace();          }          try {              if(!con.isClosed()){                  try {                      con.close();                  } catch (SQLException e) {                      e.printStackTrace();                  }              }          } catch (SQLException e) {              e.printStackTrace();          }                }      return userList;  }  
在面向对象编程的年代里,这样的代码简直不能上人容忍。试想,上面我们只是做了一张表的查询,如果我们要做第2张表,第3张表呢,又是一堆重复的代码: 
1、获取connection 
2、获取statement 
3、获取resultset 
4、遍历resultset并封装成集合 
5、依次关闭connection,statement,resultset,而且还要考虑各种异常 
6、..... 

这时候,使用模板模式的时机到了!!! 

通过观察我们发现上面步骤中大多数都是重复的,可复用的,只有在遍历ResultSet并封装成集合的这一步骤是可定制的,因为每张表都映射不同的java bean。这部分代码是没有办法复用的,只能定制。那就让我们用一个抽象的父类把它们封装一下吧: 

public abstract class JdbcTemplate {        //template method      public final Object execute(String sql) throws SQLException{                    Connection con = HsqldbUtil.getConnection();          Statement stmt = null;          try {                 stmt = con.createStatement();              ResultSet rs = stmt.executeQuery(sql);              Object result = doInStatement(rs);//abstract method               return result;          }          catch (SQLException ex) {               ex.printStackTrace();               throw ex;          }          finally {                 try {                  stmt.close();              } catch (SQLException e) {                  e.printStackTrace();              }              try {                  if(!con.isClosed()){                      try {                          con.close();                      } catch (SQLException e) {                          e.printStackTrace();                      }                  }              } catch (SQLException e) {                  e.printStackTrace();              }                        }      }            //implements in subclass      protected abstract Object doInStatement(ResultSet rs);  }  
在上面这个抽象类中,封装了SUN JDBC API的主要流程,而遍历ResultSet这一步骤则放到抽象方法doInStatement()中,由子类负责实现。 
好,我们来定义一个子类,并继承上面的父类: 

public class JdbcTemplateUserImpl extends JdbcTemplate {        @Override      protected Object doInStatement(ResultSet rs) {          List<User> userList = new ArrayList<User>();                    try {              User user = null;              while (rs.next()) {                    user = new User();                  user.setId(rs.getInt("id"));                  user.setUserName(rs.getString("user_name"));                  user.setBirth(rs.getDate("birth"));                  user.setCreateDate(rs.getDate("create_date"));                  userList.add(user);              }              return userList;          } catch (SQLException e) {              e.printStackTrace();              return null;          }      }    }  

试想,如果我每次用jdbcTemplate时,都要继承一下上面的父类,是不是有些不方面呢? 
那就让我们甩掉abstract这顶帽子吧,这时,就该callback(回调)上场了 
所谓回调,就是方法参数中传递一个接口,父类在调用此方法时,必须调用方法中传递的接口的实现类。 
那我们就来把上面的代码改造一下,改用回调实现吧: 
首先,我们来定义一个回调接口: 
public interface StatementCallback {      Object doInStatement(Statement stmt) throws SQLException;  } 
这时候,我们就要方法的签名改一下了:

private final Object execute(StatementCallback action) throws SQLException

Object result = action.doInStatement(stmt);

public Object query(StatementCallback stmt) throws SQLException{      return execute(stmt);  }

我们来写一个测试类Test.java测试一下吧: 
这时候,访问有两种方式,一种是内部类的方式,一种是匿名方式。 
//内部类方式      public Object query(final String sql) throws SQLException {          class QueryStatementCallback implements StatementCallback {                public Object doInStatement(Statement stmt) throws SQLException {                  ResultSet rs = stmt.executeQuery(sql);                  List<User> userList = new ArrayList<User>();                    User user = null;                  while (rs.next()) {                        user = new User();                      user.setId(rs.getInt("id"));                      user.setUserName(rs.getString("user_name"));                      user.setBirth(rs.getDate("birth"));                      user.setCreateDate(rs.getDate("create_date"));                      userList.add(user);                  }                  return userList;                }            }            JdbcTemplate jt = new JdbcTemplate();          return jt.query(new QueryStatementCallback());      }  

为什么spring不用传统的模板方法,而加之以Callback进行配合呢? 
试想,如果父类中有10个抽象方法,而继承它的所有子类则要将这10个抽象方法全部实现,子类显得非常臃肿。而有时候某个子类只需要定制父类中的某一个方法该怎么办呢?这个时候就要用到Callback回调了。 

原创粉丝点击