模板方法模式与回调函数

一 模板方法模式

模板方法模式(Template Method Pattern)是设计模式中比较简单的一种,但在工作中使用的场景却比较多,面试时也会被经常被问到,其基本定义如下:

Define the skeleton of an algorithm in an operation,deferring some steps tosubclasses.Template Method lets subclasses redefine certain steps of analgorithm without changing the algorithm’s structure.(定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。)

其核心要义在于,搭好一个框架,但部分操作由子类实现,类图如下:
模板方法模式类图

所谓的框架就是这个抽象类:AbstractClass,这个类的作用只是一个框架,所以不能被单独使用,其基本结构如下面代码所示:

public abstract class AbstractClass {
	// 共同的且繁琐的操作
    private void baseOperation() {
        // do something
    }
	
	// 由子类定制的操作
    protected abstract void customOperation();
    
    // 模板方法定义的框架
    public final void templateMethod() {
        baseOperation();
        customOperation();
    }
}

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.templateMethod();
    }
}

二 模板方法模式的优缺点

2.1 优点

封装不变部分,扩展可变部分,把认为是不变部分的算法封装到父类实现,而可变部分的则可以通过继承来继续扩展。

提取公共部分代码,便于维护

符合开闭原则,行为由父类控制,而基本方法是由子类实现的,因此子类可以通过扩展的方式增加相应的功能。

2.2 缺点

  • 由子类实现,子类执行的结果影响了父类的结果,也就是子类对父类产生了影响。
  • 可能引起子类泛滥和为了继承而继承的问题

三 模板方法结合回调函数

由于模板方法模式存在上述缺点,为了解决这两个问题,利用回调函数代替子类继承是一个很好的解决方案。其类图如下:
在这里插入图片描述

此时,Template类仍然只是提供了一个框架,其基本功能和AbstractClass类似,不同之处在于,Template不是抽象类,而是一个具体类(一般声明为final类),其代码如下:

public final class Template {

    private void baseOperation() {
        // do  something
    }

    public void templateMethod(Callback callback) {
        baseOperation();
        callback.customOperation();
    }
}

Callback及其子类代码如下:

public interface Callback {
    void customOperation();
}

public class SubCallback implements Callback {
    @Override
    public void customOperation() {
        // do custom things
    }
}

客户端类变化也很小,其代码如下:

public class Client {
    public static void main(String[] args) {
        Template template = new Template();
        applyTemplate(template);
    }

    public static void applyTemplate(Template template) {
        Callback c1  = new SubCallback();
        template.templateMethod(c1);
    }
}

这里我们可以看到,结合回调函数以后,Template是一个final类,无法被继承,也就不存在子类行为影响父类结果的问题,而Callback是一个接口,为了继承而继承的问题消失了。

四 应用

模板方法结合回调函数在Spring中十分常见,比如JdbcTemplate就是一种典型的应用,JdbcTemplate部分代码如下:

public class JdbcTemplate {
	public <T> T execute(StatementCallback<T> action) {
	
		Connection con = getConnection();
		Statement stmt = null;
		try {
			stmt = con.createStatement();
			applyStatementSettings(stmt);
			// 调用Callback的自定义行为
			T result = action.doInStatement(stmt);
			handleWarnings(stmt);
			return result;
		}
		catch (SQLException ex) {
			// 异常处理
		}
		finally {
			JdbcUtils.closeStatement(stmt);
			DataSourceUtils.releaseConnection(con, getDataSource());
		}
	}
}


// Callback接口定义
@FunctionalInterface
public interface StatementCallback<T> {
	@Nullable
	T doInStatement(Statement stmt) throws SQLException, DataAccessException;
}



// JdbcTemplate提供的一个默认callback实现

class ExecuteStatementCallback implements StatementCallback<Object>, SqlProvider {
	
	public Object doInStatement(Statement stmt) throws SQLException {
		stmt.execute(sql);
		return null;
	}
	// 以下省略。。。		
}

版权声明:本文为u013241673原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。