模板模式
模板模式(Template Pattern),定义一个操作中算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变算法的结构,只是重定义该算法的某些特定步骤。这种类型的设计模式属于行为型模式。
模板模式解决的问题
一些方法通用,却在每一个子类都重新写了这一方法,带来大量重复的代码的问题。相同的部分父类给出统一的模板,不同的部分,子类进行重写。
命令模式模式角色
AbstractTemplate(抽象类):在抽象类中定义了一系列基本操作,这些基本操作可以是具体的,也可以是抽象的,每一个基本操作对应算法的一个步骤,在其子类中可以重定义或实现这些步骤。同时,在抽象类中实现了一个模板方法(Template Method),用于定义一个算法的框架,模板方法不仅可以调用在抽象类中实现的基本方法,也可以调用在抽象类的子类中实现的基本方法,还可以调用其他对象中的方法。
ConcreteTemplate(具体子类):它是抽象类的子类,用于实现在父类中声明的抽象基本操作以完成子类特定算法的步骤,也可以覆盖在父类中已经实现的具体基本操作。
模板抽象类方法类型
抽象方法:在抽象类声明、由其具体子类实现。
具体方法:在抽象类中声明并实现,在具体子类中可以继承或重写。
钩子方法:在抽象类中声明并实现(可以是空的实现),子类视情况进行重写覆盖,一般是逻辑判断方法。
模板方法:定义统一模板算法的方法,提供实现方法,子类一般不需要重写。
模板模式和策略模式的区别
模板模式定义了一个统一的算法,子类可以对算法的某一些步骤,进行覆盖重写,可以提高代码的复用率。同过继承的方式实现,依赖比较强,不够灵活。策略模式每个子类都是一种实现,可以完全不相同,即便算法之间存在共性,也需要在每个子类重新实现,通过聚合的方式实现,可以动态的改变算法,比较灵活。
代码实现
/**
* 抽象模板
*/
public abstract class Game {
/**
* 具体方法
*/
public void initialize(){
System.out.println("game initialize .........");
}
/**
* 抽象方法
*/
abstract void startPlay();
abstract void endPlay();
/**
* 钩子方法
* @return 是否需要结束
*/
boolean isNeedEnd(){
return false;
}
/**
* 模板方法
* final修饰 不允许子类修改
* 每一个游戏先初始化、开始游戏 是否需要结束游戏 根据钩子进行判断
*/
public final void play(){
//初始化游戏
initialize();
//开始游戏
startPlay();
if(isNeedEnd()){
//结束游戏
endPlay();
}
}
}
/**
* 具体模板
* 足球游戏
*/
public class Football extends Game{
@Override
void endPlay() {
System.out.println("Football Game Finished!");
}
@Override
void startPlay() {
System.out.println("Football Game Started. Enjoy the game!");
}
@Override
boolean isNeedEnd() {
return false;
}
}
/**
* 模板测试类
*/
public class TemplateModeTest {
public static void main(String[] args) {
Game game = new Basketball();
game.play();
game = new Football();
game.play();
}
}
运行结果:
JDK和框架中的模板模式
public <T> T execute(StatementCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Connection con = DataSourceUtils.getConnection(getDataSource());
Statement stmt = null;
try {
Connection conToUse = con;
if (this.nativeJdbcExtractor != null &&
this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {
conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
}
stmt = conToUse.createStatement();
applyStatementSettings(stmt);
Statement stmtToUse = stmt;
if (this.nativeJdbcExtractor != null) {
stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);
}
T result = action.doInStatement(stmtToUse);
handleWarnings(stmt);
return result;
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.
JdbcUtils.closeStatement(stmt);
stmt = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);
}
finally {
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
public boolean isSkipResultsProcessing() {
return this.skipResultsProcessing;
}
spring中的JdbcTemplate、RedisTemplate、RestTemplate都是典型的模板模式,都是继承Accessor类和实现Operations接口。
优缺点
优点:封装不变部分,扩展可变部分。 提取公共代码,便于维护。 行为由父类控制,子类实现。
缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
生活中的命令模式
PPT模板,从网上下载自己喜欢的PPT模板,在不改变大的整体框架的情况下,替换掉部分内容,比如文字内容、部分图片内容,可以很大程度提高产出率。
机器学习中的迁移学习,也是一个典型的模板模式,机器学习中提供了很多模板模型,VGG19、ResNet50、InceptionV3,如果我们自己训练这个模型需要花很长的时间,几周或者更长的时间,可以把别人训练好的模型作为模板,做一些细微的调整,去训练我们自己的样本数据,得到一个适合我们特有数据的模型,只需要训练比较短的时间,就能得到一个比较好的模型。
我的启发
模板模式就像站在巨人的肩膀上,在大方向上借鉴别人的经验,进行适当的调整,以满足我们自己定制化的需求。当我们不具备造轮子的能力的时候,合理选择适合的模板也是一种不错的选择。
设计模式系列文章历史
Head First 设计模式之装饰器模式,因为参与,所以认同