一.概述
- 模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern),在一个抽象类中公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
- 简单说,模板方法模式 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤。
- 这种类型的设计模式属于行为型模式。
模板方法设计模式的核心是由抽象父类控制顶级逻辑,而将具体操作的实现推迟到子类中,这是通过继承达到对象的复用。
- 原理类图:
对原理类图的说明-即(模板方法模式的角色及职责)
- AbstractClass 抽象类, 类中实现了模板方法(template),定义了算法的骨架,具体子类需要去实现 其它的抽象方法operationr2,3,4
- ConcreteClass 实现抽象方法operationr2,3,4, 以完成算法中特点子类的步
骤
二.代码演示
/**
* 模板方法模式 需求: 计算1000个整数和1000个字符串相连接的耗时时间.
* 操作模板类:提供了统一的算法骨架
*/
public abstract class OperateTemplete {
/**
* 模板方法
*/
public final long getTotalTime() {
long begin = System.currentTimeMillis();
hookMethod();
doWork();
long end = System.currentTimeMillis();
return end - begin;
}
/**
* 抽象方法:专门留给子类实现的方法(不同的子类实现的细节不同)
*/
protected abstract void doWork();
/**钩子方法*/
public void hookMethod(){ }
}
/**
* 操作int值的类
*/
public class OperateInt extends OperateTemplete {
@Override
protected void doWork() {
int total = 0;
for (int i = 0; i < 1000; i++) {
total = total + i;
}
}
}
/**
* 操作字符串的类
*/
public class OperateString extends OperateTemplete {
@Override
protected void doWork() {
String str = "";
for (int i = 0; i < 1000; i++) {
str = str + i;
}
}
}
public class TestTemplate {
public static void main(String[] args) {
OperateString os = new OperateString();
System.out.println(os.getTotalTime());
OperateInt oi = new OperateInt();
System.out.println(oi.getTotalTime());
}
}
- 模板方法模式的钩子方法: 在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”。像上面OperateTemplete 中的hookMethod()方法就是一个钩子方法。
- 模板方法模式的注意事项和细节
- 基本思想是:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改。
- 实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
- 既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。
- 该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大。
- 一般模板方法都加上final关键字, 防止子类重写模板方法.
- 模板方法模式使用场景:当要完成在某个过程,该过程要执行一系列步骤 ,这一系列的步骤基本相同,但其个别步骤在实现时 可能不同,通常考虑用模板方法模式来处理。
三. JDK中的模板方法模式应用
(1)java.util.AbstractList
(2)java.io.InputStream
(3)java.util.concurrent包中的构建锁或其他同步组件的基础框架java.util.concurrent.locks.AbstractQueuedSynchronizer
四. Spring中的模板方法
- Spring IOC容器初始化时运用到的模板方法模式
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
//声明了一个模板方法
void refresh() throws BeansException, IllegalStateException;
}
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext, DisposableBean {
//模板方法的实现
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
}
}
}
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();//抽象的
ConfigurableListableBeanFactory beanFactory = getBeanFactory
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
}
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
protected final void refreshBeanFactory() throws IllegalStateException{
//子类实现了refreshBeanFactory
// 实现交给子类
}
public final ConfigurableListableBeanFactory getBeanFactory
}
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
//也实现了 refreshBeanFactory 和 getBeanFactory
//它的子类,就可以按它的实现方式来getBeanFactory
//比如 ClassPathXmlApplicationContext
}
Spring几乎所有的外接扩展,都采用模板方法模式,但Spring并不是像经典的模板方法模式那样需要继承抽象模板父类,而是通过实现回调接口。如Spring在对JDBC API的抽象和对Hibernate,iBatis等ORM的集成,全部都采用了一种理念或者处理方式,那就是模板方法模式与相应的Callback(回调)接口相结合。
这里以Spring对Hibernate的扩展说明。
模板方法类HibernateTemplate的模板方法execute():
public class HibernateTemplate implements HibernateOperations, InitializingBean {
@Override
@Nullable
public <T> T execute(HibernateCallback<T> action) throws DataAccessException {
return doExecute(action, false);
}
}
@FunctionalInterface
public interface HibernateCallback<T> {
@Nullable
T doInHibernate(Session session) throws HibernateException;
}
在HibernateTemplate类中实现HibernateCallback接口的却是匿名类.
Spring模板方法设计模式适用于模板类有很多方法,如HibernateTemplate,数据库操作使用的如此频繁,一个操作就需要通过继承一个子类,这显然不可能,这时使用经典的模板方法模式则显笨重。Spring使用Callback模式与模板方法模式配合,既达到了代码复用的效果,同时增加了灵活性,只需实现某些CallBack就可轻松订制Template。
无侵入设计思想,依赖优于继承。
经典的模板方法设计模式适用于如果每个具体的步骤都需要真正去具体实现,而不是简单的改变参数或设置某个对象就ok的话,使用Callback很难去订制,因为你可能需要传递多个Callback作为参数,并让用户去实现,使用Java的内部类本来就是一个比较丑陋的语法,更何况参数是多个。
五.代码演示的改造
public class OperateTemplete {
/**
* 模板方法
*/
public final long getTotalTime(OperationCallBack operationCallBack) {
long begin = System.currentTimeMillis();
operationCallBack.operate();
long end = System.currentTimeMillis();
return end - begin;
}
public static void main(String[] args) {
OperateTemplete operateTemplete = new OperateTemplete();
long totalTime = operateTemplete.getTotalTime(() -> {
int total = 0;
for (int i = 0; i < 1000; i++) {
total = total + i;
}
});
System.out.println(totalTime);
long totalTime2 = operateTemplete.getTotalTime(() -> {
String str = "";
for (int i = 0; i < 1000; i++) {
str = str + i;
}
});
System.out.println(totalTime2);
}
}
@FunctionalInterface
public interface OperationCallBack {
void operate();
}