「全网最细 + 实战源码案例」设计模式——模板方法模式

devtools/2025/2/5 16:59:08/

核心思想

  • 模板方法模式(Template Method Pattern)是一种行为型设计模式,定义了一个算法的骨架(模板),将某些步骤延迟到子类中实现(在不修改结构的情况下),以避免代码重复,提高代码复用性,保持算法的结构稳定。
  • 核心:
    • 模板方法:在父类中定义一个算法的骨架(即模板方法),其中包含一些抽象方法或钩子方法(hook methods),这些方法由子类实现。
    • 不变部分:算法的整体结构是固定的,由父类控制。
    • 可变部分:算法的某些步骤可以被子类重写,以实现不同的行为。


结构

1. 抽象类(Abstract Class)

  • 定义算法的骨架(模板方法)。
  • 由一个模板方法和若干个基本方法构成。
    • 模板方法:定义算法骨架,按某种顺序调用其包含的基本方法。
    • 基本方法:实现算法各步骤的方法,是模板方法的组成部分,分为三类:
      • 抽象方法:由抽象类声明,具体子类实现。
      • 具体方法:一个具体方法由一个抽象类或具体类声明并实现,其子类可以覆盖或继承。
      • 钩子方法:抽象类中已实现,包括用于判断的逻辑方法和需要子类重写的空方法。

2. 具体子类(Concrete Class)

  • 实现抽象类中所定义的抽象方法或钩子方法,是一个顶级逻辑的组成步骤。


现实世界类比

  • 模板方法可用于建造大量房屋。 标准房屋建造方案中可提供几个扩展点, 允许潜在房屋业主调整成品房屋的部分细节。

  • 每个建造步骤 (例如打地基、 建造框架、 建造墙壁和安装水电管线等) 都能进行微调, 这使得成品房屋会略有不同。

适用场景

  1. 算法复用:当多个类有相似的算法结构,只有某些步骤不同时。
  2. 框架设计:框架通常定义算法的股价,而将具体实现留给用户。如何 Spring 中的 JdbcTemplate定义了数据库操作的流程,用户只需实现具体的 SQL 语句。
  3. 工作流设计:定义固定的工作流程,但允许某些步骤自定义。

优缺点

优点:

  1. 代码复用:不变的部分放在子类,避免代码重复。
  2. 提高扩展性:子类通过实现抽象方法或重写钩子方法类扩展算法的某些步骤。
  3. 符合开闭原则:算法整体结构(修改)是封闭的,但具体步骤(扩展)是开放的。

缺点:

  1. 一定程度违反里氏替换原则:子类重写默认步骤后可能无法替代父类。
  2. 缺乏灵活性(相比策略模式):子类必须继承模板。

实现步骤

  1. 分析目标算法, 确定能否将其分解为多个步骤。 从所有子类的角度出发, 考虑哪些步骤能够通用, 哪些步骤各不相同。
  2. 创建抽象基类并声明一个模板方法和代表算法步骤的一系列抽象方法。 在模板方法中根据算法结构依次调用相应步骤。 可用 final最终修饰模板方法以防止子类对其进行重写。
  3. 虽然可将所有步骤全都设为抽象类型, 但默认实现可能会给部分步骤带来好处, 因为子类无需实现那些方法。
  4. 可考虑在算法的关键步骤之间添加钩子。
  5. 为每个算法变体新建一个具体子类, 它必须实现所有的抽象步骤, 也可以重写部分可选步骤。

示例

// 抽象类(定义模板方法和基本方法)
public abstract class AbstractClass {// 模板方法定义public final void cookProcess(){pourOil();heatOil();pourVegetable();pourSauce();fry();}public void pourOil(){System.out.println("倒油");}public void heatOil(){System.out.println("热油");}public abstract void pourVegetable();public abstract void pourSauce();public void fry(){System.out.println("翻炒");}
}// 具体子类——包菜
public class BaoCai extends AbstractClass{@Overridepublic void pourVegetable() {System.out.println("下锅的蔬菜是包菜");}@Overridepublic void pourSauce() {System.out.println("下锅的酱料是辣椒");}
}// 具体子类——菜心
public class CaiXin extends AbstractClass{@Overridepublic void pourVegetable() {System.out.println("下锅的蔬菜是菜心");}@Overridepublic void pourSauce() {System.out.println("下锅的酱料是蒜蓉");}
}// 客户端
public class Client {public static void main(String[] args) {AbstractClass baoCai = new BaoCai();baoCai.cookProcess();System.out.println("---------------------------------------------");AbstractClass caiXin = new CaiXin();caiXin.cookProcess();}
}

在源码中的应用


与其他模式的关系

  • 工厂方法模式是模板方法模式的一种特殊形式。 同时, 可以作为一个大型中的一个步骤。
  • 模板方法基于继承机制: 它允许你通过扩展子类中的部分内容来改变部分算法。 策略模式基于组合机制: 你可以通过对相应行为提供不同的策略来改变对象的部分行为。 模板方法在类层次上运作, 因此它是静态的。 策略在对象层次上运作, 因此允许在运行时切换行为。

http://www.ppmy.cn/devtools/156325.html

相关文章

新春贺岁,共赴AGI之旅

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入! 往期精彩文章推荐 季姮教授独家文字版干货 | 面向知识渊博的大语言模型 关于AI TIME AI TIME源起于2019年,旨在发扬科学思辨精神,邀请各界人士对人工智能理论、算法和场景应用的本质问题…

神经网络|(七)概率论基础知识-贝叶斯公式

【1】引言 前序我们已经了解了一些基础知识。 古典概型:有限个元素参与抽样,每个元素被抽样的概率相等。 条件概率:在某条件已经达成的前提下,新事件发生的概率。实际计算的时候,应注意区分,如果是计算综…

Linux 系统上安装 Docker 方法详解与比较

Docker 是现代 DevOps 和容器化应用开发的重要工具,它简化了应用的部署和管理流程。本文将详细介绍在 Linux 系统上安装 Docker 的多种方法,并对它们的适用场景、优缺点进行对比,确保读者能够根据自身需求选择最优方案。 1. 官方推荐的安装方…

【游戏设计原理】98 - 时间膨胀

从上文中,我们可以得到以下几个启示: 游戏设计的核心目标是让玩家感到“时间飞逝” 游戏的成功与否,往往取决于玩家的沉浸感。如果玩家能够完全投入游戏并感受到时间飞逝,说明游戏设计在玩法、挑战、叙事等方面达到了吸引人的平衡…

【机器学习理论】朴素贝叶斯网络

基础知识: 先验概率:对某个事件发生的概率的估计。可以是基于历史数据的估计,可以由专家知识得出等等。一般是单独事件概率。 后验概率:指某件事已经发生,计算事情发生是由某个因素引起的概率。一般是一个条件概率。 …

【学习笔记之coze扣子】应用创建

今天我们来创建一个应用,也是非常简单 首先我们先创建一个应用的工作中心,为他写上名字 第二步 进来之后我们需要创建一个工作流,如果你用工作流,你也可以点击引入你需要的工作流 创建好工作流后,你会在工作台上看见一…

蓝桥杯python基础算法(2-1)——排序

目录 一、排序 二、例题 P3225——宝藏排序Ⅰ 三、各种排序比较 四、例题 P3226——宝藏排序Ⅱ 一、排序 (一)冒泡排序 基本思想:比较相邻的元素,如果顺序错误就把它们交换过来。 (二)选择排序 基本思想…

【单细胞-第三节 多样本数据分析】

文件在单细胞\5_GC_py\1_single_cell\1.GSE183904.Rmd GSE183904 数据原文 1.获取临床信息 筛选样本可以参考临床信息 rm(list ls()) library(tinyarray) a geo_download("GSE183904")$pd head(a) table(a$Characteristics_ch1) #统计各样本有多少2.批量读取 学…