模板模式

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回调了。