模板方法模式:定义一个操作中算法的骨架,而将一些步骤延迟到子类中。模板方法使子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模板方法是关于怎样将若干个方法集成到一个方法中,以便形成一个解决问题的算法骨架。模板方法模式的关键是在一个抽象类中定义一个算法的骨架,即将若干个方法集成到一个方法中,并称该方法为一个模板方法,或简称模板。模板方法所调用的其他方法通常为抽象的方法,这些抽象方法相当于算法骨架中的各个步骤,这些步骤的实现可以有子类实现。
角色说明:
- 抽象模板:抽象模板定义了若干个方法以表示一个算法的各个步骤,这些方法中有抽象方法也有非抽象方法,其中的抽象方法称作原语操作。重要的一点是,抽象模板中还定义了一个称作模板方法的方法,该方法不仅包含有抽象模板中表示算法步骤的方法调用而且还可以包含定义在抽象模板中的其他对象的方法调用,即模板方法定义了算法的骨架。
- 具体模板:实现抽象模板中的原语操作。
钩子方法
是抽象模板中定义的具体方法,但给出了空实现或默认的实现,并允许子类重写这个具体方法。某些钩子方法的作用是对模板方法中的某些步骤进行 “挂钩” ,即允许具体模板对算法的不同点进行 “挂钩” ,以确定在什么条件下执行模板方法中的哪些算法步骤。这样的钩子方法的类型都是boolean类型的,其默认实现往往是返回值为true。另外一类钩子方法不是用来挂钩的,对于void类型的钩子方法,其默认实现一般为空,具体模板可以根据需要直接继承这样的钩子方法或重写这样的钩子方法。
案例:
SoyaMilk.java
public abstract class SoyaMilk { // 抽象模板(豆浆)
final void make() {
select();
if(test()) {
System.out.print("第二步:");
addCondiments();
}
soak();
beat();
}
void select() {
System.out.println("第一步:选择新鲜黄豆");
}
abstract void addCondiments();
void soak() {
System.out.println("第三步:黄豆和配料开始浸泡,需要三小时");
}
void beat() {
System.out.println("第四步:黄豆和配料放到豆浆机去打碎");
}
//钩子方法
boolean test() {
return true;
}
}
BlackBeanSoyaMilk.java
public class BlackBeanSoyaMilk extends SoyaMilk{ // 具体模板(制作黑豆豆浆)
@Override
void addCondiments() {
System.out.println("加入新鲜红豆");
}
}
PennutSoyaMilk.java
public class PennutSoyaMilk extends SoyaMilk{ // 具体模板(制作花生豆浆)
@Override
void addCondiments() {
System.out.println("加入新鲜花生");
}
}
PureSoyaMilk.java
public class PureSoyaMilk extends SoyaMilk{ // 具体模板(制作纯豆浆)
@Override
void addCondiments() {
// 空实现
}
@Override
boolean test() {
return false; // 确定在该条件下执行模板方法中的哪些算法步骤
}
}
Application5.java
public class Application5 {
public static void main(String[] args) {
SoyaMilk soyaMilk = new BlackBeanSoyaMilk();
soyaMilk.make();
System.out.println("---------------------------");
soyaMilk = new PennutSoyaMilk();
soyaMilk.make();
System.out.println("---------------------------");
soyaMilk = new PureSoyaMilk();
soyaMilk.make();
}
}
运行结果:
优点
- 可以通过在抽象模板中定义模板方法给出成熟的算法步骤,同时又不限制步骤的细节,具体模板实现算法细节不会改变真个算法的骨架。
- 在抽象模板模式中,可以通过钩子方法对某些步骤进行挂钩,具体模板通过钩子可以选择算法骨架中的某些步骤。
适合场景
- 设计者需要给出一个算法的固定步骤,并将某些步骤的具体实现留给子类来实现。
- 需要对代码进行重构,将各个子类公共行为提取出来集中到一个共同的父类中以避免代码重复。
源码剖析模板方法模式
spring的IOC
抽象模板:AbstractApplicationContext.java
模板方法:
public void refresh() 负责容器的初始化操作
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
protected ConfigurableListableBeanFactory obtainFreshBeanFactory()
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
抽象方法:
refreshBeanFactory()
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
getBeanFactory()
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
钩子方法:
postProcessBeanFactory
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
}
onRefresh()
protected void onRefresh() throws BeansException {
// For subclasses: do nothing by default.
}
具体模板:GenericApplicationContext.java
实现抽象模板中的抽象方法
refreshBeanFactory()
protected final void refreshBeanFactory() throws IllegalStateException {
if (!this.refreshed.compareAndSet(false, true)) {
throw new IllegalStateException(
"GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
}
this.beanFactory.setSerializationId(getId());
}
getBeanFactory()
public final ConfigurableListableBeanFactory getBeanFactory() {
return this.beanFactory;
}
具体模板:
AbstractRefreshableApplicationContext.java
实现抽象模板中的抽象方法
getBeanFactory()
public final ConfigurableListableBeanFactory getBeanFactory() {
synchronized (this.beanFactoryMonitor) {
if (this.beanFactory == null) {
throw new IllegalStateException("BeanFactory not initialized or already closed - " +
"call 'refresh' before accessing beans via the ApplicationContext");
}
return this.beanFactory;
}
}
refreshBeanFactory()
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
在以上两个具体模板中都未使用到钩子方法。