在软件开发中,我们经常会遇到这样的情况:多个类执行相似的操作流程,但每个类在流程的某些步骤上有自己特定的实现。如果为每个类都完整地编写整个流程,会导致大量重复代码,且难以维护。这时候,模板模式(Template Method Pattern)就派上用场了。

一、模板模式概述

1.1 什么是模板模式

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

简单来说,模板模式就是把不变的流程放在父类中实现,把可能变化的具体实现延迟到子类中完成。这就像我们写文档时使用的模板——文档的结构是固定的,但具体内容可以根据需要填充。

1.2 模式结构

模板模式通常包含以下几个关键组成部分:

  1. 抽象类(Abstract Class)

    • 定义了一个或多个抽象操作(称为基本操作),这些操作由子类实现

    • 实现了一个模板方法,定义算法的骨架,按顺序调用基本操作

    • 可能包含一些具体方法(有默认实现)和钩子方法

  2. 具体类(Concrete Class)

    • 实现抽象类中定义的基本操作

    • 可以选择性地覆盖钩子方法

1.3 模式特点

  • 模板方法:定义算法骨架,通常声明为final以防止子类重写

  • 基本操作:抽象方法或具体方法,由子类实现或覆盖

  • 钩子方法:提供默认实现,子类可选择是否覆盖

二、模板模式的实现

2.1 基本实现示例

让我们通过一个简单的例子来理解模板模式的实现。假设我们有一个制作饮料的流程,冲泡咖啡和茶的流程相似但不完全相同。

// 抽象类 - 饮料制作
public abstract class Beverage {// 模板方法,定义制作饮料的流程(final防止子类修改流程)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;}
}// 具体类 - 咖啡
public class Coffee extends Beverage {@Overrideprotected void brew() {System.out.println("冲泡咖啡粉");}@Overrideprotected void addCondiments() {System.out.println("加入糖和牛奶");}@Overrideprotected boolean customerWantsCondiments() {// 可以通过用户输入决定是否添加调料String answer = getUserInput();return answer.toLowerCase().startsWith("y");}private String getUserInput() {// 实际项目中可能是从界面获取用户输入return "yes";}
}// 具体类 - 茶
public class Tea extends Beverage {@Overrideprotected void brew() {System.out.println("浸泡茶叶");}@Overrideprotected void addCondiments() {System.out.println("加入柠檬");}// 不覆盖customerWantsCondiments(),使用默认实现
}

2.2 代码解析

在这个例子中:

  1. Beverage 是抽象类,定义了制作饮料的模板方法 prepareBeverage()

  2. boilWater() 和 pourInCup() 是具体方法,所有饮料共用

  3. brew() 和 addCondiments() 是抽象方法,由子类实现

  4. customerWantsCondiments() 是钩子方法,子类可以选择覆盖

  5. Coffee 和 Tea 是具体类,实现了特定的冲泡和添加调料方法

2.3 模板方法中的钩子方法

钩子方法(Hook Method)是模板模式中的一个重要概念。它是一个在抽象类中声明并提供默认实现的方法,子类可以选择性地覆盖它。钩子方法通常用于:

  1. 对模板方法的流程进行微调

  2. 为子类提供额外的扩展点

  3. 控制某些可选步骤是否执行

在上面的例子中,customerWantsCondiments() 就是一个钩子方法,它控制是否执行 addCondiments() 步骤。

三、模板模式的深入分析

3.1 优点

  1. 提高代码复用性:将公共代码移到父类中,避免了代码重复

  2. 实现反向控制:通过父类调用子类的操作,符合"好莱坞原则"("不要调用我们,我们会调用你")

  3. 便于扩展:符合开闭原则,增加新的具体类很容易

  4. 提高可维护性:算法结构集中在一个地方,修改方便

  5. 灵活性:通过钩子方法提供额外的控制点

3.2 缺点

  1. 类数量增加:每个不同的实现都需要一个子类

  2. 设计复杂度增加:需要仔细设计抽象类和具体类的关系

  3. 继承的局限性:Java等语言只支持单继承,限制了灵活性

  4. 可能导致方法泛滥:如果基本操作过多,会导致类变得复杂

3.3 适用场景

模板模式适用于以下情况:

  1. 一次性实现算法的不变部分,将可变部分留给子类实现

  2. 各子类中公共的行为应被提取出来集中到一个公共父类中

  3. 需要控制子类扩展,只允许在特定点进行扩展

  4. 多个类有相似的行为,但某些步骤的实现不同

四、模板模式在实际中的应用

4.1 Java集合框架中的模板模式

Java的AbstractListAbstractSetAbstractMap等类都使用了模板模式。它们提供了集合操作的骨架实现,具体的集合类只需要实现少量必要的方法。

例如,AbstractList提供了iterator()contains()等方法的默认实现,这些方法依赖于get(int)size()等抽象方法,由具体子类实现。

4.2 Servlet中的模板模式

在Java Web开发中,HttpServlet类使用了模板模式。它提供了service()方法作为模板方法,根据HTTP请求类型调用相应的doGet()doPost()等方法。开发者只需要覆盖需要处理的HTTP方法即可。

4.3 Spring框架中的模板模式

Spring框架大量使用了模板模式,最典型的是JdbcTemplate。它封装了JDBC操作的固定流程(获取连接、创建语句、执行SQL、处理结果、释放资源),而开发者只需要提供SQL和结果处理逻辑。

jdbcTemplate.query("SELECT * FROM users WHERE age > ?", new Object[]{18},(rs, rowNum) -> new User(rs.getInt("id"),rs.getString("name"),rs.getInt("age"))
);

在这个例子中,Spring处理了所有样板代码(异常处理、资源清理等),开发者只需要关注SQL和结果映射。

五、模板模式的最佳实践

5.1 设计原则

  1. 好莱坞原则:"不要调用我们,我们会调用你"——父类控制流程,子类提供具体实现

  2. 开闭原则:对扩展开放(通过子类实现新行为),对修改关闭(不修改模板方法)

  3. 单一职责原则:每个类只关注自己的特定实现

5.2 实现建议

  1. 将模板方法声明为final:防止子类改变算法结构

  2. 尽量减少基本操作的数量:太多抽象方法会使子类实现变得复杂

  3. 合理使用钩子方法:提供灵活性的同时不要过度使用

  4. 命名约定:可以给模板方法添加"Template"后缀以提高可读性

  5. 考虑使用组合代替继承:在某些情况下,策略模式可能是更好的选择

5.3 与其他模式的关系

  1. 与工厂方法模式:工厂方法模式常被模板方法调用

  2. 与策略模式:都是封装算法,但策略模式使用组合,模板模式使用继承

  3. 与装饰器模式:装饰器模式动态添加行为,模板模式静态定义算法骨架

六、总结

模板模式是一种强大而灵活的设计模式,它通过定义算法的骨架并将具体步骤延迟到子类中实现,达到了代码复用和扩展性的平衡。在实际开发中,当遇到多个类有相似流程但某些步骤实现不同的情况时,考虑使用模板模式可以显著提高代码质量和可维护性。

然而,模板模式也有其局限性,特别是它对继承的依赖。在现代软件开发中,组合优于继承的原则越来越被重视,因此在某些场景下,可以考虑使用策略模式等替代方案。

理解并合理运用模板模式,可以帮助我们设计出更加清晰、灵活和可维护的代码结构,是每个软件工程师工具箱中的重要工具之一。

 

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

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

相关文章

基于单片机宠物喂食器/智能宠物窝/智能饲养

传送门 👉👉👉👉其他作品题目速选一览表 👉👉👉👉其他作品题目功能速览 概述 深夜加班时,你是否担心家中宠物饿肚子?出差旅途中,是否焦虑宠…

静态补丁脚本 - 修改 libtolua.so

直接改arm64的so, 使用python脚本。#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 静态补丁脚本 - 修改 libtolua.so 主要功能: 1. 修改 luaL_loadbuffer 函数,将跳转目标从 luaL_loadbufferx 改为 luaL_loadfilex 2. …

2-大语言模型—理论基础:详解Transformer架构的实现(2)

目录 1-大语言模型—理论基础:详解Transformer架构的实现(1)-CSDN博客https://blog.csdn.net/wh1236666/article/details/149443139?spm1001.2014.3001.5502 2.3、残差连接和层归一化 2.3.1、什么是层归一化? 2.3.2、层归一化的核心特点&#xff08…

SmartX 用户建云实践|富士康:基于榫卯企业云平台构建分布式云,支撑全球多地工厂重要产线

作为全球最大的电子科技智造服务商,富士康集团在全球范围内构建生产制造网络。为实现多厂区统一管理与降本增效,在逐步替代 VMware 虚拟化架构的过程中,富士康对比了自研 OpenStack Ceph 平台和 SmartX 超融合方案,最终选择基于 …

ADC选型设计

1、最大摆伏FSR: 0 ~ 4.096V,一般Vref要等于FSR 2、最大频率:根据奈奎斯特采样定理大于2倍的信号频率才够还原信号,所以选择20/50倍更好, 3、最小精度,对于一给定模拟输入,实际数字输出与理论预…

基于深度学习的火灾智能检测系统设计与实现

在各类安全事故中,火灾因其突发性强、破坏力大,一直是威胁人们生命财产安全的重大隐患。传统的火灾检测方式多依赖烟雾传感器、温度传感器等,存在响应滞后、易受环境干扰等问题。随着深度学习技术的飞速发展,基于计算机视觉的火灾…

HIVE实战处理(二十四)留存用户数

留存概念: 次X日活跃留存,次X日新增留存,也就是看今天的新增或活跃用户在后续几天的留存情况一、留存表的生成逻辑 因为用户活跃日期和留存的日期无法对齐所以搞了2级分区(dt,static_day) 1)首先获得计算日D、根据要出…

W3C XHTML 活动:标准化的未来与交互式体验

W3C XHTML 活动:标准化的未来与交互式体验 概述 W3C(World Wide Web Consortium)是全球领先的互联网技术标准制定组织。XHTML,作为W3C推荐的标准之一,是一种基于XML的标记语言,旨在提供一个更加结构化、兼容性和可扩展性更高的网页内容表示方式。本文将围绕W3C的XHTML活…

Java-数构链表

1.链表 1.1链表的概念和结构 链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中引用链接次序实现的。 这里大多讨论无头单向非循环链表。这种结构,结构简单,一般与其他数据结构结合,作为其他数据结构的子…

Windows系统软件游戏丢失找不到mgmtapi.dll修复解决方法

在使用电脑系统时经常会出现丢失找不到某些文件的情况,由于很多常用软件都是采用 Microsoft Visual Studio 编写的,所以这类软件的运行需要依赖微软Visual C运行库,比如像 QQ、迅雷、Adobe 软件等等,如果没有安装VC运行库或者安装…

初识C++——开启新旅途

从今天开始主包也是掉入C这个深坑,上完课也是跟没上一样,所以写好博客复习还是很重要的,话不多说,进入正题~~1、命名空间(1)namespace的价值与作用在C/C中,变量、函数和后面要学到的类都是大量存在的,这些变…

vue2 面试题及详细答案150道(141 - 150)

《前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs&…

第十三章 Go包管理

文章目录使用logurs处理程序日志logrus 常用配置使用viper处理程序配置使用logurs处理程序日志 下载包,在终端执行命令 go get github.com/sirupsen/logrus官方示例 package mainimport (log "github.com/sirupsen/logrus" )func main() {log.WithFiel…

EP01:【Python 第一弹】基础入门知识

一、基础入门知识 1.1 代码规范 1.1.1 语句分隔符 ; 换行 1.1.2 格式化 对 Windows 和 Linux 操作系统,快捷键是Ctrl Alt L对 macOS 操作系统,快捷键是Cmd Option L 1.1.3 注释 单行注释 # 这是一行注释多行注释 """ 这 是 …

实用的文件和文件夹批量重命名工具

在日常工作中,文件和文件夹的命名管理常常让人头疼。尤其是面对大量文件时,手动重命名不仅耗时,还容易出错。今天,我要给大家推荐一款超级实用的工具——OncePower 文件批量重命名,它不仅能批量重命名文件和文件夹&…

【Git】报错:git config --global http.sslBackend “openssl“

问题解决 报错:git config --global http.sslBackend “openssl”解决方法: git config --global http.sslBackend "openssl"之后再 push 即可正常提交。 🔍 原因分析 ​​系统环境不支持 OpenSSL 后端​​ Git 在某些平台&#xf…

Redisson RLocalCachedMap 核心参详解

🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编…

AI辅助编程时代的高效规范开发指南:工具、原则与提效策略

引言:AI辅助编程的时代背景与核心挑战 人工智能在编程领域的应用虽可追溯至20世纪50年代,但近十年实现了革命性突破,推动其从早期的代码补全工具演进为能理解上下文、生成完整函数乃至项目架构的智能系统。关键发展里程碑包括:20…

百度网盘TV版1.21.0 |支持倍速播放,大屏云看片

百度网盘TV版是专为智能电视设计的应用程序,让用户可以直接在大屏幕上观看保存在云端的视频资源。此应用提供了与手机端几乎相同的功能,包括倍速播放功能,使得用户可以更方便地享受高清视频内容。无需繁琐的操作步骤,即可实现云端…

C++控制台贪吃蛇开发(二):让灵蛇舞动起来!

资料合集下载链接: ​​https://pan.quark.cn/s/472bbdfcd014​ 本文将深入讲解蛇移动的机制,并带你一步步实现以下功能: 理解蛇移动的核心算法:为什么蛇的移动是“倒着”更新的? 用代码表示方向:如何使用​​dx​​和​​dy​​变量优雅地控制方向。 编写核心​​move…