Java设计模式--模板模式

一.模板模式概念

在Java中实现某类事情的步骤有些是固定的,有些是会发生变化的,因此出现了模板方法这一概念。模板模式在Java中一般才用abstract实现,我们将固定的步骤在抽象类中实现,将根据需要变化的部分设置为抽象方法,让其实现类来根据自己的需要实现(必须实现),在标准的模板方法模式实现中,主要是使用继承的方式,来让父类在运行期间可以调用到子类的方法。

二.模板模式实现步骤:

1.写先出解决该类事情的一个具体例子的解决方案(也就是将这个问题特殊化,提出一种解决方案,并写出相应的代码);
2.分析代码,把会发生变化的代码抽取出来独立成一个方法,把该方法描述成一个抽象的方法;
3.使用final修饰模板方法,防止别人重写模板方法。
我们用一个很简单的代码来表述一下:

public abstract class Template {  
  
    //模板方法  
    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 TemplateImpl extends Template {  
    @Override  
    protected void method3() {  
        System.out.println("method3()在子类TemplateImpl中实现了!!");  
    }  
}  

在这里子类只覆盖了自己必须覆盖(模板类的abstract方法)的部分。
我们来测试一下:

Template t1 = new TemplateImpl();  
t1.templateMethod();  

//结果是:
父类实现业务逻辑  
父类默认实现,子类可覆盖  
method3()在子类TemplateImpl中实现了!!  

可以看出这里TemplateImpl根据需要在必须实现的method3()中添加了自己的功能模块,method2()方法父类已经提供了默认实现,如果子类觉得有必要更改的实现,也可以根据自己的需要来实现相应的功能模块。

Spring--模板模式

在之前接触spring的时候使用了JdbcTemplate方法,当时就觉得spring中的设计模式真的是太流弊了。模板方法(template method)在spring中也是被大量使用,如:jdbcTemplate,hibernateTemplate,JndiTemplate以及一些包围的包装等都无疑使用了模板模式,但spring并不是单纯使用了模板方法,而是在此基础上做了创新,配合callback(回调)一起使用,用得极其灵活。 回调模式这里不进行讲解,下一节再进行讲解。

spring通过封装JDBC API,对外提供jdbcTemplate实现了代码的灵活复用,大大提高了程序员的开发效率。现在假设一下spring没有采用模板模式会怎么样呢?如下是一段曾经堪称经典的JDBC API代码:

public List query() {    
    List userList = new ArrayList();  
    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;  
}  

可以看出在这段简单的User表查询代码中,我们依次做了以下工作:
1、获取connection
2、获取statement
3、获取resultset
4、遍历resultset并封装成集合
5、依次关闭connection,statement,resultset,而且还要考虑各种异常
6、.....
这里只是查询了一个User表所需的代码,如果需要查询其他表呢?当然是也要不断地做重复的工作......可想而知需要多少冗余的代码来完成相同的工作。

此时模板模式就出现了:
通过观察我们发现上面步骤中大多数都是重复的,可复用的,只有在遍历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);  
}

在上面的抽象类中,封装了JDBC API的主要流程,而遍历ResultSet这一步骤则放到抽象方法doInStatement()中,交由子类实现。
接下来我们定义一个子类,并继承上面的父类:

public class JdbcTemplateUserImpl extends JdbcTemplate {  
  
    @Override  
    protected Object doInStatement(ResultSet rs) {  
        List userList = new ArrayList();  
          
        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;  
        }  
    }  
}  

我们在子类的doInStatement()方法中遍历并封装了结果集返回。
接下来我们只需要来调用方法即可:

String sql = "select * from User";  
JdbcTemplate jt = new JdbcTemplateUserImpl();  
List userList = (List) jt.execute(sql);  

至此,模板模式就成功了应用到了JDBC API当中,这里只是一张表的查询,查询过程中代码只需要实现查询结果集的封装即可,不需要在新的查询当中再次编写关于创建连接、捕获异常、释放连接这些公共的代码,通过采用模板模式极大地提高了代码复用性。

这里我们通过代码讲解spring中的JdbcTemplate。根据前边模板模式的使用步骤:先将固定化的流程:数据库连接的获取、连接的关闭等功能模块实现。然后将变化的部分(结果集的封装)通过抽象方法交给子类或者回调函数来实现。

模板模式学习过程中参照了一些前人的文章:
https://blog.csdn.net/zhang23242/article/details/9305925
https://blog.csdn.net/jpzhu16/article/details/50888435

你可能感兴趣的:(Java设计模式--模板模式)