【设计模式】行为型-模板方法模式

方法千变万化,心灵如潮,模板如画,画出生活的韵味。

文章目录

  • 一、茶与咖啡
  • 二、模板方法模式
  • 三、模板方法模式的核心组成
  • 四、运用模板方法模式
  • 五、模板方法模式的应用场景
  • 六、小结
  • 推荐阅读

一、茶与咖啡

场景假设:我们需要完成茶和咖啡的制作。

// 咖啡类
class Coffee {// 准备配方的方法void prepareRecipe() {boilWater(); // 煮沸水brewCoffeeGrinds(); // 冲泡咖啡pourInCup(); // 倒入杯子addSugarAndMilk(); // 添加糖和牛奶}// 下面是每个步骤的具体实现void boilWater() { System.out.println("Boiling water"); }void brewCoffeeGrinds() { System.out.println("Dripping Coffee through filter"); }void pourInCup() { System.out.println("Pouring into cup"); }void addSugarAndMilk() { System.out.println("Adding Sugar and Milk"); }
}// 茶类
class Tea {// 准备配方的方法void prepareRecipe() {boilWater(); // 煮沸水steepTeaBag(); // 浸泡茶包pourInCup(); // 倒入杯子addLemon(); // 添加柠檬}// 下面是每个步骤的具体实现void boilWater() { System.out.println("Boiling water"); }void steepTeaBag() { System.out.println("Steeping the tea"); }void pourInCup() { System.out.println("Pouring into cup"); }void addLemon() { System.out.println("Adding Lemon"); }
}

咖啡和茶创建两个不同的类,并在每个类中实现整个制作过程。这将导致大量的代码重复,因为煮沸水和倒入杯子的步骤对于咖啡和茶来说都是相同的。在这个例子中,boilWaterpourInCup 在两个类中都是重复的。如果我们需要修改这些步骤的实现,我们需要在两个类中都进行修改,这就增加了维护的复杂性。此外,如果我们需要添加一个新的饮料类型,我们又需要复制大量的代码。

二、模板方法模式

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

该模式提供了一个模板(即抽象类),其中包含了一系列的方法,其中某些方法是抽象的,需要由子类实现,而其他方法是具体的,并且已经实现好了。这样,当客户端调用模板方法时,将按照模板的定义来执行一系列的步骤,其中部分步骤可能在子类中有不同的实现。

三、模板方法模式的核心组成

模板方法模式的核心组件主要包括以下几个部分:

  1. 抽象类(Abstract Class):这个类定义了一系列的方法,包括具体的方法(实现了特定功能)和抽象的方法(没有具体实现,需要子类来提供实现)。此外,抽象类还定义了一个模板方法,这个方法按照特定的顺序调用上述的一系列方法。
  2. 具体类(Concrete Class):这个类继承自抽象类,并提供抽象方法的具体实现。当模板方法被调用时,这些具体的实现将被执行。
  3. 模板方法(Template Method):在抽象类中定义,这个方法包含了一个算法的骨架。它按照特定的顺序调用一系列的方法。其中一些方法是具体的,由抽象类提供实现;另一些方法是抽象的,由具体类提供实现。
  4. 钩子方法(Hook Method):这是一种特殊的方法,它在抽象类中通常没有做任何事或者只提供默认的实现,但是它提供了一个扩展点,允许子类在必要时覆盖它。

在这里插入图片描述

在这个类图中:

  1. AbstractClass 是一个抽象类,它定义了 templateMethod()(模板方法)和两个抽象方法 primitiveOperation1() 和 primitiveOperation2()。模板方法定义了一个算法的骨架,按照特定的顺序调用一系列的方法。其中一些方法是具体的,由抽象类提供实现;另一些方法是抽象的,由具体类提供实现。
  2. ConcreteClass 是一个具体类,它继承自 AbstractClass 并提供抽象方法的具体实现。当模板方法被调用时,这些具体的实现将被执行。

四、运用模板方法模式

场景假设:我们需要制作茶、咖啡等多种饮料。

  1. 定义抽象类:首先,我们需要定义一个抽象类,这个类包含了制作饮料的通用步骤。

    // 抽象类:含有咖啡因的饮料
    abstract class CaffeineBeverage {// 这是我们的模板方法。它定义了算法的步骤。final void prepareRecipe() {boilWater(); // 第一步:煮沸水brew(); // 第二步:冲泡。但是我们并不知道是冲泡咖啡还是茶,所以这是一个抽象方法。pourInCup(); // 第三步:倒入杯子addCondiments(); // 第四步:添加调料。但是我们并不知道是添加什么调料,所以这是一个抽象方法。}// 这两个方法是抽象的,也就是说,我们需要子类来提供具体的实现。abstract void brew();abstract void addCondiments();// 这两个方法是具体的,也就是说,它们在所有子类中都是一样的,所以我们可以将它们的实现放在这个抽象类中。void boilWater() { System.out.println("Boiling water"); }void pourInCup() { System.out.println("Pouring into cup"); }
    }
    

    在这个抽象类 CaffeineBeverage 中,我们定义了一个模板方法 prepareRecipe(),它按照特定的顺序调用一系列的步骤。其中 brew() 和 addCondiments() 是抽象的,我们需要子类来提供具体的实现。

  2. 定义具体类:然后,我们为每种饮料定义一个具体的类,这个类继承自抽象类,并提供抽象方法的具体实现。

    // 具体类:咖啡
    class Coffee extends CaffeineBeverage {// 对于咖啡来说,冲泡就是用热水过滤咖啡void brew() {System.out.println("Dripping Coffee through filter");}// 对于咖啡来说,添加的调料是糖和牛奶void addCondiments() {System.out.println("Adding Sugar and Milk");}
    }// 具体类:茶
    class Tea extends CaffeineBeverage {// 对于茶来说,冲泡就是用热水浸泡茶叶void brew() {System.out.println("Steeping the tea");}// 对于茶来说,添加的调料是柠檬void addCondiments() {System.out.println("Adding Lemon");}
    }
    

    在这两个具体类 Coffee 和 Tea 中,我们提供了 brew() 和 addCondiments() 方法的具体实现。当模板方法 prepareRecipe() 被调用时,这些具体的实现将被执行。

五、模板方法模式的应用场景

模板方法模式通常在以下情况下使用:

  1. 算法框架固定,但具体实现可变:当一个算法的框架已经确定,但其中的一些步骤的具体实现可能会随着应用场景的变化而变化时,可以使用模板方法模式。这样,可以将算法的框架定义在抽象模板中,而将可变的部分留给子类来实现。
  2. 避免代码重复:当多个类中存在相似的算法流程,但具体步骤略有不同,为避免代码重复,可以将这些公共部分提取到抽象模板中,从而避免代码重复,提高代码的复用性。
  3. 提供算法定制化的扩展点:模板方法模式提供了一种在不改变算法结构的情况下,允许子类重新定义算法中特定步骤的方法。这样,可以为客户端提供一种定制化的扩展点,允许客户端根据需要定制算法的具体实现。
  4. 框架设计:模板方法模式在框架设计中也经常被使用,例如在 Web 框架中,可以定义一个抽象的控制器类作为模板,其中包含了请求处理的流程,而具体的控制器类则根据具体业务需求来实现这些步骤。

六、小结

模板方法模式能够帮助我们定义算法的骨架,并允许子类定制算法的具体实现,从而提高了代码的复用性和灵活性,是一种非常有用的设计模式。在实际开发中,我们可以根据具体情况来灵活运用模板方法模式,提高代码的可维护性和扩展性。

推荐阅读

  1. Spring 三级缓存
  2. 深入了解 MyBatis 插件:定制化你的持久层框架
  3. Zookeeper 注册中心:单机部署
  4. 【JavaScript】探索 JavaScript 中的解构赋值
  5. 深入理解 JavaScript 中的 Promise、async 和 await

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

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

相关文章

基于Unet++在kaggle—2018dsb数据集上实现图像分割

目录 1. 作者介绍2. 理论知识介绍2.1 Unet模型介绍 3. 实验过程3.1 数据集介绍3.2 代码实现3.3 结果 4. 参考链接 1. 作者介绍 郭冠群,男,西安工程大学电子信息学院,2023级研究生 研究方向:机器视觉与人工智能 电子邮件&#xff…

Go变量作用域精讲及代码实战

1. 变量的作用域概述 在编程中,变量的作用域(Scope)定义了变量在程序中的可见性和生命周期。理解变量的作用域对于编写健壮且可维护的代码至关重要。Go语言(简称Go)提供了几种不同的作用域类型,使得开发者可…

在大数据时代:为何硬盘仍是数据中心存储的核心

在云计算和人工智能应用场景不断涌现的时代背景下,数据集的价值急剧上升,硬盘对于数据中心运营商来说变得比以往任何时候都更为关键。硬盘存储了全球大部分的艾字节(EB)数据,行业分析师预计,在艾字节持续增…

Oracle数据库面试题-10

1. 描述Oracle数据库体系结构的主要组件。 Oracle数据库体系结构由多个组件组成,这些组件协同工作以确保数据的存储、处理和安全性。以下是Oracle数据库的一些主要组件: 数据库实例(Database Instance):Oracle数据库的…

华为手机USB调试调过登录

【抓包工具】配置:绕过华为手机打开 USB 调试需要先登录华为账号问题 参考上面的文章。但是可能因为没有登录账号,没法切到生产模式。 登录荣耀账号,再试就可以了,记得默认允许电脑调试,然后退出荣耀账号

C++:十大排序

目录 时间复杂度分析 选择排序 引言 算法思想 动图展示 代码实现 (升序) 优化 代码实现 分析 冒泡排序 引言 算法思想 动图展示 代码实现 插入排序 引言 算法思想 动图展示 代码实现 计数排序 引言 算法思想 动图展示 代码实现 桶排序 引言 算法思…

利安科技上市首日股价大涨:2023营收净利润下滑,募资金额大幅缩水

《港湾商业观察》施子夫 6月7日,宁波利安科技股份有限公司(以下简称,利安科技)正式在深交所创业板挂牌上市,股票简称为利安科技,股票代码300784。 上市当天,利安科技股价大涨348.76%。 2022年…

46.Python-web框架-Django - 多语言配置

目录 1.Django 多语言基础知识 1.1什么是Django国际化和本地化? 1.2Django LANGUAGE_CODE 1.3关于languages 1.4RequestContext对象针对翻译的变量 2.windows系统下的依赖 3.django多语言配置 3.1settings.py配置 引用gettext_lazy 配置多语言中间件&#x…

深入理解Elasticsearch集群:节点与分片机制

Elasticsearch作为当下最流行的开源搜索引擎和数据分析引擎之一,其强大的分布式集群能力和可扩展性是其核心优势。在Elasticsearch集群中,节点(Node)和分片(Shard)是两个核心概念,它们共同构成了…

PyTorch -- 最常见损失函数 LOSS 的选择

损失函数:度量模型的预测结果与真实值之间的差异;通过最小化 loss -> 最大化模型表现代码实现框架:设有 模型预测值 f (x), 真实值 y 方法一: 步骤 1. criterion torch.nn.某个Loss();步骤 2. loss criterion(f(x…

广州·2025全国眼睛健康产业博览会眼科医学大会|全国眼博会

广州2025全国眼睛健康产业博览会眼科医学大会,2025年4月10-12日,在广州南丰国际会展中心举办; ——随着时代的进步和科技的飞速发展,人们的眼睛健康问题日益受到关注。为了推动眼睛健康产业的持续发展,加强眼科医学的…

实施ISO 26262与ISO 21434的关键要素分析

随着汽车工业的快速发展和智能化水平的不断提升,汽车的功能性和安全性成为了消费者关注的重点。为了确保车辆的安全性和可靠性,国际标准化组织(ISO)制定了一系列与汽车安全相关的标准,其中ISO 26262(道路车…

set与map的详细封装步骤

目录 一.set与map在STL中的源码 二.修改红黑树 1.插入与查找时的比较方式 2.插入时的返回值 3.补充成员函数 三.封装set与map 1.迭代器的实现 2.函数接口 3.map中的operator[] 四.完整代码 set.h map.h RBTree.h 一.set与map在STL中的源码 想要简单实现set与map 需…

短视频矩阵工具有哪些?如何辨别是否正规?

随着短视频平台的持续火爆,搭建短视频矩阵成为各大品牌商家提高营销效果和完成流量变现的主要方式之一,类似于短视频矩阵工具有哪些等问题也在多个社群有着不小的讨论度。 而就短视频矩阵工具的市场现状而言,其整体呈现出数量不断增长&#x…

使用神卓互联来访问单位内部web【内网穿透神器】

在现代工作环境中,有时我们需要从外部访问单位内部的 web 资源,而神卓互联这款内网穿透神器就能完美地满足这一需求。 使用神卓互联来访问单位内部 web 其实并不复杂,以下是大致的使用步骤和配置方法。 首先,我们需要在单位内部的…

Three.js做了一个网页版的我的世界

前言 笔者在前一阵子接触到 Three.js 后, 发现了它能为前端 3D 可视化 / 动画 / 游戏方向带来的无限可能, 正好最近在与朋友重温我的世界, 便有了用 Three.js 来仿制 MineCraft 的想法, 正好也可以通过一个有趣的项目来学习一下前端 3D 领域 介绍 游戏介绍 相信大家对我的世…

模式识别与机器学习复习题解析(2023春)

文章目录 一、判断题二、填空题三、单选题四、简答题relu激活h1layer2h2 h1w2b2relu激活h2outputout h2w3 b3 一、判断题 1 单层感知机的局限性,它仅对线性问题具有分类能力( )。T 2.多层感知机的问题是隐藏层的权值无法训练( )。T 3.ReLU和Batch Normalization都…

vue3+ Element-Plus 点击勾选框往input中动态添加多个tag

实现效果&#xff1a; template&#xff1a; <!--产品白名单--><div class"con-item" v-if"current 0"><el-form-item label"平台名称"><div class"contaion" click"onclick"><!-- 生成的标签 …

Unity HoloLens2 MRTK 空间锚点 基础教程

Unity HoloLens2 MRTK 空间锚点 基础教程 Unity HoloLens2 空间锚点MRTK 空间锚点 准备Unity 工程创建设置切换 UWP 平台UWP 平台设置 下载并安装混合现实功能工具导入混合现实工具包和 OpenXR 包 Unity 编辑器 UWP 设置Unity 2019.4.40 设置Unity 2022.3.0 设置Unity 2022.3.0…

CDH远程监控所有HDFS节点磁盘空间和自动清除日志

目录 1. SSH协议的重要性 2. JSch库简介 3. 项目依赖配置 4. 亲测可用的代码实现 5. 实际应用场景 6. 安全性和最佳实践 在大数据运维领域,自动化是一项至关重要的技能。随着集群规模的扩大和业务的复杂化,手动检查和操作各个服务器上的Hadoop分布式文件系统(HDFS)状…