深入解析模板模式,提升代码复用性,简化复杂流程管理
模板模式 是一种行为型设计模式,它定义了算法的骨架,而将某些步骤的具体实现延迟到子类中。通过这种方式,模板方法允许子类在不改变算法结构的情况下,重新定义算法中的某些步骤。
核心思想:
模板模式提供了一个通用的算法框架,并允许子类在不改变框架的前提下,覆盖或扩展算法的某些步骤。父类定义了算法的骨架,而子类负责具体实现某些步骤。
在很多优秀的框架和源码中都有应用,比如Spring框架中的IOC使用到了模板模式
案例:咖啡和茶的制作 ☕🍵
我们可以用模板模式来模拟咖啡和茶的制作过程。制作咖啡和茶的流程基本相似,例如:
- 烧水
- 浸泡饮料
- 倒入杯中
- 加入调料
尽管制作步骤相同,但细节不同。比如,咖啡需要加入牛奶和糖,而茶可能会加入柠檬。因此,我们可以定义一个通用的模板,子类根据具体饮品重写部分步骤。
场景:
- 通用算法:制作饮品的基本流程。
- 可变部分:具体浸泡的饮品(茶或咖啡)和加入的调料。
模板模式 UML 类图
类图解释:
- Beverage(抽象类):定义了通用的模板方法
prepareRecipe()
,并包含算法的骨架。 - Coffee 和 Tea(具体实现类):分别实现
brew()
和addCondiments()
,表示咖啡和茶的具体制作步骤。 - 模板方法:
prepareRecipe()
是模板方法,定义了算法的框架,而某些步骤(如brew()
和addCondiments()
)由子类实现。
代码实现
Step 1: 创建抽象模板类 Beverage
// 抽象模板类
public abstract class Beverage {
// 模板方法,定义制作饮料的步骤
public final void prepareRecipe() {
boilWater();
brew(); // 留给子类实现
pourInCup();
addCondiments(); // 留给子类实现
}
// 通用方法:烧水
public void boilWater() {
System.out.println("Boiling water...");
}
// 通用方法:倒入杯中
public void pourInCup() {
System.out.println("Pouring into cup...");
}
// 抽象方法:浸泡饮品,子类实现
public abstract void brew();
// 抽象方法:添加调料,子类实现
public abstract void addCondiments();
}
Step 2: 实现具体的 Coffee
类
// 具体类 Coffee,继承 Beverage
public class Coffee extends Beverage {
@Override
public void brew() {
System.out.println("Brewing coffee...");
}
@Override
public void addCondiments() {
System.out.println("Adding sugar and milk...");
}
}
Step 3: 实现具体的 Tea
类
// 具体类 Tea,继承 Beverage
public class Tea extends Beverage {
@Override
public void brew() {
System.out.println("Steeping the tea...");
}
@Override
public void addCondiments() {
System.out.println("Adding lemon...");
}
}
Step 4: 测试模板方法模式
public class TemplateMethodPatternDemo {
public static void main(String[] args) {
// 制作咖啡
Beverage coffee = new Coffee();
System.out.println("\nMaking coffee:");
coffee.prepareRecipe();
// 制作茶
Beverage tea = new Tea();
System.out.println("\nMaking tea:");
tea.prepareRecipe();
}
}
输出结果:
Making coffee:
Boiling water...
Brewing coffee...
Pouring into cup...
Adding sugar and milk...
Making tea:
Boiling water...
Steeping the tea...
Pouring into cup...
Adding lemon...
解释:
prepareRecipe()
是模板方法,它定义了制作饮品的步骤。具体的饮品制作方法(如brew()
和addCondiments()
)由子类实现。Coffee
和Tea
分别实现了具体的饮品制作过程。- 模板方法的复用:
boilWater()
和pourInCup()
这些通用步骤在模板类中实现,避免了重复代码。
钩子方法优化
在模板模式中,钩子方法(Hook Method) 是一个定义在抽象类中的方法,通常提供一个默认实现(或空实现),子类可以根据需要选择性地覆盖它。钩子方法使模板模式更加灵活,允许子类控制算法的执行步骤,而不强制要求所有子类都实现它。
钩子方法的应用场景:
在饮品制作的例子中,某些饮品不需要添加调料(如有些茶或咖啡不加糖、不加柠檬等),我们可以通过钩子方法来灵活控制是否添加调料,而不是每次都强制执行这一步骤。
优化的模板模式:代码实现
Step 1: 在模板类中添加钩子方法
我们在 Beverage
抽象类中添加一个钩子方法 customerWantsCondiments()
,用来决定是否执行 addCondiments()
步骤。默认情况下,钩子方法返回 true
,表示会添加调料。
// 抽象模板类
public abstract class Beverage {
// 模板方法,定义制作饮料的步骤
public final void prepareRecipe() {
boilWater();
brew(); // 留给子类实现
pourInCup();
if (customerWantsCondiments()) { // 钩子方法控制是否添加调料
addCondiments(); // 留给子类实现
}
}
// 通用方法:烧水
public void boilWater() {
System.out.println("Boiling water...");
}
// 通用方法:倒入杯中
public void pourInCup() {
System.out.println("Pouring into cup...");
}
// 抽象方法:浸泡饮品,子类实现
public abstract void brew();
// 抽象方法:添加调料,子类实现
public abstract void addCondiments();
// 钩子方法,子类可选择性覆盖
public boolean customerWantsCondiments() {
return true; // 默认返回 true,表示需要添加调料
}
}
Step 2: 修改具体的 Coffee
类和 Tea
类
具体的 Coffee
和 Tea
类可以选择是否重写 customerWantsCondiments()
方法。如果不想添加调料,可以重写该钩子方法并返回 false
。
实现 Coffee
类
如果咖啡需要添加调料,Coffee
类不需要覆盖钩子方法,使用默认实现。
// 具体类 Coffee,继承 Beverage
public class Coffee extends Beverage {
@Override
public void brew() {
System.out.println("Brewing coffee...");
}
@Override
public void addCondiments() {
System.out.println("Adding sugar and milk...");
}
}
实现 Tea
类
假设我们有一种特殊的茶不需要添加调料,那么我们可以通过覆盖钩子方法,返回 false
来跳过添加调料步骤。
// 具体类 Tea,继承 Beverage
public class Tea extends Beverage {
@Override
public void brew() {
System.out.println("Steeping the tea...");
}
@Override
public void addCondiments() {
System.out.println("Adding lemon...");
}
// 覆盖钩子方法,不需要添加调料
@Override
public boolean customerWantsCondiments() {
return false; // 不添加调料
}
}
Step 3: 测试优化后的模板模式
public class TemplateMethodWithHookDemo {
public static void main(String[] args) {
// 制作咖啡(默认添加调料)
Beverage coffee = new Coffee();
System.out.println("\nMaking coffee:");
coffee.prepareRecipe();
// 制作茶(不添加调料)
Beverage tea = new Tea();
System.out.println("\nMaking tea:");
tea.prepareRecipe();
}
}
输出结果:
Making coffee:
Boiling water...
Brewing coffee...
Pouring into cup...
Adding sugar and milk...
Making tea:
Boiling water...
Steeping the tea...
Pouring into cup...
钩子方法 customerWantsCondiments()
:在模板类 Beverage
中加入了钩子方法,控制是否执行添加调料的步骤。
Coffee
类:默认需要添加调料,所以不重写钩子方法,使用了默认的 true
。
Tea
类:通过覆盖钩子方法并返回 false
,跳过了添加调料的步骤,灵活控制了制作流程。
通过引入钩子方法,模板模式的灵活性得到了提升。钩子方法允许子类在不改变整体算法框架的情况下控制特定步骤的执行,从而更灵活地定制算法。在饮品制作的例子中,我们通过钩子方法实现了灵活控制是否添加调料,展现了模板模式的实用性和灵活性。
Spring IOC 源码中的模板模式应用详解
Spring IoC 容器(依赖注入机制)中使用了模板模式,特别是在Bean 的初始化和实例化流程中,Spring 通过模板模式封装了通用的处理逻辑,子类可以根据需求定制特定的行为。
Spring 使用模板模式的一个主要场景是Bean 的创建、初始化、销毁的整个生命周期管理。Spring 提供了通用的处理流程,而将特定的实现(如依赖注入、生命周期回调等)延迟到子类或扩展点。
1. 模板模式在 Spring IoC 容器中的应用
在 Spring 的 IoC 容器 中,AbstractBeanFactory
是核心的抽象类,它定义了获取和创建 Bean 的通用模板方法。这些模板方法封装了常见的 Bean 初始化流程,如:
- 依赖注入
- Bean 的生命周期回调
- 代理机制
而具体的 Bean 创建和初始化行为则交由子类实现或扩展,如 DefaultListableBeanFactory
。这种设计很好地体现了模板模式的思想:定义通用流程,延迟部分步骤的实现。
2.Spring IOC中的AbstractBeanFactory与模板模式
AbstractBeanFactory
类的结构
在 Spring 中,AbstractBeanFactory
是 BeanFactory
的一个抽象实现,定义了 Bean 获取和创建的通用逻辑,而具体的创建逻辑则由子类实现。它提供了一个通用的 getBean()
模板方法,并将具体的 Bean 实例化和属性注入等操作交给其子类完成。
源码片段:
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false); // 调用模板方法
}
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
Object sharedInstance = getSingleton(name); // 从单例缓存中获取 Bean
if (sharedInstance != null) {
return (T) getObjectForBeanInstance(sharedInstance, name, name, null); // 返回共享实例
}
// 创建 Bean 实例
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
final String[] dependsOn = mbd.getDependsOn();
// 调用子类实现的创建 Bean 的逻辑
T bean = (T) createBean(beanName, mbd, args);
return bean;
}
protected abstract Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException;
}
解释:
getBean()
是一个模板方法,它定义了获取 Bean 的通用流程,包括从缓存中获取、创建 Bean 等通用操作。doGetBean()
是核心的模板方法,控制了整个 Bean 获取的逻辑。而createBean()
是抽象方法,由子类实现具体的 Bean 创建过程。
3.AbstractAutowireCapableBeanFactory 类与模板模式
AbstractAutowireCapableBeanFactory
是 AbstractBeanFactory
的一个子类,负责创建 Bean 实例并执行依赖注入。它实现了模板方法 createBean()
,并通过调用一系列钩子方法(如属性注入、生命周期回调等)来完成 Bean 的创建流程。
源码片段:
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
// 创建 Bean 的实例
BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
// 执行属性注入
populateBean(beanName, mbd, instanceWrapper);
// 初始化 Bean(包括调用生命周期方法)
Object exposedObject = initializeBean(beanName, instanceWrapper.getWrappedInstance(), mbd);
return exposedObject;
}
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
// 执行属性注入的逻辑
}
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
// 调用 Bean 的初始化方法,支持 AOP、回调等功能
return bean;
}
}
解释:
createBean()
是一个模板方法,定义了创建和初始化 Bean 的流程。具体步骤包括创建实例、注入依赖、调用初始化方法等。populateBean()
是一个钩子方法,用于执行依赖注入。子类可以重写这个方法,提供自定义的依赖注入逻辑。initializeBean()
负责调用 Bean 的初始化方法以及支持 AOP 代理等操作。
4. 钩子方法与扩展点
在 Spring IoC 容器中,AbstractAutowireCapableBeanFactory
类通过钩子方法提供了一系列扩展点,开发者可以通过这些钩子自定义 Bean 的创建流程。例如:
populateBean()
:允许自定义依赖注入逻辑。initializeBean()
:允许在 Bean 初始化时执行自定义操作,例如调用自定义的初始化方法或进行 AOP 增强。
Spring 还支持 Bean 生命周期的回调方法(如 afterPropertiesSet()
、@PostConstruct
),这些操作也是通过钩子方法实现的,进一步体现了模板模式的灵活性。
总结
优点:
- 代码复用:通过将通用的算法骨架放在抽象类中,模板模式让子类共享算法的基本结构,减少了代码重复。
- 灵活性:子类可以覆盖或扩展算法中的某些步骤,而不需要修改整体算法的结构。
- 遵循开闭原则:模板方法提供了一个可扩展的框架,子类通过扩展而非修改来实现算法的具体步骤。
缺点:
- 难以应对复杂变化:如果算法步骤之间存在较强的耦合,模板模式可能难以应对复杂的变化。
- 增加代码复杂性:引入抽象类和模板方法后,代码的结构变得更复杂,特别是当算法的步骤较多时。
模板模式的使用场景
- 具有固定流程的系统:当系统中存在某些具有固定流程的操作,但具体某些步骤需要根据具体情况进行实现时(如游戏中玩家的攻击流程、文件处理流程等)。
- 工作流引擎:在一些工作流引擎中,可以用模板方法来定义流程的基本框架,不同类型的流程可以覆盖具体的执行步骤。
- 框架开发:在框架设计中,模板模式被广泛使用,框架提供基本的操作流程,开发者可以根据需求扩展具体操作。
模板模式通过定义算法的固定骨架,并允许子类定制具体实现,是一种常见的行为型设计模式。它能够有效地减少代码重复,提升代码的可扩展性。在许多具有固定流程或框架设计中,模板模式提供了一种优秀的解决方案。