模板方法模式详解:定义程序骨架与框架设计

news/2024/12/20 2:44:49/

目录

1. 什么是模板方法模式

模板方法模式是一种行为型设计模式,它在一个方法中定义一个算法的骨架,将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

这种模式在框架设计中被广泛使用,比如:

  • Spring框架中的各种Template类
  • JUnit测试框架
  • Servlet的生命周期方法

2. 为什么需要模板方法模式

模板方法模式主要解决以下问题:

  1. 代码复用:将公共的算法骨架提取到父类中
  2. 扩展性:允许子类通过重写特定方法来改变算法的定步骤
  3. 控制反转:父类控制算法的整体流程,子类提供具体实现

3. 模板方法模式的结构

UML类图

AbstractClass
+templateMethod()
#primitiveOperation1()
#primitiveOperation2()
#hook()
ConcreteClass
#primitiveOperation1()
#primitiveOperation2()
#hook()

核心角色

  1. AbstractClass(抽象类)
    • 定义了一个模板方法,该方法包含算法的骨架
    • 定义了算法各个步骤的抽象方法
    • 可能包含钩子方法(hook methods)的默认实现
  2. ConcreteClass(具体类)
    • 实现父类中的抽象方法
    • 可以覆盖钩子方法
    • 不能覆盖模板方法

4. 实现示例

让我们通过一个制作饮料的例子来理解模板方法模式

// 抽象类:定义饮料制作模板
public abstract class BeverageTemplate {// 模板方法,定义了制作饮料的算法骨架public final void prepareBeverage() {boilWater();brew();pourInCup();if (customerWantsCondiments()) {addCondiments();}}// 基本方法 - 具体方法private void boilWater() {System.out.println("将水煮沸");}// 基本方法 - 具体方法private void pourInCup() {System.out.println("倒入杯中");}// 基本方法 - 抽象方法,由子类实现protected abstract void brew();// 基本方法 - 抽象方法,由子类实现protected abstract void addCondiments();// 钩子方法,决定是否需要调料protected boolean customerWantsCondiments() {return true; // 默认返回true}
}// 具体类:咖啡
public class Coffee extends BeverageTemplate {@Overrideprotected void brew() {System.out.println("用沸水冲泡咖啡");}@Overrideprotected void addCondiments() {System.out.println("加入糖和牛奶");}
}// 具体类:茶
public class Tea extends BeverageTemplate {private boolean wantsLemon;public Tea(boolean wantsLemon) {this.wantsLemon = wantsLemon;}@Overrideprotected void brew() {System.out.println("用沸水浸泡茶叶");}@Overrideprotected void addCondiments() {System.out.println("加入柠檬");}@Overrideprotected boolean customerWantsCondiments() {return wantsLemon;}
}// 使用示例
public class BeverageDemo {public static void main(String[] args) {System.out.println("制作咖啡:");BeverageTemplate coffee = new Coffee();coffee.prepareBeverage();System.out.println("\n制作加柠檬的茶:");BeverageTemplate teaWithLemon = new Tea(true);teaWithLemon.prepareBeverage();System.out.println("\n制作不加柠檬的茶:");BeverageTemplate teaWithoutLemon = new Tea(false);teaWithoutLemon.prepareBeverage();}
}

运行结果:

制作咖啡:
将水煮沸
用沸水冲泡咖啡
倒入杯中
加入糖和牛奶制作加柠檬的茶:
将水煮沸
用沸水浸泡茶叶
倒入杯中
加入柠檬制作不加柠檬的茶:
将水煮沸
用沸水浸泡茶叶
倒入杯中

运行结果说明:

  1. 咖啡和茶都遵循相同的制作流程(模板方法定义的算法骨架)
  2. 它们各自实现了自己的冲泡(brew)和加调料(addCondiments)方法
  3. 茶类通过钩子方法(customerWantsCondiments)控制是否需要加入调料
  4. 整个流程由父类控制,子类只需要实现特定的步骤

5. 钩子方法的使用

钩子方法(Hook Methods)是模板方法模式中的一个重要概念,它让子类可以对父类的算法流程进行干预和补充。

5.1 钩子方法的类型和作用

  1. 条件型钩子方法
    • 返回布尔值,决定是否执行某个步骤
    • 用于控制算法流程
    • 例如前面例子中的 customerWantsCondiments()
  2. 空实现钩子方法
    • 提供默认的空实现
    • 子类可以选择性覆盖
    • 用于在算法中插入可选的处理步骤
  3. 默认实现钩子方法
    • 提供默认实现
    • 子类可以选择是否覆盖
    • 用于提供通用的处理逻辑

5.2 钩子方法示例

让我们通过一个文件处理的例子来详细说明钩子方法的使用:

// 抽象文件处理器
public abstract class FileProcessor {// 模板方法public final void processFile(String filePath) {if (!validateFile(filePath)) {  // 条件型钩子return;}readFile(filePath);beforeProcess();  // 空实现钩子processContent();if (needBackup()) {  // 条件型钩子backup();}afterProcess();  // 空实现钩子cleanup();  // 默认实现钩子}// 条件型钩子方法:验证文件protected boolean validateFile(String filePath) {// 默认实现:检查文件是否存在return new File(filePath).exists();}// 空实现钩子方法:处理前的准备工作protected void beforeProcess() {// 空实现,子类可以选择性覆盖}// 空实现钩子方法:处理后的收尾工作protected void afterProcess() {// 空实现,子类可以选择性覆盖}// 条件型钩子方法:是否需要备份protected boolean needBackup() {return false;  // 默认不备份}// 默认实现钩子方法:清理工作protected void cleanup() {System.out.println("执行基本清理工作");// 子类可以选择覆盖或通过super调用}// 抽象方法protected abstract void readFile(String filePath);protected abstract void processContent();protected abstract void backup();
}// 文本文件处理器
public class TextFileProcessor extends FileProcessor {private String content;private boolean isImportant;public TextFileProcessor(boolean isImportant) {this.isImportant = isImportant;}@Overrideprotected void readFile(String filePath) {System.out.println("读取文本文件: " + filePath);// 实际的文件读取代码...}@Overrideprotected void processContent() {System.out.println("处理文本内容");}@Overrideprotected void backup() {System.out.println("备份文本文件");}// 覆盖条件型钩子方法@Overrideprotected boolean needBackup() {return isImportant;  // 根据文件重要性决定是否备份}// 覆盖空实现钩子方法@Overrideprotected void beforeProcess() {System.out.println("文本处理前的准备工作");}// 扩展默认实现钩子方法@Overrideprotected void cleanup() {super.cleanup();  // 调用父类的清理方法System.out.println("执行文本文件特定的清理工作");}
}// 使用示例
public class FileProcessorDemo {public static void main(String[] args) {System.out.println("处理普通文本文件:");FileProcessor normalProcessor = new TextFileProcessor(false);normalProcessor.processFile("test.txt");System.out.println("\n处理重要文本文件:");FileProcessor importantProcessor = new TextFileProcessor(true);importantProcessor.processFile("important.txt");}
}

运行结果:

处理普通文本文件:
读取文本文件: test.txt
文本处理前的准备工作
处理文本内容
执行基本清理工作
执行文本文件特定的清理工作处理重要文本文件:
读取文本文件: important.txt
文本处理前的准备工作
处理文本内容
备份文本文件
执行基本清理工作
执行文本文件特定的清理工作

5.3 钩子方法使用技巧

  1. 条件型钩子方法的使用
    • 用于控制算法流程的分支
    • 返回布尔值,命名应该表达明确的判断含义
    • 例如:shouldProcess()isValid()canExecute()
  2. 空实现钩子方法的使用
    • 在算法关键节点提供扩展点
    • 子类可以选择是否实现
    • 通常用于前置/后置处理
    • 例如:beforeXXX()afterXXX()onXXX()
  3. 默认实现钩子方法的使用
    • 提供基础实现,允许子类扩展
    • 子类可以选择完全覆盖或调用super
    • 适用于有通用处理逻辑的场景

5.4 注意事项

  1. 命名规范
    • 条件型钩子方法使用 is、should、can 等前缀
    • 空实现钩子方法使用 before、after、on 等前缀
    • 名称应该清晰地表达方法的用途
  2. 文档说明
    • 清晰说明钩子方法的作用
    • 说明默认行为
    • 说明子类可以如何使用该钩子
  3. 设计建议
    • 钩子方法应该是protected的
    • 不要在钩子方法中放置太多逻辑
    • 钩子方法应该是可选的,不影响核心算法

6. 最佳实践与注意事项

  1. 封装变化
    • 将容易变化的步骤定义为抽象方法
    • 将稳定的步骤实现在抽象类中
    • 使用钩子方法处理可选步骤
  2. 遵循开闭原则
    • 模板方法应该是final的,防止子类改变算法骨架
    • 通过添加新的子类来扩展功能
    • 不修改已有的代码结构
  3. 命名规范
    • 模板方法应该清晰地表达其用途
    • 抽象方法的命名应该反映其在算法中的作用
    • 钩子方法通常以 should、will、do 等词开头
  4. 注意事项
    • 避免模板方法过于复杂
    • 抽象方法的数量要适中
    • 在文档中清晰说明每个抽象方法的职责

使用场景

模板方法模式适用于以下场景:

  • 多个类有相似的算法,只是其中某些步骤不同
  • 需要控制子类扩展的时候
  • 一次性实现算法的不变部分,并将可变部分留给子类
  • 防止代码重复

优点

  1. 提高代码复用性
  2. 遵循开闭原则
  3. 符合单一职责原则
  4. 父类控制,子类实现

缺点

  1. 每个不同的实现都需要一个子类
  2. 可能会导致类的数量增加
  3. 父类可能会变得过于庞大

总结

模板方法模式是一种简单但强大的设计模式,它通过把不变的行为搬移到超类,去除子类中的重复复代码来体现它的优势。它是基于继承的代码复用的基本技术。在框架设计中有着广泛的应用。使用时要注意权衡父类和子类之间的责任分配,以及钩子方法的合理使用。


http://www.ppmy.cn/news/1556539.html

相关文章

HUGGINFACE NLP-dataset

1 What if my dataset isn’t on the Hub? 1.1 Working with local and remote datasets 1.1.1 supports several common data formats, CSV & TSV csv load_dataset("csv", data_files"my_file.csv") Text files text load_dataset("text&quo…

【Liunx-后端开发软件安装】Liunx安装minio

【Liunx-后端开发软件安装】Liunx安装nginx 使用安装包安装 一、简介 MinIO 是一个高性能的对象存储系统,专为处理大规模非结构化数据而设计。它完全兼容 Amazon S3 API,这使得 MinIO 不仅可以作为本地存储解决方案,还能轻松地与基于云的服务…

金蝶云苍穹踩过的坑(慢慢更新)

IDEA不能用最新版,不然搜不到金蝶的插件。 我用的是2024.1.7/2023.1.7 IDEA里增加金蝶插件库的地址也变了,现在是 https://tool.kingdee.com/kddt/idea-updatePlugins.xml 金蝶云苍穹部署在服务器 MAC本地IDEA调试的时候,登录N次能成功一次…

决策引擎技术

决策引擎(Decision Engine)是一种用于自动化决策过程的软件系统。它通常用于处理复杂的业务逻辑,根据输入的数据和预定义的规则或模型来做出决策。决策引擎在许多领域都有广泛的应用,如金融、保险、医疗、供应链管理等。 在Java中…

linux centos 7 安装 mongodb7

MongoDB 是一个基于文档的 NoSQL 数据库。 MongoDB 是一个文档型数据库,数据以类似 JSON 的文档形式存储。 MongoDB 的设计理念是为了应对大数据量、高性能和灵活性需求。 MongoDB使用集合(Collections)来组织文档(Documents&a…

快速且靠谱的简单安装 PostgreSQL 15 yum 安装postgis3.3

快速且靠谱的简单安装 PostgreSQL 15 yum 安装postgis3.3 1、确保已经安装了PostgreSQL数据库。2、添加PostGIS的EPEL仓库3、使用YUM安装PostGIS4、以下为其他安装方式,一个个去找源码的编译安装,过程较为繁琐(不熟路的不推荐) 要…

设计模式の建造者适配器桥接模式

文章目录 前言一、建造者模式二、适配器模式2.1、对象适配器2.2、接口适配器 三、桥接模式 前言 本篇是关于设计模式中建造者模式、适配器模式(3种)、以及桥接模式的笔记。 一、建造者模式 建造者模式是属于创建型设计模式,通过一步步构建一个…

【嵌入式——QT】QT多线程编程

这里介绍的是moveToThread的方式&#xff0c;继承QThread的方式可以参考我的另一篇文章【嵌入式——QT】QThread创建多线程 编程实现 首先创建一个类&#xff0c;但是这个类一定要继承QObject SerialWorker.h #ifndef SERIALWORKER_H #define SERIALWORKER_H#include <QO…