模板模式(Template):行为型模式的一种,定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method 使得子类可以不改变 一个算法的结构即可重定义该算法的某些特定步骤。
简单的理解就是,一个架构或者业务的主体逻辑和流程是确定的,那么我们可以将它的这些逻辑流程抽象出来,然后真正具体实现的时候在它的子类之中进行实现。
这是第一种思想,实现相对来说并不困难,引申的另一种思想就是,如果当一个业务是固定而且繁琐的,但调用又是比较频繁的话,每次调用用到的参数或许不同,那么也可以考虑使用的模板模式。
在Spring中就有一个比较重要而且常用的类JdbcTemplate,作为数据库的连接,会经常被使用到,但它里面的方法繁多,并不想被继承,所以我们可以把变化的东西抽出 来作为一个参数传入 JdbcTemplate 的方法中。但是变化的东西是一段代码,而且这段代码会用到JdbcTemplate 中的变量。怎么办?那我们就用回调对象吧。在这个回调对象中定义一个操纵JdbcTemplate 中变量的方法,我们去实现这个方法,就把变化的东西集中到这里了。然后我们再传入 这个回调对象到 JdbcTemplate,从而完成了调用。这就是 Template Method 不需要继承的另一种实 现方式。
现在举一个例子吧,在数据库连接中,执行的流程是固定的:
1.获取连接,2.创建语句集,3.执行语句集,并获取结果集,4.解析语句集,5.关闭结果集,6.关闭语句集,7关闭连接
所以我们可以把这部分单独拿出来放在JdbcTemplete里面,而实际的查询语句,是经常变化的。
部分JdbcTemplate代码:
package Template;
import java.util.List;
import javax.sql.DataSource;
import javax.swing.tree.RowMapper;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
public class JdbcTemplate {
private DataSource dataSource;
public JdbcTemplate(DataSource dataSource){
this.dataSource = dataSource;
}
private Connection getConnection() throws Exception{
return this.dataSource.getConnection();
}
private PreparedStatement createPreparedStatement(Connection conn,String sql) throws Exception{
return conn.prepareStatement(sql);
}
private ResultSet executeQuery(PreparedStatement pstmt,Object [] values) throws Exception{
for (int i = 0; i <values.length; i ++){
pstmt.setObject(i,values[i]);
}
return pstmt.executeQuery();
}
private void closeStatement(Statement stmt) throws Exception{
stmt.close();
}
private void closeResultSet(ResultSet rs) throws Exception{
rs.close();
}
private void closeConnection(Connection conn) throws Exception{
//通常把它放到连接池回收
}
private List<?> parseResultSet(ResultSet rs, RowMapper rowMapper) throws Exception{
List<Object> result = new ArrayList<Object>();
int rowNum = 1;
while (rs.next()){
result.add(rowMapper.mapRow(rs,rowNum ++));
}
return result;
}
public List<?> executeQuery(String sql,RowMapper<?> rowMapper,Object [] values){
try {
//1、获取连接
Connection conn = this.getConnection();
//2、创建语句集
PreparedStatement pstmt = this.createPreparedStatement(conn,sql);
//3、执行语句集,并且获得结果集
ResultSet rs = this.executeQuery(pstmt,values);
//4、解析语句集
List<?> result = this.parseResultSet(rs,rowMapper);
//5、关闭结果集
this.closeResultSet(rs);
//6、关闭语句集
this.closeStatement(pstmt);
//7、关闭连接
this.closeConnection(conn);
return result;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
}
对应数据库中成员Member类:
package com.gupaoedu.vip.pattern.template.entity;
/**
* Created by Tom on 2018/3/11.
*/
public class Member {
private String username;
private String password;
private String nickName;
private int age;
private String addr;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
}
对应DAO查询类:
package Template.dao;
import Template.JdbcTemplate;
import Template.entity.Member;
import javax.swing.tree.RowMapper;
import java.sql.ResultSet;
import java.util.List;
public class MemberDao {
private JdbcTemplate jdbcTemplate = new JdbcTemplate(null);
public List<?> query(){
String sql = "select * from t_member";
return JdbcTemplate.executeQuery(sql,new RowMapper<Member>(){
@Override
public Member mapRow(ResultSet rs, int rowNum) throws Exception {
Member member = new Member();
member.setUsername(rs.getString("username"));
member.setPassword(rs.getString("password"));
member.setAge(rs.getInt("age"));
member.setAddr(rs.getString("addr"));
return member;
}
},null);
}
}
需要的Rowmapper
package Template;
import java.sql.ResultSet;
public interface Rowmapper<T> {
public T mapRow(ResultSet rs, int rowNum) throws Exception;
}
以上并不算特别完整,一个是具体需要创建相对应数据库,加入依赖,所以这里只是举例。
可以看到,一个是但业务架构定下来之后,基础流程可以使用模板方法,另一个但单独的比较复杂但是流程固定的单个功能确定的是后也可以使用模板方法