【Java设计模式】模板方法+回调函数
1. 模板方法定义
定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。其核心要义在于,搭好一个框架,但部分操作由子类实现
定义父类
public abstract class AbstractClass {
//共同的且繁琐的操作
private void baseOperation() {
//do something
}
//由子类定制的操作
protected abstract void customOperation();
//模板方法定义的框架
public final void templateMethod() {
baseOperation();
customOperation();
}
}
子类代码
public class ConcreteClassOne extends AbstractClass {
@Override
protected void customOperation() {
// do custom things
...
}
}
测试
public class Client {
public static void main(String[] args) {
AbstractClass c1 = new ConcreteClassOne();
AbstractClass c2 = new ConcreteClassTwo();
applyTemplate(c1);
}
public static void applyTemplate(AbstractClass abstractClass) {
abstractClass.execute();
}
}
2. 模板方法优缺点
2.1 优点
- 封装不变部分,扩展可变部分,把认为是不变部分的算法封装到父类实现,而可变部分的则可以通过继承来继续扩展。
- 提取公共部分代码,便于维护
- 符合开闭原则,行为由父类控制,而基本方法是由子类实现的,因此子类可以通过扩展的方式增加相应的功能。
2.2 缺点
- 由子类实现,子类执行的结果影响了父类的结果,也就是子类对父类产生了影响。
- 可能引起子类泛滥和为了继承而继承的问题
3. 模板方法结合回调函数【推荐】
由于模板方法模式存在上述缺点,为了解决这两个问题,利用回调函数代替子类继承是一个很好的解决方案。Template
类仍然只是提供了一个框架,其基本功能和AbstractClass
类似,不同之处在于,Template
不是抽象类,而是一个具体类(一般声明为final类),其代码如下:
public final class Template {
private void baseOperation() {
//公共的操作,比如校验参数、创建连接等等重复的步骤
...
}
public void templateMethod(Callback callback) {
baseOperation();
callback.doInXXX();
}
}
Callback及其子类代码如下:
//函数式声明接口
@FunctionalInterface
public interface Callback {
void doInXXX();
}
测试
public class Client {
public static void main(String[] args) {
Template template = new Template();
template.templateMethod(() -> System.out.println("callback接口具体实现..."));
}
}
3.1 案例一
模板方法结合回调函数在Spring中十分常见,比如JdbcTemplate就是一种典型的应用,JdbcTemplate部分代码
- 获取connection
- 获取statement
- statement 执行方法
- 获取resultset
- 遍历resultset并封装成集合
- 依次关闭connection,statement,resultset,而且还要考虑各种异常
public class JdbcTemplate {
//template method
private final Object execute(StatementCallback action) throws SQLException{
Connection con = HsqldbUtil.getConnection();
Statement stmt = null;
try {
stmt = con.createStatement();
Object result = action.doInStatement(stmt);//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();
}
}
}
//查询方法内,执行模板方法 + StatementCallback内部实现
public Object query(StatementCallback stmt) throws SQLException{
return execute(stmt);
}
}
回调接口
public interface StatementCallback {
Object doInStatement(Statement stmt) throws SQLException;
}
测试
public class Client {
public static void main(String[] args) {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.query(new StatementCallback() {
public Object doInStatement(Statement stmt) throws SQLException {
ResultSet rs = stmt.executeQuery(sql);
List<User> userList = new ArrayList<User>();
User user = null;
while (rs.next()) {
user = new User();
user.setId(rs.getInt("id"));
user.setUserName(rs.getString("user_name"));
userList.add(user);
}
return userList;
}
});
}
}
3.2 案例二
FtpTemplate
public final class FtpTemplate {
private final FtpPool ftpPool;
public FtpTemplate(FtpPool ftpPool) {
this.ftpPool = ftpPool;
}
public <T> T execute(FtpCallback<T> action) throws SftpException {
Assert.notNull(action, "Callback object must not be null");
String hostName = ftpPool.isUniqueHost() ? null : HostsManage.getHostName();
//ftp装饰者设计模式
FtpClientWrapper ftpClientWrapper = null;
try {
//1.从池中借走一个对象
ftpClientWrapper = ftpPool.borrowObject(hostName);
//回调函数
return action.doInSftp(ftpClientWrapper.getFtp());
} finally {
if (ftpClientWrapper != null) {
if (ftpClientWrapper.reset()) {
//2.把对象归还到对象池
ftpPool.returnObject(hostName, ftpClientWrapper);
} else {
//3.无法归还就销毁对象
ftpPool.invalidateObject(hostName, ftpClientWrapper);
}
}
}
}
public void download(String from, String to) throws SftpException {
this.execute(ftpClient -> new FftpWrapper(ftpClient).download(from, to));
}
public void upload(String from, String to) throws SftpException {
this.execute(ftpClient -> new FftpWrapper(ftpClient).upload(from, to));
}
}
FtpCallback
@FunctionalInterface
public interface FtpCallback<T> {
T doInSftp(FTPClient ftp) throws SftpException;
}