介绍
定义一个操作中算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤。通俗讲,完成一件事情有固定的几个步骤,但是每个步骤根据对象的不同而实现细节不同,可以在父类中定义一个完成该事情的总方法,按照完成事件需要的步骤去调用其每个步骤的实现方法,每个步骤的具体实现,由子类完成。
应用1: spring中对Hibernate的支持,将一些已经定好的方法封装起来,比如开启事务、获取Session、关闭Session等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存。
应用2:我们查询数据库的时候,其实jdbc底层做了很多事情,如获取连接,创建语句集、执行语句集等。
案例
场景:下面我们来模拟一个JdbcTemplate的简单实现。
代码:
1>定义一个抽象类JdbcTemplate
public abstract class JdbcTemplate {
private DataSource dataSource;
public JdbcTemplate(DataSource dataSource) {
this.dataSource = dataSource;
}
//该方法执行步骤已经规定(骨架搭好)
public List<Object> excuteQuery(String sql, Object[] values){
try {
//1.获取连接
Connection conn=dataSource.getConnection();
//2.创建语句集
PreparedStatement pstmt=conn.prepareStatement(sql);
//3.执行语句集,获得结果集
ResultSet resultSet=pstmt.executeQuery();
//4.解析语句集
List<Object> result=new ArrayList<Object>();
int rowNum=1;
//假如表里10条记录被查出来,那么这里循环这10条记录
while (resultSet.next()){
//一条记录存储一个bean,processResult方法有我们自己去实现
result.add(processResult(resultSet,rowNum++));
}
//5.关闭结果集
resultSet.close();
//6.关闭语句集
pstmt.close();
//7.关闭连接
conn.close();
return result;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
/**
* 定制化实现该接口,但是上面的整个工作流程还是不变
* 以上骨架搭好,但是里面的子方法可以交给子类去做不同的实现
* @param resultSet
* @return
*/
public abstract Object processResult(ResultSet resultSet,int rowNum) throws Exception;
}
2>来一个实体类Member
public class Member {
private String username;
private String password;
private String nickname;
private int age;
private String address;
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 getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
3>实体类对应的dao
public class MemberDao extends JdbcTemplate{
public MemberDao(DataSource dataSource) {
super(dataSource);
}
public List<Object> query(){
String sql="select * from member";
return super.excuteQuery(sql,null);
}
@Override
public Object processResult(ResultSet resultSet,int rowNum) throws Exception{
Member member=new Member();
member.setUsername(resultSet.getString("username"));
member.setAge(resultSet.getInt("age"));
member.setAddress(resultSet.getString("address"));
return member;
}
}
4>测试类
public class MemberDaoTest {
public static void main(String[] args) {
MemberDao memberDao=new MemberDao(null);
memberDao.query();
}
}
分析:以上方法虽然实现了查询,但是缺点很明显,dao类一定要继承抽象类,然后再去调用父类的excuteQuery方法。其实我们通常使用的jdbcTemplate是直接作为dao里的引用传过去的,下面将以上方法做下改进。
改进代码:
1>改进后的JdbcTemplate
//将excuteQuery里的子方法抽离出来,实现解耦
public class JdbcTemplate {
private DataSource dataSource;
public JdbcTemplate1(DataSource dataSource) {
this.dataSource = dataSource;
}
//抽离方法-获取连接
private Connection getConnection() throws Exception{
return 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 resultSet)throws Exception{
resultSet.close();
}
//抽离方法-关闭连接
private void closeConnection(Connection conn)throws Exception{
//通常把他放入连接池中回收
}
//结果集映射
private List<?> parseResultSet(ResultSet rs,RowMapper rowMapper)throws Exception{
// 假如表里10条记录被查出来,那么这里循环这10条记录
List<Object> result=new ArrayList<Object>();
int rowNum=1;
while (rs.next()){
//一条记录存储一个bean,processResult方法有我们自己去实现
result.add(rowMapper.mapRow(rs,rowNum++));
}
return result;
}
//执行查询
public List<?> excuteQuery(String sql, RowMapper<?> rowMapper,Object[] values){
try {
//1.获取连接
Connection conn=this.getConnection();
//2.创建语句集
PreparedStatement pstmt=this.createPreparedStatement(conn,sql);
//3.执行语句集,获得结果集
ResultSet resultSet=this.executeQuery(pstmt,values);
//4.解析语句集
List<?> result=this.parseResultSet(resultSet,rowMapper);
//5.关闭结果集
this.closeResultSet(resultSet);
//6.关闭语句集
this.closeStatement(pstmt);
//7.关闭连接
this.closeConnection(conn);
return result;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
}
2>新增RowMapper接口
public interface RowMapper<T> {
public T mapRow(ResultSet rs,int rowNum)throws Exception;
}
3>改进后的dao(符合我们日常所用的JdbcTemplate)
//在这里dao不需要再去继承抽象类,而是直接引入JdbcTemplate
public class MemberDao{
private JdbcTemplate jdbcTemplate=new JdbcTemplate(null);
public List<?> query(){
String sql="select * from member";
return jdbcTemplate.excuteQuery(sql, new RowMapper<Object>() {
@Override
public Object mapRow(ResultSet rs, int rowNum) throws Exception {
Member member=new Member();
member.setUsername(rs.getString("username"));
member.setAge(rs.getInt("age"));
member.setAddress(rs.getString("address"));
return member;
}
},null);
}
}
总结:模板模式就是先有一个流程,这个流程是规定了不可变得,你只能在该流程的子流程里去重新实现下自己的一些逻辑,不会影响到整体的流程步骤。