乐于分享
好东西不私藏

模板方法模式

模板方法模式

一、定义

模版方法模式(Template Method Pattern)又叫模版模式是一种行为型模式,它在一个方法中定义了一个算法的骨架,并允许子类为一个或多个步骤提供实现。模版方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

       1、固定骨架:将通用的、不变的流程(如清洗、装罐)在父类中实现。
       2、延迟实现:将特定的、变化的步骤(如水果预处理、熬煮时间)在子类中实现。   
           场景理解:在“水果罐头”场景中,制作罐头的整体流程是固定的(清洗 -> 预处理 -> 熬煮 -> 装罐)。但是,苹果需要“去皮去核”,而黄梨需要“去核切块”且熬煮时间不同并加了特制处理。模版方法模式可以保证流程统一,同时让具体水果实现各自的细节。

二、核心思想

将公共的行为抽取到父类中,而将差异化的行为留给子类去实现

  • 代码复用:公共逻辑只写一次,避免重复代码。
  • 流程控制:父类控制核心流程的执行顺序,子类无法篡改。
  • 钩子方法:允许子类通过重写钩子方法,反向控制父类的流程(例如:黄梨罐头需要加柠檬汁防氧化,而苹果不需要)。
  • 抽象类(AbstractClass)= 罐头制作模版(定义流程骨架)。
  • 具体类(ConcreteClass) = 具体的罐头产品(如:苹果罐头、黄梨罐头)。

三、使用场景

模版方法模式适用于以下情况

1、多个子类有共有的方法,并且逻辑基本相同。

2、重要、复杂的算法,可以把核心算法设计为模版方法,周边的相关细节功能则由各个子类实现。

3、重构时,经常使用的方法。将相同的代码抽取到父类中,然后通过钩子函数约束其行为。

四、主要角色

角色

职责

示例

抽象类 (AbstractClass)

定义抽象的原语操作,实现一个模版方法作为算法的骨架。

CannedFruitTemplate

 (水果罐头模版)

具体类 (ConcreteClass)

实现父类定义的抽象方法,以完成算法中与特定子类相关的步骤。

CannedApple

, 

CannedYellowPeaches

五、普通方式实现

/** * @ClassName SimpleCannedFruit * @Description: 普通放实现制作罐头 * @Author 公众号-小书浓 * @Date 2026/5/6 * @Version V1.0 */public class SimpleCannedFruit {// 普通方法:制作苹果罐头public void makeCannedAppleApple() {System.out.println("--- 制作苹果罐头 ---");System.out.println("1. 清洗苹果...");System.out.println("2. 苹果去皮、去核...");System.out.println("3. 放入糖水中煮15分钟...");System.out.println("4. 高温杀菌并密封装罐...");System.out.println("--- 制作完成 ---\n");}// 普通方法:制作黄梨罐头public void makeCannedYellowPeaches() {System.out.println("--- 制作黄梨罐头 ---");System.out.println("1. 清洗黄梨...");System.out.println("2. 黄梨去核、切成四瓣...");System.out.println("3. 放入糖水中煮20分钟...");System.out.println("4. 黄梨罐头中滴入几滴新鲜柠檬汁...");System.out.println("5. 高温杀菌并密封装罐...");System.out.println("--- 制作完成 ---\n");}}

六、模板方法实现

代码实现

1、模版抽象类

/** * @ClassName CannedFruitTemplate * @Description: 模版方法抽象类定义算法骨架 * @Author 公众号-小书浓 * @Date 2026/5/6 * @Version V1.0 */public abstract class CannedFruitTemplate {// 模版方法:定义制作罐头的固定流程骨架(final防止子类篡改流程)public final void makeCannedFruit() {prepareFruit();// 准备水果(可变步骤,交给子类)wash();// 清洗(固定步骤)cook();// 熬煮/处理(可变步骤,交给子类)// 钩子方法控制:根据水果特性决定是否加特制料if (needExtraIngredients()) {    addExtraIngredients();}
// 装罐密封(固定步骤)
canning();                System.out.println("--- " + getFruitName() + 
"罐头制作完成!---\n");}// 抽象方法:准备水果(子类必须实现)protected abstract void prepareFruit();// 抽象方法:熬煮/处理(子类必须实现)protected abstract void cook();// 抽象方法:获取水果名称(用于打印信息)protected abstract String getFruitName();// 固定步骤:清洗水果(父类直接实现,子类无需关心)private void wash() {System.out.println("用纯净水清洗水果...");}// 固定步骤:装罐密封(父类实现)private void canning() {System.out.println("高温杀菌并密封装罐...");}// 钩子方法(Hook):默认不需要额外配料,子类可以覆盖它protected boolean needExtraIngredients() {return false;}// 固定步骤:添加额外配料(默认空实现或基础实现)protected void addExtraIngredients() {System.out.println("加入特制配料...");}}

2、具体实现

/** * @ClassName CannedApple * @Description: 模版方法实现苹果罐头 * @Author 公众号-小书浓 * @Date 2026/5/6 * @Version V1.0 */// 制作苹果罐头public class CannedApple extends CannedFruitTemplate {@Overrideprotected void prepareFruit() { System.out.println("苹果去皮、去核,切成小块...");}@Overrideprotected void cook() {System.out.println("苹果放入糖水中,中火熬煮15分钟...");}@Overrideprotected String getFruitName() {return "苹果";}}
/** * @ClassName CannedYellowPeaches * @Description: 模版方法实现黄梨罐头 * @Author 公众号-小书浓 * @Date 2026/5/6 * @Version V1.0 */// 制作黄梨罐头public class CannedYellowPeaches extends CannedFruitTemplate {@Overrideprotected void prepareFruit() {System.out.println("黄梨去核,切成四瓣,防止氧化...");}@Overrideprotected void cook() {System.out.println("将黄梨放入糖水中,小火慢炖20分钟...");}@Overrideprotected String getFruitName() {return "黄梨";}// 覆盖钩子方法:黄梨罐头需要加柠檬汁防止氧化变色@Overrideprotected boolean needExtraIngredients() {return true;}@Overrideprotected void addExtraIngredients() {System.out.println("黄梨罐头中滴入几滴新鲜柠檬汁...");}}

3、测试类

/** * @ClassName ClientTemplate * @Description: 模版方法实现测试 * @Author 公众号-小书浓 * @Date 2026/5/6 * @Version V1.0 */public class ClientTemplate {public static void main(String[] args) {// 制作苹果罐头CannedFruitTemplate 
appleCanned = new CannedApple();appleCanned.makeCannedFruit();// 制作黄梨罐头CannedFruitTemplate
orangeCanned = new CannedYellowPeaches();orangeCanned.makeCannedFruit();}}
========================================================运行结果:   苹果去皮、去核,切成小块...   用纯净水清洗水果...   苹果放入糖水中,中火熬煮15分钟...   高温杀菌并密封装罐...   --- 苹果罐头制作完成!---   黄梨去核,切成四瓣,防止氧化...   用纯净水清洗水果...   将黄梨放入糖水中,小火慢炖20分钟...   黄梨罐头中滴入几滴新鲜柠檬汁...   高温杀菌并密封装罐...   --- 黄梨罐头制作完成!---

七、对比分析

特性

普通方式(独立方法/类)

模版方法模式

代码结构

逻辑分散,重复代码多

逻辑集中在父类,子类只关注差异

流程控制

容易在不同类中写乱步骤顺序

父类通过 final 方法严格控制顺序

扩展性

新增罐头需重写所有步骤

新增罐头只需继承并实现抽象方法

维护性

修改公共步骤(如清洗)需改多处

修改公共步骤只需改父类一处

是否符合开闭原则

否

是

八、优缺点总结

优点

1、封装不变部分:将公共的清洗、装罐逻辑封装在父类,实现了代码复用。

2、扩展可变部分:苹果和黄梨的预处理、熬煮时间差异由子类实现,互不影响。

3、反向控制(钩子):通过 needExtraIngredients 钩子方法,让子类(黄梨)有机会决定是否执行父类流程中的某一步(加配料)。

缺点

1、类的数量增加:每一个不同的水果罐头都需要一个子类来实现。

2、继承的缺陷:如果父类增加了新的抽象方法,所有子类都必须进行实现,否则编译报错。

九、总结

模版方法模式通过提取“制作罐头”的通用流程,完美解决了苹果罐头和黄梨罐头制作过程中的代码复用与差异化问题。

1、统一标准:无论生产什么水果罐头,清洗和装罐的标准流程不会变。

2、灵活定制:黄梨罐头通过重写钩子方法,灵活地增加了“加柠檬汁”的步骤,而苹果罐头则保持默认。

3、易于维护:未来如果要增加“草莓罐头”或“菠萝罐头”,只需新增一个类,无需触碰核心生产流程代码。

相关学习资料