当前位置: 首页 > news >正文

【设计模式】模板方法模式--让你的代码更具灵活性与可扩展性

文章目录

  • 前言
  • 模板方法模式的定义
    • 核心组成
    • 模板方法模式与其他设计模式的区别
  • 代码实现
    • 抽象类
    • 具体类
    • Client
  • 经典类图
    • spring中的例子
  • 总结

前言

在软件开发中,设计模式是一种经过实践检验的、可复用的解决方案,它们可以帮助我们解决某一特定领域的典型问题。设计模式不仅能提高代码的可读性、可维护性,还能让我们的代码更加灵活和易于扩展。在这个不断发展的技术世界中,了解并掌握设计模式对于软件开发人员来说是至关重要的。

模板方法模式是一种广泛应用于各种软件系统中的行为设计模式。它在算法的骨架上定义了一系列步骤,而具体的实现则留给子类来完成。通过这种方式,模板方法模式允许我们在不改变算法结构的情况下,灵活地重用和扩展代码。

  • 在这篇文章中,我们将深入探讨模板方法模式的定义、工作原理和适用场景。
  • 我们还将通过一个实际的代码示例,来展示如何在我们熟悉的编程语言中实现模板方法模式。
  • 最后,我们将讨论模板方法模式的优缺点,以帮助你在实际开发中做出明智的决策。

模板方法模式的定义

模板方法模式是一种行为设计模式,它在父类中定义了一个算法的骨架,将一些具体步骤的实现延迟到子类。这样,模板方法模式允许子类在不改变算法结构的情况下,重新定义算法中的某些步骤。这是“设计模式:可复用面向对象软件的基础”一书中对模板方法模式的正式定义。

用通俗易懂的话来说,模板方法模式就像是一份食谱。食谱提供了烹饪某道菜的基本步骤,但具体的烹饪方法和食材可以根据个人口味进行调整。

在编程中,我们可以将这些基本步骤定义在一个抽象类(食谱)中,然后让子类(具体的烹饪方式)根据需要重写或扩展这些步骤。

核心组成

模板方法模式的核心组成部分包括:

  • 抽象类:定义了一个算法的骨架,包含一系列抽象方法和具体方法。抽象方法由子类实现,具体方法可以包含一些通用逻辑或者调用抽象方法。
  • 具体类:继承自抽象类,实现抽象方法,根据需要重写或扩展算法中的具体步骤。

通过这种方式,模板方法模式让我们能够在保持算法骨架不变的前提下,根据实际需求灵活地调整和扩展算法的具体实现。

模板方法模式与其他设计模式的区别

  1. 模板方法模式 vs 策略模式: 模板方法模式主要关注算法的骨架和步骤,通过继承抽象类来扩展或重写算法的部分步骤。而策略模式关注于将算法封装成一组可互换的策略对象,通过组合来实现不同的算法。模板方法模式使用继承,策略模式使用组合。

  2. 模板方法模式 vs 工厂方法模式: 模板方法模式关注的是算法的骨架和不同的实现细节。工厂方法模式则是一种创建型设计模式,关注的是如何创建对象,将对象的实例化延迟到子类中实现。它们都利用继承来实现不同的行为,但解决的问题类型不同。

  3. 模板方法模式 vs 装饰器模式: 模板方法模式通过继承抽象类来重写或扩展算法的部分步骤。装饰器模式则通过将对象包装在装饰器类中来动态地扩展对象的功能。装饰器模式不改变原对象的接口,而是通过组合来实现功能的扩展。模板方法模式侧重于算法的骨架,装饰器模式侧重于对象功能的动态扩展。

  4. 模板方法模式 vs 观察者模式: 模板方法模式关注算法的骨架和部分实现细节。观察者模式则是一种行为型设计模式,主要用于实现对象之间的松耦合通信。观察者模式中,被观察者(主题)维护一组观察者,并在状态改变时通知它们。模板方法模式和观察者模式解决的问题类型和目的不同。

代码实现

咖啡店中不同类型的咖啡制作过程。假设我们有一个咖啡店,出售美式咖啡和拿铁咖啡。这两种咖啡的制作过程有些相似,但也有些不同。我们可以使用模板方法模式来实现这个场景。

抽象类

// 抽象类:CoffeeBeverage,定义咖啡制作的骨架
abstract class CoffeeBeverage {// 模板方法:prepareCoffee,定义制作咖啡的步骤public final void prepareCoffee() {boilWater();brewCoffee();pourInCup();addCondiments();}// 具体方法:boilWater,烧水(公共步骤)private void boilWater() {System.out.println("Boiling water");}// 抽象方法:brewCoffee,冲泡咖啡(留给子类实现)protected abstract void brewCoffee();// 具体方法:pourInCup,倒进杯子(公共步骤)private void pourInCup() {System.out.println("Pouring coffee into the cup");}// 抽象方法:addCondiments,加入调料(留给子类实现)protected abstract void addCondiments();
}

具体类

// 具体类:Americano,美式咖啡
class Americano extends CoffeeBeverage {@Overrideprotected void brewCoffee() {System.out.println("Brewing Americano coffee");}@Overrideprotected void addCondiments() {System.out.println("Adding sugar and milk");}
}// 具体类:Latte,拿铁咖啡
class Latte extends CoffeeBeverage {@Overrideprotected void brewCoffee() {System.out.println("Brewing Latte coffee");}@Overrideprotected void addCondiments() {System.out.println("Adding steamed milk and foam");}
}

Client

// 测试类
public class CoffeeShop {public static void main(String[] args) {System.out.println("Making an Americano coffee:");CoffeeBeverage americano = new Americano();americano.prepareCoffee();System.out.println("\nMaking a Latte coffee:");CoffeeBeverage latte = new Latte();latte.prepareCoffee();}
}

代码解释:

  • 首先,我们创建了一个名为 CoffeeBeverage 的抽象类,它定义了制作咖啡的骨架。其中,prepareCoffee() 方法是一个模板方法,它定义了咖啡制作的步骤。boilWater() 和 pourInCup() 方法是具体方法,表示公共步骤,而 brewCoffee() 和 addCondiments() 方法是抽象方法,留给子类实现。

  • 接下来,我们创建了两个具体类:Americano 和 Latte,分别表示美式咖啡和拿铁咖啡。这两个类继承了 CoffeeBeverage 抽象类,并分别实现了 brewCoffee() 和 addCondiments() 方法。这样,我们就为每种咖啡定义了各自的制作细节。

  • 最后,我们创建了一个测试类 CoffeeShop,在 main() 方法中,我们实例化了 Americano 和 Latte 类,然后分别调用它们的 prepareCoffee() 方法来制作咖啡。运行程序后,你将看到每种咖啡制作过程中的不同步骤。

经典类图

在这里插入图片描述

spring中的例子

Spring 框架中,有一个典型的应用模板方法模式的例子,那就是 JdbcTemplate 类。JdbcTemplateSpring 框架为简化 JDBC 操作而提供的一个工具类,它通过模板方法模式来处理与数据库相关的一些通用操作,例如资源管理、错误处理等。JdbcTemplate 提供了一系列模板方法,如 query(), update() 等,允许开发者仅关注 SQL 语句及结果集的处理,而无需关心底层资源的分配与释放等问题。
下面是一个简单的例子,说明如何使用 Spring 的 JdbcTemplate 类来查询数据库中的数据:

首先,我们需要在项目中引入 Spring JDBC 的依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

接下来,创建一个 User 实体类,用于表示数据库中的用户信息:

@Data
public class User {private int id;private String name;private String email;
}

然后,创建一个 UserDao 接口,定义查询用户的方法:

public interface UserDao {List<User> findAllUsers();
}

创建 UserDaoImpl 类,实现 UserDao 接口,并使用 JdbcTemplate 查询数据库:

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;public class UserDaoImpl implements UserDao {private JdbcTemplate jdbcTemplate;public UserDaoImpl(DataSource dataSource) {jdbcTemplate = new JdbcTemplate(dataSource);}@Overridepublic List<User> findAllUsers() {String sql = "SELECT * FROM users";return jdbcTemplate.query(sql, new UserRowMapper());}private static final class UserRowMapper implements RowMapper<User> {@Overridepublic User mapRow(ResultSet resultSet, int rowNum) throws SQLException {User user = new User();user.setId(resultSet.getInt("id"));user.setName(resultSet.getString("name"));user.setEmail(resultSet.getString("email"));return user;}}
}

在这个例子中,我们使用了 JdbcTemplate 的 query() 方法作为模板方法,它接受一个 SQL 语句和一个自定义的 RowMapper 作为参数。我们只需要实现 RowMapper 的 mapRow() 方法来处理结果集,而无需关心其他底层操作。这正是模板方法模式的优势所在。

总之,Spring 框架中的 JdbcTemplate 是一个很好的模板方法模式实践例子。它简化了数据库操作,使得开发者可以专注于 SQL 语句和结果集处理,而无需关心底层的资源管理和错误处理等问题。

总结

  • 优点:

    • 代码复用:模板方法模式将公共的行为提取到父类中,子类只需实现特定的行为。这有助于减少代码重复,并提高了代码的可维护性。
    • 提高代码的可扩展性:模板方法模式允许在不修改抽象类的前提下,扩展和修改子类的具体行为。这意味着我们可以轻松地添加新的子类以实现新的功能。
    • 封装性良好:模板方法模式将算法的实现细节封装在子类中,父类只需关心算法的整体结构。这有助于将关注点分离,使得代码更容易理解和维护。
    • 便于维护:模板方法模式将公共逻辑集中在父类中,使得维护和修改这些逻辑变得更加简单。此外,对于子类的修改,也可以方便地进行定位和维护。
  • 缺点:

    • 类数量可能增多:由于模板方法模式需要为每个具体实现创建一个子类,这可能导致类数量的增加,从而增加了系统的复杂性。
    • 可能导致过度抽象:在设计模板方法时,如果过度抽象,可能导致子类的实现变得复杂和难以理解。因此,在设计时需要权衡抽象层次和实际需求。
    • 不同子类之间的代码重复:如果子类之间存在相似的行为,可能导致代码重复。在这种情况下,可以通过进一步提取公共代码到父类中或使用其他设计模式来解决这个问题。

总之,模板方法模式是一种非常实用的设计模式,它可以帮助我们在保持代码结构的同时,实现灵活的行为扩展。然而,使用模板方法模式时,需要注意避免类数量的过度增长和过度抽象的问题。

相关文章:

【设计模式】模板方法模式--让你的代码更具灵活性与可扩展性

文章目录 前言模板方法模式的定义核心组成模板方法模式与其他设计模式的区别 代码实现抽象类具体类Client 经典类图spring中的例子 总结 前言 在软件开发中&#xff0c;设计模式是一种经过实践检验的、可复用的解决方案&#xff0c;它们可以帮助我们解决某一特定领域的典型问题…...

搞明白Redis持久化机制

Redis是一种内存数据库&#xff0c;其内存中的数据存储在计算机的内存中&#xff0c;如果服务器发生崩溃或者重启&#xff0c;内存中的数据将会丢失。为了避免这种情况发生&#xff0c;Redis提供了两种持久化机制&#xff1a;RDB和AOF。 一、RDB持久化 Redis支持将当前数据状…...

C# 中的正则表达式,如何使用正则表达式进行字符串匹配和替换?

在 C# 中&#xff0c;可以使用正则表达式进行字符串匹配和替换。正则表达式是一种用来描述字符串模式的语言&#xff0c;可以用来检查一个字符串是否符合某种模式&#xff0c;或者从字符串中提取符合某种模式的子串。下面我们介绍一些常用的正则表达式操作&#xff1a; 创建正…...

7年时间,从功能测试到测试开发月薪30K,有志者事竟成

突破自己的技术瓶颈并不是一蹴而就&#xff0c;还是需要看清楚一些东西&#xff0c;这里也有一些经验和见解跟大家分享一下。同样是职场人士&#xff0c;我也有我的经历和故事。在工作期间&#xff0c;我有过2年加薪5次的小小“战绩”&#xff08;同期进入公司的员工&#xff0…...

ES6 块级作用域

ES6之前没有块级作用域&#xff0c;ES5的var没有块级作用域的概念&#xff0c;只有function有作用域的概念&#xff0c;ES6的let、const引入了块级作用域。 ​ ES5之前if和for都没有作用域&#xff0c;所以很多时候需要使用function的作用域&#xff0c;比如闭包。 1.1.1 什么…...

ShardingSphere-JDBC垂直分片

什么是数据分片&#xff1f; 简单来说&#xff0c;就是指通过某种特定的条件&#xff0c;将我们存放在同一个数据库中的数据分散存放到多个数据库&#xff08;主机&#xff09;上面&#xff0c;以达到分散单台设备负载的效果。 数据的切分&#xff08;Sharding&#xff09;根据…...

Node 04-http模块

HTTP 协议 概念 HTTP&#xff08;hypertext transport protocol&#xff09;协议&#xff1b;中文叫 超文本传输协议 是一种基于TCP/IP的应用层通信协议 这个协议详细规定了 浏览器 和 万维网 服务器 之间互相通信的规则 协议中主要规定了两个方面的内容: 客户端&#xff1…...

记录项目过程中的编译错误及解决方法(持续更新中)

文章目录 前言 前言 记录做项目的时候编译问题&#xff0c;好记性不如烂笔头&#xff0c;下次碰到相同的问题也可以方便查阅 2023.3.22 问题1&#xff1a;每次跑回归测试的时候&#xff0c;总是会出现错误&#xff0c;总共只有5个test&#xff0c;单独跑这个case的时候是没有…...

Android Hilt依赖注入框架

Hilt 是一个基于 Dagger2 的依赖注入框架&#xff0c;它提供了一些简便的注入方式来简化开发者在 Android 应用中使用 Dagger2 的复杂性。Hilt 旨在简化 Android 应用程序中的依赖注入实现&#xff0c;使开发人员能够更轻松地管理依赖项和应用程序的组件。 Hilt 的主要目标是提…...

LeetCode:59. 螺旋矩阵 II

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; &#x1f33b;算法&#xff0c;不如说它是一种思考方式&#x1f340; 算法专栏&#xff1a; &#x1f449;&#x1f3fb;123 一、&#x1f331;59. 螺旋矩阵 II 题目描述&#xff1a;给你一个正整数 n &#xff0c…...

信息安全复习六:公开密钥密码学

一、章节梗概 1.公开密钥密码模型的基本原理 2.两个算法&#xff1a;RSA&D-H算法 主要内容 1.对称密钥密码的密钥交换问题 2.公钥密码模型的提出 3.设计公钥密码的基本要求 4.数字签名 5.RSA算法 6.公钥密码的特征总结 二、对称密钥密码 对称加密算法中&#xff0c;数据…...

YOLOv8 更换主干网络之 ShuffleNetv2

《ShuffleNet V2: Practical Guidelines for Efficient CNN Architecture Design》 目前,神经网络架构设计多以计算复杂度的间接度量——FLOPs为指导。然而,直接的度量,如速度,也取决于其他因素,如内存访问成本和平台特性。因此,这项工作建议评估目标平台上的直接度量,而…...

async/await最详细的讲解

一、async 和 await 在干什么 async 是“异步”的简写&#xff0c;而 await 的意思是等待。async 用于申明一个 function 是异步的&#xff0c;而 await 等待某个操作完成。 async/await 是一种编写异步代码的新方法。之前异步代码的方案是回调和 promise。 async/await 像 p…...

学习数据结构第6天(栈的基本概念)

栈的基本概念 栈的定义栈的基本操作栈的存储结构 栈的定义 栈(Stack)是一种基于先进后出(FILO)或者后进先出(LIFO)的数据结构&#xff0c;是一种只允许在一端进行插入和删除操作的特殊线性表。 栈按照先进后出的原则存储数据&#xff0c;先进入的数据被压入栈底&#xff0c;最…...

自动化添加时间戳版本号

自动化添加时间戳版本号 前言一、静态资源二、版本号的来源三. 版本信息的位置四. 添加时间戳版本号1. 手动添加2. 自动化生成 前言 软件开发和发布过程中&#xff0c;版本是个极其重要的因素。大至操作系统&#xff0c;小到功能组件&#xff0c;都会涉及到版本相关的问题。 …...

【C语言】指针进阶[上] (字符、数组指针、指针数组、数组传参和指针传参)

简单不先于复杂&#xff0c;而是在复杂之后。 目录 1. 字符指针 面试题 2. 指针数组 3. 数组指针 3.1 数组指针的定义 3.2 &数组名 VS 数组名 3.3 数组指针的使用 4. 数组参数、指针参数 4.1 一维数组传参 4.2 二维数组传参 4.3 一级指针传参 4.4 二…...

软件测试外包干了4年,感觉废了..

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入湖南某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…...

ai改写句子软件-ai改写

AI免费伪原创&#xff1a;助力网站内容升级 您是否曾经为网站优化而烦恼&#xff0c;无论是内容更新还是SEO优化&#xff0c;都需要大量的时间和精力。但是&#xff0c;您是否知道&#xff0c;现在有一款能够使用AI技术来帮助您完成这些任务&#xff0c;而且还是免费的呢&…...

zabbix监控linux主机

1.本实验使用centos7主机&#xff0c;IP地址为10.1.60.115&#xff0c;firewalld和selinux服务已关闭 2.下载zabbix yum源(与zabbix server用一样的版本) rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm 3.安装zabbix客户…...

编程中泛型的使用规则和限制是什么?

泛型是一种程序设计风格&#xff0c;它允许程序员在编写代码时使用一些以后才指定的类型&#xff0c;在实例化时作为参数指明这些类型。泛型主要用于实现通用的数据结构&#xff0c;例如集合、映射、列表等&#xff0c;使得这些数据结构可以存储多种类型的元素。 在泛型使用之…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文&#xff5c;魏琳华 编&#xff5c;王一粟 一场大会&#xff0c;聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中&#xff0c;汇集了学界、创业公司和大厂等三方的热门选手&#xff0c;关于多模态的集中讨论达到了前所未有的热度。其中&#xff0c;…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

微服务商城-商品微服务

数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

Java毕业设计:WML信息查询与后端信息发布系统开发

JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发&#xff0c;实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构&#xff0c;服务器端使用Java Servlet处理请求&#xff0c;数据库采用MySQL存储信息&#xff0…...