行为型设计模式——模板方法模式

学习难度:⭐ ,比较常用

模板方法模式

在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关。例如拿泡茶这件事来说,可以分为4个步骤,第一步洗茶具,第二步烧开水,第三步放入茶叶并根据不同的茶叶泡不同的时间,第四步品茶。以上的一二四步都是一样的,只有第三步不一样,因此可以将一二四步具体实现好,即模板方法。第三步则是用户自己需要实现的方法,即抽象方法。模板方法的定义: 定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。

模板方法(Template Method)模式包含以下主要角色:

  • 抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。

    • 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。

    • 基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:

      • 抽象方法(Abstract Method) :一个抽象方法由抽象类声明、由其具体子类实现。

      • 具体方法(Concrete Method) :一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。

      • 钩子方法(Hook Method) :在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。

        一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。

  • 具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。

案例

【例】炒菜

炒菜的某些步骤是固定的,分为开启灶台、倒油、炒菜、倒调料品、起锅共五个步骤。假设炒菜和倒调料品两个步骤不一样,其他都一样,现通过模板方法模式来用代码模拟。类图如下:

在这里插入图片描述

代码

编写抽象模板类,其中开炉灶、倒油、起锅都是固定的方法,而炒菜和放调味品则是根据不同的菜品而不同的,而cook()方法则是一个将上述步骤组合调用的一个方法,使用final修饰不可被重写,如下:

// 烹饪抽象类
public abstract class AbstractCook {// 做菜很多步骤都一样的,只是关键步骤不一样,// 因此,相同步骤作为模板实现好,不同的自己实现private String food;public AbstractCook(String food){this.food = food;}public void openStove(){//1. 开启灶台是相同步骤System.out.println("打开灶台,将锅烧热");}public void pourOil(){//2. 倒油是相同步骤System.out.println("油倒入锅中,并烧热");}//3. 翻炒时间和手法不一样public abstract void fry();//4. 不同菜的调味品不一样public abstract void pourSauce();//5. 出锅是一样的public void takeFood(){System.out.println("将炒好的"+food+"盛入餐盘中");}// final 修饰不被继承public final void cook(){openStove();pourOil();fry();pourSauce();takeFood();}
}

然后具体的菜品的制作继承上述模板类,其中共同的步骤就不用重新写了,只需要重写自己的炒菜和放调味品的具体方法

// 炒贵州黄牛肉类
public class CookBeef extends AbstractCook{public CookBeef() {super("贵州黄牛肉");}@Overridepublic void fry() {System.out.println("牛肉下锅,总共炒10分钟,每个30秒翻一下");}@Overridepublic void pourSauce() {System.out.println("放盐和味精,其他放酱油、蒜末、辣椒、葱花去腥提鲜");}
}// 炒青菜类
public class CookVegetable extends AbstractCook{public CookVegetable() {super("青菜");}@Overridepublic void fry() {System.out.println("蔬菜下锅,总共炒2分钟,每个5秒翻一下");}@Overridepublic void pourSauce() {System.out.println("放盐和味精,其他放耗油即可");}
}

客户端测试类:

public class Main {public static void main(String[] args) {// 炒牛肉CookBeef cookBeef = new CookBeef();cookBeef.cook();System.out.println("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=");// 炒青菜CookVegetable cookVegetable = new CookVegetable();cookVegetable.cook();}
}

输出结果:

打开灶台,将锅烧热
油倒入锅中,并烧热
牛肉下锅,总共炒10分钟,每个30秒翻一下
放盐和味精,其他放酱油、蒜末、辣椒、葱花去腥提鲜
将炒好的贵州黄牛肉盛入餐盘中
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
打开灶台,将锅烧热
油倒入锅中,并烧热
蔬菜下锅,总共炒2分钟,每个5秒翻一下
放盐和味精,其他放耗油即可
将炒好的青菜盛入餐盘中

优点

  • 提高代码复用性

    将相同部分的代码放在抽象的父类中,而将不同的代码放入不同的子类中。

  • 实现了反向控制

    通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制 ,并符合“开闭原则”。

缺点

  • 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
  • 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。

适用场景

  • 算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。
  • 需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。

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

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

相关文章

SpringBoot项目的两种发布方式(jar包和war包)

SpringBoot项目的两种发布方式&#xff08;jar包和war包&#xff09; 在springboot入门和项目示例这个项目和application.yml配置端口号和访问url路径基础上进行修改 1、使用jar包方式发布 1.1、在pom.xml中添加一个SpringBoot的构建的插件 <build><plugins>&l…

腾讯云服务器多少钱?2024年腾讯云服务器报价明细表

腾讯云服务器租用价格表&#xff1a;轻量应用服务器2核2G3M价格62元一年、2核2G4M价格118元一年&#xff0c;540元三年、2核4G5M带宽218元一年&#xff0c;2核4G5M带宽756元三年、轻量4核8G12M服务器446元一年、646元15个月&#xff0c;云服务器CVM S5实例2核2G配置280.8元一年…

测试人,你还在写用例吗?是什么在支撑着你写?

测试交付的过程&#xff0c;通常是伴随的是一个测试用例生命周期过程&#xff0c;通常有测试需求分析、测试用例设计、测试用例实现、测试用例执行&#xff0c;以及测试用例管理等几个阶段组成。 为什么要有测试用例&#xff1f; 首先测试用例这是测试岗位的基本交付物之一。开…

第10章-特殊函数-贝塞尔函数

贝兹函数又称贝塞尔曲线&#xff0c;是计算机图形学中相当重要的参数曲线&#xff0c;在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。 贝塞尔函数由线段和节点组成&#xff0c;节点是可拖动的支点&#xff0c;线段像可伸缩的皮筋&#xff0c;通过 控制曲线上的4个点&…

LeetCode 144. 94. 145. 二叉树的前序,中序,后续遍历(详解) ੭ ᐕ)੭*⁾⁾

经过前面的二叉树的学习&#xff0c;现在让我们实操来练练手~如果对二叉树还不熟悉的小伙伴可以看看我的这篇博客~数据结构——二叉树&#xff08;先序、中序、后序及层次四种遍历&#xff08;C语言版&#xff09;&#xff09;超详细~ (✧∇✧) Q_Q-CSDN博客 144.二叉树的前序遍…

你知道程序员如何利用citywork实现财富自由吗?

周末到了&#xff0c;我要去citywalk寻找心灵的呼吸&#xff01;”有谁没有设想过疲惫的工作日之后好好地去走一走&#xff0c;亲近大自然呢&#xff1f;谁又不想在闲暇之余唤起对生活的趣味呢&#xff1f;可是对于我们悲催的打工人而言&#xff0c;没有citywalk&#xff0c;只…

class_5:在c++中一个类包含另一个类的对象叫做组合

#include <iostream> using namespace std;class Wheel{ public://成员数据string brand; //品牌int year; //年限//真正的成员函数void printWheelInfo(); //声明成员函数 };void Wheel::printWheelInfo() {cout<<"我的轮胎品牌是&#xff1a;"<…

任务10:安装配置Java开发环境

任务描述 知识点&#xff1a; Java开发工具Maven配置 重 点&#xff1a; 安装配置Java开发工具 IDEA为IDEA配置自定义Maven&#xff08;国内源&#xff09; 内 容&#xff1a; 下载并配置JDK 1.8下载安装IDEA为IDEA配置自定义MavenWindows环境安装配置Hadoop 任务指导…

C#/.NET学习值得推荐的在线论坛和技术社区

前言 本文来源于知乎的一个提问&#xff0c;C#/.NET程序员学习有哪些值得推荐的在线论坛和技术社区&#xff1f;其实很早之前DotNetGuide就已经新增了C#/.NET/.NET Core充电站栏目&#xff0c;当然大家有更好的.NET相关学习站点和资源欢迎PR投稿&#x1f91e;。 GitHub - YSGS…

GO——cobra

定义 Cobra 是 Go 的 CLI 框架 CLI&#xff0c;command-line interface&#xff0c;命令行界面 使用 注意 第一个cmd的USE即使命名了也没有意义&#xff0c;一般保持和项目名一致。 示例 package mainimport ("fmt""github.com/spf13/cobra" )func …

C语言中对变量的理解

变量(variable)是程序中不可或缺的组成单位&#xff0c;是最基本的存储单元。 1.什么是变量&#xff1f; Ⅰ.概念&#xff1a; 内存中的一个存储区域&#xff0c;该区域的数据可以在同一类型范围内不断变化。 通过变量名&#xff0c;可以访问这块内存区域&#xff0c;获取里…

信息安全导论期末复习

信息安全导论 第二章 古典密码 凯撒密码 维吉尼亚密码 仿射加密 这是一种扩展的移位变换 示例&#xff1a; 仿射加密难点在于求逆元 近代密码 希尔密码 现代密码 数学基础 辗转相除法 用于求最大公因子&#xff0c;最大公因子的值就是所列式子中最后一个不为零的余…

6.3、SDN在云计算中的应用

目录 一、SDN概念 1.1、传统网络机制 1.2、SDN网络机制 1.3、二者区别 1.4、SDN架构 二、云数据中心 2.1、公有云环境特点 2.2、两大挑战 2.3、云数据中心引入SDN技术解决两大挑战 三、SDN云计算解决方案 3.1、SDN云计算解决方案之控制平面openflow协议 3.1.…

Linux的基础命令学习

pwd - 显示当前工作目录的路径 cd - 切换工作目录&#xff0c;ls - 列出当前目录的文件和子目录 rm - 删除文件或目录 mkdir - 创建新目录 rm - 删除目录 nano/vi - 编辑文本文件&#xff0c;按Enter键进入 之后按i键就可以进入写入模式 之后输入文字以后按Esc键与:q就不保…

C# 图解教程 第5版 —— 第22章 命名空间和程序集

文章目录 22.1 引用其他程序集22.2 命名空间22.2.1 命名空间名称22.2.2 命名空间的补充22.2.3 命名空间跨文件伸展22.2.4 嵌套命名空间 22.3 using 指令22.3.1 using 命名空间指令22.3.2 using 别名指令22.3.3 using static 指令 22.4 程序集的结构22.5 程序集标识符22.6 强命名…

Camunda Asynchronous continuations

示例一 Service public class ExceptionService implements JavaDelegate {Overridepublic void execute(DelegateExecution execution) {System.out.println(1/0);} }ACT_RU_TASK &#xff1a;明明是UserTask2完成任务了&#xff0c;ServiceTask内部出错了&#xff0c;按说事务…

Spring Boot接口请求响应慢,超过10秒以上,如无法优化SQL或代码的情况下,建议写入数据库或缓存中,请求接口时从数据库或缓存中读取返回

举例 Override public Map<String, Object> getCockpitStaffAttendanceTask() {Map<String, Object> map new HashMap<>();int chuqin 0; //出勤int queqin 0; //缺勤int chidao 0; //迟到int zaotui 0; //早退//获取所有设备卡号 并且已经绑定了人Lis…

MS-DETR: Efficient DETR Training with Mixed Supervision论文学习笔记

论文地址&#xff1a;https://arxiv.org/pdf/2401.03989.pdf 代码地址&#xff08;中稿后开源&#xff09;&#xff1a;GitHub - Atten4Vis/MS-DETR: The official implementation for "MS-DETR: Efficient DETR Training with Mixed Supervision" 摘要 DETR 通过迭代…

微调您的Embedding模型以最大限度地提高RAG管道中的相关性检索

英文原文地址&#xff1a;https://betterprogramming.pub/fine-tuning-your-embedding-model-to-maximize-relevance-retrieval-in-rag-pipeline-2ea3fa231149 微调您的Embedding模型以最大限度地提高RAG管道中的相关性检索 微调嵌入前后的 NVIDIA SEC 10-K 文件分析 2023 年…

高效工作法:占位图片生成工具助力项目快速迭代

在现代设计和开发项目中&#xff0c;图片资源的重要性不言而喻。然而&#xff0c;项目中经常会遇到寻找合适图片、调整图片尺寸和格式等问题&#xff0c;这些问题不仅耗时耗力&#xff0c;还可能影响到项目的进度和质量。此时&#xff0c;占位图片生成工具应运而生&#xff0c;…