模板方法设计模式
模板方法模式(Template),定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可以重新定义该算法的某些特定步骤。
它在一个抽象类中公开定义了执行它的方法模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
它属于行为型设计模式
模板方法模式是通过把不变的行为搬移到超类(父类),去除子类中的重复代码来体现它的优势。
模板方法模式提供了一个很好的代码复用平台,因为有些时候,我们会遇到由一系列步骤构成的过程需要执行。这个过程从高层次上看是相同的,但是有些步骤的实现可能不同。这时候我们通常就要考虑使用这个模式了。
当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。我们可以通过模板方法模式将这些行为搬移到单一的地方,这样就可以帮助子类拜托重复的不变行为带来的纠缠。
模板方法模式解决豆浆制作问题
应用实例要求 编写制作豆浆的程序
说明如下: 制作豆浆的流程 选材—>添加配料—>浸泡—>放到豆浆机打碎
通过添加不同的配料,可以制作出不同口味的豆浆 选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的(红豆、花生豆浆。。。)
模板方法模式的钩子方法
在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”。
模板方法模式的注意事项和细节
- 基本思想是:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方 法或者已经实现的某些步骤,子类就会继承这些修改
- 实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
- 既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步 骤的实现。
- 该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大
- 一般模板方法都加上 final 关键字, 防止子类重写模板方法.
- 模板方法模式使用场景:当要完成在某个过程,该过程要执行一系列步骤 ,这一系列的步骤基本相同,但其 个别步骤在实现时 可能不同,通常考虑用模板方法模式来处理
在Spring中的应用
经典模板方法定义:
父类定义了骨架(调用了哪些方法以及顺序),某些特定的方法由子类实现。
最大的好处:代码复用,减少重复代码。除了子类要实现的特定方法,其他方法及方法调用顺序都在父类中定义好了。
⽗类模板⽅法中有两类⽅法:
共同的⽅法: 所有⼦类都会⽤到的代码
不同的⽅法: ⼦类要覆盖的⽅法,分两种:
- 抽象⽅法:⽗类中的是抽象⽅法,⼦类必须覆盖
- 钩⼦⽅法:⽗类中是⼀个空⽅法,⼦类继承了默认也是空的
注:为什么叫钩⼦,⼦类可以通过这个钩⼦(⽅法),控制⽗类,因为这个钩⼦实际是⽗类的⽅法(空⽅法)!
Spring模板⽅法模式实质:
是模板⽅法模式和回调模式的结合,是Template Method不需要继承的另⼀种实现⽅式。Spring⼏乎所有的外接扩展都采⽤这 种模式。
具体实现:
JDBC的抽象和对Hibernate的集成,都采⽤了⼀种理念或者处理⽅式,那就是模板⽅法模式与相应的Callback接⼝相结合。 采⽤模板⽅法模式是为了以⼀种统⼀⽽集中的⽅式来处理资源的获取和释放,以JdbcTempalte为例:
public abstract class JdbcTemplate {
public final Object execute(String sql) {
Connection con = null;
Statement stmt = null;
try {
con = getConnection();
stmt = con.createStatement();
Object retValue = executeWithStatement(stmt, sql);
return retValue;
} catch (SQLException e) {
...
} finally{
closeStatement(stmt);
releaseConnection(con);
}
}
protected abstract Object executeWithStatement(Statement stmt, String sql);
}
引⼊回调原因:
JdbcTemplate是抽象类,不能够独⽴使⽤,我们每次进⾏数据访问的时候都要给出⼀个相应的⼦类实现,这样肯定不⽅便,所以 就引⼊了回调。
回调代码
public interface StatementCallback{
Object doWithStatement(Statement stmt);
}
利⽤回调⽅法重写JdbcTemplate⽅法
public class JdbcTemplate {
public final Object execute(StatementCallback callback) {
Connection con = null;
Statement stmt = null;
try {
con = getConnection();
stmt = con.createStatement();
Object retValue = callback.doWithStatement(stmt);
return retValue;
} catch (SQLException e) {
...
} finally {
closeStatement(stmt);
releaseConnection(con);
}
}
...//其它⽅法定义
}
Jdbc使⽤⽅法如下:
JdbcTemplate jdbcTemplate =...;
final String sql=...;
StatementCallback callback=new n StatementCallback(){
public Object=doWithStatement(Statement stmt){
return ...;
}
}
jdbcTemplate.execute(callback);
为什么JdbcTemplate没有使⽤继承?
因为这个类的⽅法太多,但是我们还是想⽤到JdbcTemplate已有的稳定的、公⽤的数据库连接,那么我们怎么办呢?
我们可以把变化的东西抽出来作为⼀个参数传⼊JdbcTemplate的⽅法中。但是变化的东西是⼀段代码,⽽且这段代码会⽤到 JdbcTemplate中的变量。怎么办?
那我们就⽤回调对象吧。在这个回调对象中定义⼀个操纵JdbcTemplate中变量的⽅法,我们去实现这个⽅法,就把变化的东西 集中到这⾥了。然后我们再传⼊这个回调对象到JdbcTemplate,从⽽完成了调⽤。