模板模式
通常又叫模板方法模式,定义一个算法的骨架,并允许子类为一个或者多个步骤提供实现.
模板方法使得子类可以再不改变算法结构的情况下,重新定义算法的某些步骤. 是行为型设计模式.
适用场景
一次性实现一个算法的不变部分,将可变的行为留给子类来实现;
各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复;
常见的应用:AbstractList
、HttpServlet
的service
方法、Spring的 JdbcTemplate
、Tomcat的架构
中.
模仿JdbcTemplate的模板模式
简单模仿 Spring
的JdbcTemplate
,JDBC操作可以理解为有流程的,可以复制的.最开始学习JDBC操作时:打开连接 → 获取语句集 → 设置参数、执行语句集 → 处理结果集 → 关闭连接 .
JdbcTemplate
代码
package com.template;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class JdbcTemplate {
private DataSource dataSource;
public JdbcTemplate(DataSource dataSource){this.dataSource=dataSource;}
public <T> List<T> executeQuery(String sql,Object[] args,RowMapper<T> rowMapper){
PreparedStatement ps=null;
Connection connection=null;
try {
//1.获取连接
connection =this.getConnection(dataSource);
//2.获得语句集
ps = createPreparedStatement(connection, sql);
//3.执行语句集得到ResultSet
ResultSet rs=doExecuteQuery(ps,args);
//4.处理结果集
List<T> result = parseResultSet(rs,rowMapper);
return result;
}catch (Exception e){
e.printStackTrace();
}finally {
//5.释放资源.
closePreparedStatement(ps);
closeConnection(connection);
}
return null;
}
private void closeConnection(final Connection connection) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
private void closePreparedStatement(final PreparedStatement ps) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
private <T> List<T> parseResultSet(final ResultSet rs, final RowMapper<T> rowMapper) throws Exception {
List<T> result=new ArrayList<>();
int rowNum=1;
while(rs.next()){
result.add(rowMapper.mapperRow(rs,rowNum++));
}
return result;
}
private ResultSet doExecuteQuery(final PreparedStatement ps, final Object[] args) throws SQLException {
for (int i=0;i<args.length;i++){
ps.setObject( i+1,args[i]);
}
return ps.executeQuery();
}
private PreparedStatement createPreparedStatement(Connection connection,String sql) throws SQLException {
return connection.prepareStatement(sql);
}
protected Connection getConnection(final DataSource dataSource) throws SQLException {
return dataSource.getConnection();
}
}
RowMapper
接口
RowMapper接口可以理解为是 钩子,用来在JdbcTemplate模板方法处理结果集时使用.
package com.template;
import java.sql.ResultSet;
public interface RowMapper<T> {
T mapperRow(ResultSet rs,int rowNum) throws Exception;
}
UserDao
测试效果
package com.template;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.util.List;
public class UserDao extends JdbcTemplate {
private static final Object[] EMPTY_ARGS=new Object[0];
public UserDao(final DataSource dataSource) {
super(dataSource);
}
public List<User> selectAll(){
String sql="select * from user";
return super.executeQuery(sql, EMPTY_ARGS, new RowMapper<User>() {
@Override
public User mapperRow(final ResultSet rs, final int rowNum) throws Exception {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setAge(rs.getInt("age"));
user.setSex(rs.getString("sex"));
return user;
}
});
}
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setUser("root");
dataSource.setPassword("yourPassword");
UserDao dao = new UserDao(dataSource);
List<User> users = dao.selectAll();
for(User user:users){
System.out.println(user);
}
}
}
代码测试效果
优点
提高代码复用、扩展性,符合开闭原则
缺点
增加系统复杂度 ;父类添加抽象方法,所有子类都需要重写抽象方法;
框架中常见的模板方法
1.JDK中 AbstractList.get
常见的ArrayList
、Vector
就是AbstractList
的实现,ArrayList返回数组中的index下标的元素.
Servlet-api
中
service
方法继续调用service(HttpServletRequest,HttpServletResponse)
方法,进一步根据请求方法来决定调用doGet
、doPost
等,我们以前开发时候写Servlet,有时候重写doGet
、doPost
方法来.
上面的例子,你可能觉得模板方法只是实现抽象方法,但是在Tomcat
中模板方法的使用可以说是非常精妙了.
Tomcat
中
Tomcat几大组件 Server、Service、Engine、Host、Context、Connector都继承了LifecycleBase抽象类.
LifecycleBase
的init、start、stop、destroy都采用了模板方法,其中以init
方法为例分析:
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
//组件初始化前判断状态是否是新生NEW的
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
//设置组件状态进入 `开始初始化之前`
setStateInternal(LifecycleState.INITIALIZING, null, false);
//不同组件采用不同实现,来完成组件的初始化
initInternal();
//组件状态设置为 初始化结束
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.initFail",toString()), t);
}
}
//不同组件的初始化具体逻辑需要重写
protected abstract void initInternal() throws LifecycleException;
所以,Tomcat每个组件一般都会重写 initInternal、startInternal等模板方法,代码看起来完全不冗余,也符合开闭利于扩展新的组件.