设计模式-模板方法模式(TemplateMethod)

1. 概念

  • 模板方法模式是一种行为设计模式,它在一个方法中定义算法的骨架,将一些步骤延迟到子类中实现。

2. 原理结构图

2.1 图

在这里插入图片描述

2.2 角色
  • 抽象类(Abstract Class)
    • 定义抽象的基本操作(Primitive Operations),这些操作构成了模板方法的骨架。
    • 实现模板方法(Template Method),模板方法是定义算法结构的方法,它调用基本操作来执行这个算法的步骤。
    • 可以定义一些钩子(Hook),这些是抽象方法或具体方法,提供了默认实现,子类可以选择性地覆盖它们。
      • 具体方法(Concrete Methods)
        • 在抽象类中实现一些不变化的方法,即那些构成算法骨架的共同步骤。
        • 通常是私有(Private)或者最终(Final)的,以确保子类不会改变这些方法。
      • 抽象操作(Abstract Operations)
        • 在抽象类中声明的一些抽象方法,它们代表了算法中需要由子类实现的可变部分。
        • 通常这些抽象操作对应于算法的某些特定步骤,子类将根据具体需求来实现这些步骤。
  • 具体子类(Concrete Class)
    • 继承自抽象类的具体子类。
    • 实现或覆盖抽象操作以提供具体的实现细节。
    • 如果有需要,也可以覆盖钩子方法来影响算法的某些部分。
  • 客户端(Client)
    • 调用抽象类中的模板方法,通常会通过一个具体子类的实例来进行调用。

3. 代码示例

3.1 示例1–在线购物系统
  • 假设正在开发一个在线购物系统,其中有不同的支付方式(如信用卡支付、支付宝支付等)。每种支付方式可能有一些共通的步骤,如验证支付信息、处理支付等,但也有一些特定的步骤,如与支付网关交互。
abstract class Payment {// 模板方法,定义了支付处理的流程public final void processPayment() {validatePaymentInfo();performPrePaymentOperations();handlePayment();performPostPaymentOperations();}// 抽象方法,由具体子类实现protected abstract void validatePaymentInfo();protected abstract void handlePayment();// 钩子方法,可以有默认实现,子类可以选择覆盖protected void performPrePaymentOperations() {System.out.println("Performing pre-payment operations (default implementation).");}// 钩子方法,可以有默认实现,子类可以选择覆盖protected void performPostPaymentOperations() {System.out.println("Performing post-payment operations (default implementation).");}
}// 信用卡支付子类
class CreditCardPayment extends Payment {@Overrideprotected void validatePaymentInfo() {System.out.println("Validating credit card payment info.");// 实现具体的验证逻辑}@Overrideprotected void handlePayment() {System.out.println("Handling credit card payment with payment gateway.");// 实现与支付网关交互的逻辑}// 覆盖钩子方法以提供自定义行为@Overrideprotected void performPostPaymentOperations() {System.out.println("Updating order status after credit card payment.");// 实现更新订单状态的逻辑}
}// 支付宝支付子类
class AlipayPayment extends Payment {@Overrideprotected void validatePaymentInfo() {System.out.println("Validating Alipay payment info.");// 实现具体的验证逻辑}@Overrideprotected void handlePayment() {System.out.println("Handling Alipay payment with Alipay gateway.");// 实现与支付宝网关交互的逻辑}// 不需要覆盖钩子方法,使用默认实现
}public class PaymentProcessor {public static void main(String[] args) {Payment creditCardPayment = new CreditCardPayment();Payment alipayPayment = new AlipayPayment();// 处理信用卡支付creditCardPayment.processPayment();// 处理支付宝支付alipayPayment.processPayment();}
}
  • 将看到如下输出:
Validating credit card payment info.
Performing pre-payment operations (default implementation).
Handling credit card payment with payment gateway.
Updating order status after credit card payment.
Validating Alipay payment info.
Performing pre-payment operations (default implementation).
Handling Alipay payment with Alipay gateway.
Performing post-payment operations (default implementation).
  • 在这个例子中,Payment 类定义了支付处理的算法骨架,CreditCardPayment 和 AlipayPayment 子类则提供了具体实现的细节。processPayment 方法作为模板方法,确保每种支付方式都遵循相同的处理流程,而子类则负责实现各自特有的验证和支付逻辑。同时,钩子方法 performPrePaymentOperations 和 performPostPaymentOperations 提供了额外的扩展点,允许子类在需要时插入自定义的行为。

3.2 示例2–文件处理系统
  • 定义一个处理文件的通用流程,但具体的文件读取和写入操作可以由不同的子类来实现。
// 抽象类,定义文件处理的模板方法  
abstract class FileProcessor {// 模板方法,定义了文件处理的流程  public final void processFile(String filePath) {System.out.println("Starting file processing...");// 读取文件内容  String content = readFile(filePath);// 对读取的内容进行处理  String processedContent = processContent(content);// 将处理后的内容写入文件  writeFile(processedContent, filePath);System.out.println("File processing completed.");}// 抽象方法,由子类提供具体的实现  protected abstract String readFile(String filePath);// 抽象方法,由子类提供具体的实现  protected abstract String processContent(String content);// 抽象方法,由子类提供具体的实现  protected abstract void writeFile(String content, String filePath);// 可以提供一个钩子方法,允许子类在必要时覆盖  protected void beforeProcessing() {System.out.println("Before processing hook method.");}// 可以提供一个钩子方法,允许子类在必要时覆盖  protected void afterProcessing() {System.out.println("After processing hook method.");}
}// 具体类1,实现文件处理器,用于处理文本文件  
class TextFileProcessor extends FileProcessor {@Overrideprotected String readFile(String filePath) {// 实现读取文本文件的逻辑  System.out.println("Reading text file: " + filePath);// 假设这里返回读取到的文件内容  return "Content from text file";}@Overrideprotected String processContent(String content) {// 实现处理文本文件内容的逻辑  System.out.println("Processing text content...");// 假设这里对内容进行了处理  return "Processed text content";}@Overrideprotected void writeFile(String content, String filePath) {// 实现写入文本文件的逻辑  System.out.println("Writing to text file: " + filePath);// 假设这里将内容写入了文件  }// 可以选择覆盖钩子方法  @Overrideprotected void beforeProcessing() {System.out.println("Before processing text file.");}
}// 具体类2,实现文件处理器,用于处理二进制文件  
class BinaryFileProcessor extends FileProcessor {@Overrideprotected String readFile(String filePath) {// 实现读取二进制文件的逻辑  System.out.println("Reading binary file: " + filePath);// 假设这里返回读取到的二进制内容(转换为字符串表示)  return "Content from binary file";}@Overrideprotected String processContent(String content) {// 实现处理二进制文件内容的逻辑  System.out.println("Processing binary content...");// 假设这里对内容进行了处理  return "Processed binary content";}@Overrideprotected void writeFile(String content, String filePath) {// 实现写入二进制文件的逻辑  System.out.println("Writing to binary file: " + filePath);// 假设这里将内容写入了文件  }
}// 主类,用于演示模板方法模式的使用  
public class FileProcessorDemo {public static void main(String[] args) {// 创建文本文件处理器对象  FileProcessor textFileProcessor = new TextFileProcessor();// 使用文本文件处理器处理文件  textFileProcessor.processFile("path/to/text/file.txt");// 创建二进制文件处理器对象  FileProcessor binaryFileProcessor = new BinaryFileProcessor();// 使用二进制文件处理器处理文件  binaryFileProcessor.processFile("path/to/binary/file.bin");}
}
  • 将看到如下输出:
Starting file processing...
Reading text file: path/to/text/file.txt
Processing text content...
Writing to text file: path/to/text/file.txt
File processing completed.
Starting file processing...
Reading binary file: path/to/binary/file.bin
Processing binary content...
Writing to binary file: path/to/binary/file.bin
File processing completed.
  • 在这个例子中,FileProcessor 是一个抽象类,它定义了一个 processFile 模板方法,该方法按照预定的顺序调用了三个抽象方法:readFile、processContent 和 writeFile。这些抽象方法必须由子类提供具体的实现。
  • TextFileProcessor 和 BinaryFileProcessor 是 FileProcessor 的具体子类,分别用于处理文本文件和二进制文件。每个子类都提供了 readFile、processContent 和 writeFile 方法的具体实现,以符合它们各自处理文件类型的特定需求。
  • FileProcessorDemo 类是主类,用于演示如何使用这两个具体的文件处理器。通过创建 TextFileProcessor 和 BinaryFileProcessor 的实例,并调用它们的 processFile 方法,可以执行文件处理流程。

4. 优缺点

  • 主要作用
    • 定义一个操作中的算法骨架,并将一些步骤延迟到子类中实现。
  • 优点
    • 代码复用:通过定义稳定的操作序列,减少了重复代码的编写,提高了代码复用性。
    • 结构清晰:将不变的部分和可变的部分分离,使得操作流程更加清晰和易于理解。
    • 扩展性好:子类可以通过实现抽象方法来定制特定步骤,从而在不修改原有结构的情况下进行扩展。
    • 易于维护:由于操作流程被统一管理,当需要修改流程时,只需在抽象类或模板方法中修改,减少了维护工作量。
  • 缺点
    • 抽象类的数量增加:为了使用模板方法,可能需要创建新的抽象类。
    • 代码的复杂性:对于初学者而言,理解模板方法的继承和多态特性可能较为复杂。
    • 子类的依赖性增加:子类需要依赖父类的具体实现,这可能导致子类与父类之间的耦合度增加。

5. 应用场景

5.1 主要包括以下几个方面
  1. 有固定处理步骤的算法:当一个复杂的算法或流程中的某些步骤在各个子类中实现相同,而某些步骤需要根据具体子类实现不同功能时,可以使用模板方法模式。
  2. 代码复用和扩展性要求高的场景:如果希望在不改变算法结构的前提下,通过继承机制来扩展程序的功能,模板方法模式可以实现这一点。
  3. 有多个子类共享相似处理过程的情况:当多个子类具有类似的处理流程,但部分处理步骤在各子类间有所差异时,模板方法可以将这些步骤抽象化,并在抽象类中定义共同的处理过程。
  4. 插件式系统:当希望系统能够动态地加载和卸载功能模块时,模板方法模式允许将新功能的实现作为插件插入到系统中,而不需要修改现有代码。
  5. 框架开发:在设计软件框架时,模板方法模式可以用来定义框架的基础结构和默认行为,同时允许用户通过继承机制定制特定行为。

5.2 实际应用
  1. 文件读取和处理:当需要从不同类型的文件中读取数据并进行处理时,可以使用模板方法模式。首先定义一个抽象类,其中包含读取文件、解析数据和处理数据的步骤。然后,为每种文件类型创建一个子类,实现具体的读取和解析方法。这样,可以确保处理流程的一致性,同时根据不同的文件类型提供特定的实现。
  2. 图形绘制:在图形绘制系统中,可以使用模板方法模式来定义绘制不同形状的算法骨架。首先定义一个抽象类,其中包含绘制形状的步骤,如设置画笔颜色、绘制边框等。然后,为每种形状创建一个子类,实现具体的绘制方法。这样,可以确保绘制流程的一致性,同时根据不同的形状提供特定的实现。
  3. 网络请求处理:在网络应用程序中,可以使用模板方法模式来处理不同类型的网络请求。首先定义一个抽象类,其中包含发送请求、接收响应和处理结果的步骤。然后,为每种请求类型创建一个子类,实现具体的发送和接收方法。这样,可以确保请求处理流程的一致性,同时根据不同的请求类型提供特定的实现。
  4. 游戏开发:在游戏开发中,可以使用模板方法模式来定义游戏的主循环。首先定义一个抽象类,其中包含更新游戏状态、渲染画面和处理用户输入的步骤。然后,为每种游戏类型创建一个子类,实现具体的更新和渲染方法。这样,可以确保游戏循环的一致性,同时根据不同的游戏类型提供特定的实现。

6. JDK中的使用

  • 集合框架:在java.util.Collections类中,例如sort()方法就是一个模板方法。它定义了排序的算法骨架,具体的比较逻辑则由实现Comparator接口的子类来完成。
  • I/O流:java.io.InputStream和java.io.OutputStream等抽象类中使用了模板方法模式。它们定义了读取和写入数据的基本步骤,而具体如何读取和写入则由子类如FileInputStream和FileOutputStream来实现。
  • JUnit测试框架:在JUnit中,TestCase类使用了模板方法模式。setUp()和tearDown()方法定义了测试用例执行前后的准备工作和清理工作,而具体的测试逻辑则在继承自TestCase的子类中实现。

7. 注意事项

  • 合理使用:模板方法模式适用于有固定算法结构,但某些步骤需要在不同子类中具体实现的情况。不是所有的场景都需要使用模板方法模式,只有在多个子类中需要重复实现一些通用方法或算法时,才考虑使用它。
  • 区分钩子方法:模板方法模式中的钩子方法是一种可选的扩展点,它们可以为子类提供覆盖超类中某些步骤的机会。正确理解和使用钩子方法与模板方法的关系,可以帮助更好地实现算法的可定制性。
  • 避免滥用:尽管模板方法模式可以提高系统的扩展性,但过度使用可能导致代码复杂性和维护难度增加。因此,应当在确实需要时才使用模板方法模式。
  • 关注子类实现:在使用模板方法模式时,需要确保子类正确地实现了所有抽象方法,否则可能导致运行时错误或逻辑不一致。

8. 模板方法模式 VS 建造者模式

模式类型目的模式架构主要角色应用场景
模板方法模式行为型定义一个操作中的算法骨架,并将一些步骤延迟到子类中实现。抽象类和具体方法类适用于有固定处理步骤的算法,但某些步骤需要在不同的子类中实现时使用。
建造者模式创建型将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。Builder、ConcreteBuilder、Director 和 Product适用于创建复杂对象

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/817869.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从启发式到模型化 京东推荐广告排序机制演化

1、序言:广告排序机制的前世今生 1.1、简介:广告排序机制 在线广告是国内外各大互联网公司的重要收入来源之一,而在线广告与传统广告最大的区别就在于其超大规模的实时竞价环境:数以万计的广告主在一天内可以参与亿级别的流量竞…

解决宝塔的FTP无法使用被动模式

问题:宝塔安装完ftp管理软件之后,无法使用被动模式连接 解决: 提示: 如果还是不行,那么要看看防火墙和安全组有没有放行被动模式的端口,宝塔安装的pure-ftpd软件的被动模式端口默认是39000至400…

Kubernetes 升级不弃 Docker:KubeKey 的丝滑之道

作者:尹珉,KubeSphere Ambaasador&Contributor,KubeSphere 社区用户委员会杭州站站长。 引言 随着 Kubernetes 社区的不断发展,即将迎来 Kubernetes 1.30 版本的迭代。在早先的 1.24 版本中,社区作出一个重要决策…

计算机网络——42攻击和对策

攻击和对策 IDS:入侵检测系统 分组过滤 对TCP/IP头部进行检查不检查会话间的相关性 IDS:intrusion detection system 深入分组检查:检查分组的内容(e.g. 检查分组中的特征串,已知攻击数据库的病毒和攻击串)检查分组间…

【网站项目】捷邻小程序

🙊作者简介:拥有多年开发工作经验,分享技术代码帮助学生学习,独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。🌹赠送计算机毕业设计600个选题excel文件,帮助大学选题。赠送开题报告模板&#xff…

FPGA - 以太网UDP通信(二)

一,引言 前文链接:FPGA - 以太网UDP通信(一) 在上文章中介绍了以太网简介,以太网UDP通信硬件结构,以及PHY芯片RGMII接口-GMII接口转换逻辑,接下来介绍UDP通信结构框图以及数据链路层&#xff…

Python | Leetcode Python题解之第28题找出字符串中的第一个匹配项的下标

题目: 题解: class Solution:def strStr(self, haystack: str, needle: str) -> int:# Func: 计算偏移表def calShiftMat(st):dic {}for i in range(len(st)-1,-1,-1):if not dic.get(st[i]):dic[st[i]] len(st)-idic["ot"] len(st)1re…

自己开发的App如何上架,详细解读App上架操作流程

对于企业或个人开发的App,上架是必经之路。然而,许多人不清楚如何进行App上架。工信部在2023年规定,App必须备案才能上架。那么,让我们一起了解App上架流程吧。 1. 准备上架所需材料 在上架App之前,需要准备应用图标…

类加载子系统

目录 类的加载 加载流程 类的加载器 类的链接 类的检验阶段 类的准备阶段 类的解析阶段 类的初始化 static与final的搭配问题 ()的线程安全性 类的初始化情况:主动使用vs被动使用 类的使用 类的卸载 类、类的加载器、类的实例之间的引用关系 类的生命…

端口协议(爆破、未授权)

常见端口服务及攻击方向: 弱口令爆破 工具:https://github.com/vanhauser-thc/thc-hydra hydra是一个支持多协议的自动化的爆破工具。 支持的服务、协议: telnet ftp pop3[-ntlm] imap[-ntlm] smb smbnt http-{head|get} http-{get|post}-…

深度学习入门(3)

一、感知机 感知机接收多个输入信号,输出一个信号。这里所说的“信号”可以想象成电流或河流那样具备“流动性”的东西。 但是,和实际的电 流不同的是,感知机的信号只有“流 / 不流”( 1 / 0 )两种取值。在本书中&…

【研发日记】Matlab/Simulink软件优化(一)——动态内存负荷压缩

文章目录 背景介绍 初始代码 优化代码 分析和应用 总结 背景介绍 在一个嵌入式软件开发项目中,有一个使用MATLAB Function编写的算法模块,功能是从一个较大的数组中提取一段数据,然后求均值输出,示例如下: 初始代…

Python和Java哪个更适合后端开发?

Python和Java都是强大的后端开发语言,它们各自有鲜明的特点和适用场景。选择哪一个更适合后端开发,主要取决于具体的项目需求、团队技术栈、个人技能偏好以及长期发展考虑等因素。 下面是两者在后端开发中的优势和劣势: 「Python&#xff1…

Maven超详细使用

定义 是一款用于管理和构建java项目的工具 作用 1. 依赖管理 2. 统一项目结构 3. 项目构建 项目目录结构 POM 项目对象模型 (Project Object Model) POM (Project Object Model) :指的是项目对象模型,用来描述当前的maven项目。 仓库 本地仓库&#…

银河麒麟高级服务器操作系统adb读写缓慢问题分析

1.问题环境 处理器: HUAWEI Kunpeng 920 5251K 内存: 512 GiB 整机类型/架构: TaiShan 200K (Model 2280K) BIOS版本: Byosoft Corp. 1.81.K 内核版本 4.19.90-23.15.v2101.ky10.aarch64 第三方应用 数据库 2.问题…

苹果个人证书管理

根据近日工业和信息化部发布的《工业和信息化部关于开展移动互联网应用程序备案工作的通知》,相信不少要进行IOS平台App备案的朋友遇到了一个问题,就是apple不提供云管理式证书的下载,也就无法获取公钥及证书SHA-1指纹。 已经上架的应用不想重…

瑞芯微RK3568/RK3588+鸿蒙,矿鸿工控屏、矿鸿工控板、矿鸿网关,推动矿业数智化变革

4月10日至12日,以“绿色智能创新,携手共赢未来”为主题的第二届中国国际矿业装备与技术展览会在西安举行。信迈科技携矿鸿解决方案及产品亮相,赋能矿山行业数智化升级和国产化改造进程全面提速。 作为华为矿山军团矿鸿生态使能合作伙伴&#…

【位运算】3097. 或值至少为 K 的最短子数组 II

本文涉及知识点 位运算 LeetCode3097. 或值至少为 K 的最短子数组 II 给你一个 非负 整数数组 nums 和一个整数 k 。 如果一个数组中所有元素的按位或运算 OR 的值 至少 为 k ,那么我们称这个数组是 特别的 。 请你返回 nums 中 最短特别非空 子数组 的长度&…

数据可视化-ECharts Html项目实战(10)

在之前的文章中,我们学习了如何在ECharts中编写雷达图,实现特殊效果的插入运用,函数的插入,以及多图表雷达图。想了解的朋友可以查看这篇文章。同时,希望我的文章能帮助到你,如果觉得我的文章写的不错&…

国内免费中文版ChatGPT网站入口(2024/4/15)

首先向大家介绍一些基本信息:ChatGPT3.5在官方网站上是可以免费使用的,而ChatGPT4.0则采取按月收费的模式,价格为20美元(约合140元人民币)。因此,所谓的免费使用主要指的是GPT3.5。如果有人声称GPT4.0也是免…