定义
一个操作中算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤。
通俗点的理解就是 :完成一件事情,有固定的数个步骤,但是每个步骤根据对象的不同,而实现细节不同;就可以在父类中定义一个完成该事情的总方法,按照完成事件需要的步骤去调用其每个步骤的实现方法。每个步骤的具体实现,由子类完成。
模板设计模式常在数据库操作中使用,我现在使用模板模式做一个JDBC的查询模板:
(1) 抽象查询父类
public abstract class AbstractDao {
/**
* 查询
* @param sql
* @param params
* @return
*/
protected Object find(String sql, Object[] params) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
Object obj = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
rs = ps.executeQuery();
while (rs.next()) {
obj = rowMapper(rs);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.free(rs, ps, conn);
}
return obj;
}
protected abstract Object rowMapper(ResultSet rs) throws SQLException;
//同时可以添加 insert ,update 等方法
}
(2)具体的UserDao
/**
* userDao
*
* @author aries
*
*/
public class UserDao extends AbstractDao {
public User findUser(int userId) {
String sql = "select * from t_user where userId = ?";
Object[] params = new Object[] { userId };
Object user = super.find(sql, params);
System.out.println((User) user);
return (User) user;
}
@Override
protected Object rowMapper(ResultSet rs) throws SQLException {
User user = new User();
user.setId(rs.getInt("userId"));
user.setName(rs.getString("name"));
user.setAge(rs.getInt("age"));
user.setSex(rs.getString("sex"));
user.setAddress(rs.getString("address"));
return user;
}
}
(3)以上代码中用到的User类和JDBCUtil
public class JDBCUtils {
private static String url = "jdbc:mysql://localhost:3306/jdbcstudy";
private static String user = "root";
private static String password = "123";
private JDBCUtils() {
}
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e);
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
public static void free(ResultSet rs, PreparedStatement ps, Connection conn){
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(ps != null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
/**
* 用户类
*
* @author aries
*
*/
public class User {
private Integer id;
private String name;
private Integer age;
private String sex;
private String address;
//set...get省略
}
模板模式的优点
具体细节步骤实现定义在子类中,子类定义详细处理算法是不会改变算法整体结构。
代码复用的基本技术,在数据库设计中尤为重要。
存在一种反向的控制结构,通过一个父类调用其子类的操作,通过子类对父类进行扩展增加新的行为,符合“开闭原则”。
不足
每个不同的实现都需要定义一个子类,会导致类的个数增加,系统更加庞大。
使用场景
有多个子类共有的方法,且逻辑相同
重要的、复杂的方法,可以考虑作为模板方法
重构时,模板方法模式是一个经常使用到的模式,把相同的代码抽取到父类中,通过钩子函数约束其行为
应用实例
做试卷,大家题目都是一样的,只是答案不同
对于汽车,车从发动到停车的顺序是相同的,不同的是引擎声、鸣笛声等
造房时,地基、走线、水管都一样,只有在建筑后期才有差异
注意事项
为防恶意操作,一般模板方法都加上final关键字
模板模式与策略模式的异同
(1)相似:
策略和模板方法模式都可以用来满足开闭原则,使得软件模块在不改变代码的情况下易于扩展。
两种模式都表示通用function与该function的详细实现的分离。不过,它们所提供的粒度有一些差异。
(2)差异:
在策略中,客户和策略之间的耦合更加松散,而在模板方法中,两个模块耦合得更紧密。
在策略中,虽然抽象类也可以根据具体情况而使用,但大多使用一个接口,而不使用具体类,而在Template方法中大多使用抽象类或具体类,不使用接口。
在Strategy模式中,类的整体行为一般用接口表示,另一方面,Template方法用于减less代码重复,样板代码在基本框架或抽象类中定义。 在Template Method中,甚至可以有一个具有默认实现的具体类。
简而言之,您可以在策略模式中更改整个策略(algorithm),但是在Template模式中,只有一些事情发生变化(algorithm的一部分),而其余事件保持不变。 在Template Method中,不变步骤是在一个抽象基类中实现的,而变体步骤要么是默认的实现,要么根本就没有实现。 在Template方法中,组件devise器强制执行algorithm所需的步骤和步骤的sorting,但允许组件客户端扩展或replace某些步骤。