【设计模式深度剖析】【1】【行为型】【模板方法模式】| 以烹饪过程为例加深理解

👈️上一篇:结构型设计模式对比

文章目录

  • 模板方法模式
  • 定义
    • 英文原话
    • 直译
    • 如何理解呢?
  • 2个角色
    • 类图
    • 代码示例
  • 应用
    • 优点
    • 缺点
    • 使用场景
  • 示例解析:以烹饪过程为例
    • 类图
    • 代码示例

模板方法模式

模板方法模式(Template Method Pattern)是一种行为型设计模式,它通过一个抽象类定义了一个操作的算法骨架,而将一些步骤延迟到子类中实现。

简而言之,模板方法模式就像是一个烹饪食谱,规定了基本的烹饪流程(算法骨架),但允许厨师根据具体食材(子类)调整某些步骤(如烹饪时间、温度等),从而制作出不同风味的菜肴

定义

英文原话

Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.

直译

定义一个操作中的算法框架,进而推迟一些步骤的执行,将其延迟到子类中。模板方法使得子类在不改变算法的结构的情况下,可以改变算法的某些特定步骤。

如何理解呢?

模板方法模式从字面上理解,可以拆分为“模板”和“方法”两个部分。

  1. 模板:指的是一种固定的框架或结构,它定义了某个过程或操作的基本流程或步骤。在模板方法模式中,这个“模板”通常由抽象类中的模板方法提供,该方法定义了一个算法的框架,即算法执行的大致步骤和顺序。
  2. 方法:指的是具体的操作或步骤。在模板方法模式中,这些“方法”通常包括抽象方法(由子类实现)和具体方法(在抽象类中实现)。抽象方法定义了算法中需要子类实现的部分,而具体方法则包含了算法中通用的、不需要子类改变的部分。

我们可以以一个简单的烹饪过程为例。假设我们有一个基本的烹饪流程,其中包含了预热烤箱、烹饪食物和关闭烤箱的步骤。但不同的食物需要不同的烹饪时间和温度,这部分就是可以定制的部分。

假设我们想要烤一个蛋糕和一个披萨。两者都需要预热烤箱,但烹饪时间和温度不同,烹饪完成后都需要关闭烤箱。这里,预热烤箱和关闭烤箱就是模板方法中固定的部分,而烹饪食物则是需要根据不同食物来定制的部分。(见下文示例解析)

2个角色

模板方法模式(Template Method Pattern)中的角色通常包括:

  1. 抽象类(Abstract Class):这个角色定义了一个或多个抽象操作,以便让子类实现。这些抽象操作是基本操作,还需要定义一个或几个模板方法,这些模板方法一般是具体的方法,定义了一个算法的框架。
  2. 具体子类(Concrete Subclasses):这是抽象模板角色的子类,它们实现抽象模板角色中的抽象方法,以完成算法中与特定子类相关的步骤。

通过使用模板方法模式,开发者可以在不改变算法结构的情况下,通过子类来重定义算法的某些特定步骤,从而实现算法的灵活性和可复用性。

类图

在这里插入图片描述

代码示例

下面是一个Java示例,展示了模板方法模式:

package com.polaris.designpattern.list3.behavioral.pattern01.templatemethod.classicdemo;// 抽象类,定义了模板方法
abstract class AbstractClass {// 模板方法,定义了算法的框架  public final void templateMethod() {specificMethod1(); // 调用第一个抽象方法  // 可能还有其他的通用操作或条件判断  specificMethod2(); // 调用第二个抽象方法  }// 抽象操作,子类必须实现  protected abstract void specificMethod1();// 另一个抽象操作,子类也必须实现  protected abstract void specificMethod2();
}// 具体子类A  
class ConcreteClassA extends AbstractClass {// 实现抽象操作  @Overrideprotected void specificMethod1() {System.out.println("ConcreteClassA.specificMethod1()");}// 实现抽象操作  @Overrideprotected void specificMethod2() {System.out.println("ConcreteClassA.specificMethod2()");}
}// 具体子类B  
class ConcreteClassB extends AbstractClass {// 实现抽象操作  @Overrideprotected void specificMethod1() {System.out.println("ConcreteClassB.specificMethod1()");}// 实现抽象操作  @Overrideprotected void specificMethod2() {System.out.println("ConcreteClassB.specificMethod2()");}
}// 客户端代码  
public class TemplateMethodTest {public static void main(String[] args) {AbstractClass classA = new ConcreteClassA();classA.templateMethod(); // 输出 ConcreteClassA.specificMethod1() 和 ConcreteClassA.specificMethod2()  AbstractClass classB = new ConcreteClassB();classB.templateMethod(); // 输出 ConcreteClassB.specificMethod1() 和 ConcreteClassB.specificMethod2()  }
}
/* Output:
ConcreteClassA.specificMethod1()
ConcreteClassA.specificMethod2()
ConcreteClassB.specificMethod1()
ConcreteClassB.specificMethod2()
*///~

在这个示例中,AbstractClass 是抽象类,它定义了一个模板方法 templateMethod(),该方法调用了两个抽象操作 specificMethod1()specificMethod2()ConcreteClassAConcreteClassB 是具体子类,它们分别实现了这两个抽象操作。客户端代码通过调用模板方法来执行算法,而具体的步骤(specificMethod1()specificMethod2())则由不同的子类实现。

模板方法模式(Template Method Pattern)是一种行为型设计模式,它定义了一个操作中的算法框架,而将一些步骤延迟到子类中。这使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。

应用

模板方法模式在以下场景中特别有用:

  1. 算法复用:当多个类有相似的行为,但部分行为需要定制时,可以使用模板方法模式。
  2. 框架设计:在框架设计中,模板方法模式可以帮助实现一些可扩展的、可定制的框架。
  3. 一次性算法:如果一个算法很少改变,但其中的某些步骤可能需要根据具体情况进行定制时,可以使用模板方法模式。

优点

  1. 代码复用:模板方法模式通过把不变行为搬到超类,去除了子类中的重复代码。
  2. 扩展性好:子类可以通过实现或重写抽象方法或钩子方法来改变或扩展算法的部分行为。
  3. 灵活性高:在模板方法模式中,可以通过定义抽象方法和钩子方法来实现算法的灵活性和可扩展性。
  4. 符合开闭原则:对扩展开放,对修改关闭。在模板方法模式中,增加新的功能可以通过增加新的子类来实现,而不需要修改现有的代码。

缺点

  1. 抽象层次提高:模板方法模式会增加类的抽象层次,使得子类之间的关系更加复杂。
  2. 可能产生过多子类:如果每个不同的行为都使用子类来实现,那么类的个数可能会急剧增加。
  3. 性能考虑:由于使用了继承,如果子类过多,可能会影响系统的性能。

使用场景

  1. 创建框架:当我们需要创建一个框架,并希望这个框架具有可扩展性和可定制性时,可以使用模板方法模式。
  2. 实现回调:在某些情况下,我们可能需要让子类在特定的事件发生时执行一些操作,这时可以使用模板方法模式来实现回调。
  3. 资源初始化:当资源的初始化或清理具有固定的流程,但某些步骤可能需要根据具体情况进行定制时,可以使用模板方法模式。
  4. 算法复用:当多个类有相似的行为,但部分行为需要根据具体情况进行定制时,可以使用模板方法模式来复用代码。

模板方法模式是一种非常实用的设计模式,它可以帮助我们更好地组织代码,提高代码的可维护性和可扩展性。

示例解析:以烹饪过程为例

假设我们有一个基本的烹饪流程,其中包含了预热烤箱、烹饪食物和关闭烤箱的步骤。但不同的食物需要不同的烹饪时间和温度,这部分就是可以定制的部分。

假设我们想要烤一个蛋糕和一个披萨。两者都需要预热烤箱,但烹饪时间和温度不同,烹饪完成后都需要关闭烤箱。这里,预热烤箱和关闭烤箱就是模板方法中固定的部分,而烹饪食物则是需要根据不同食物来定制的部分。

类图

在这里插入图片描述

代码示例

package com.polaris.designpattern.list3.behavioral.pattern01.templatemethod.cookingdemo;// 抽象类,代表烹饪流程
abstract class CookingProcess {// 模板方法,定义了烹饪的整个过程  public final void cook() {preheatOven(); // 预热烤箱  cookFood();    // 烹饪食物(需要子类实现)  turnOffOven(); // 关闭烤箱  }// 预热烤箱的具体方法,不需要子类改变  protected void preheatOven() {System.out.println("Preheating oven to 350°F...");// 假设预热完成需要一些时间,这里省略等待逻辑  }// 烹饪食物的方法,需要子类实现  protected abstract void cookFood();// 关闭烤箱的具体方法,不需要子类改变  protected void turnOffOven() {System.out.println("Turning off the oven...");}
}// 蛋糕烹饪类  
class CakeCooking extends CookingProcess {// 实现烹饪食物的方法,这里是烹饪蛋糕  @Overrideprotected void cookFood() {System.out.println("Baking cake for 30 minutes...");// 假设烹饪完成需要一些时间,这里省略等待逻辑  }
}// 披萨烹饪类  
class PizzaCooking extends CookingProcess {// 实现烹饪食物的方法,这里是烹饪披萨  @Overrideprotected void cookFood() {System.out.println("Baking pizza for 15 minutes at 450°F...");// 假设烹饪完成需要一些时间,这里省略等待逻辑  }
}// 客户端代码  
public class CookingDemo {public static void main(String[] args) {CookingProcess cakeCooking = new CakeCooking();cakeCooking.cook(); // 烹饪蛋糕  System.out.println("--------------------");CookingProcess pizzaCooking = new PizzaCooking();pizzaCooking.cook(); // 烹饪披萨  }
}
/* Output:
Preheating oven to 350°F...
Baking cake for 30 minutes...
Turning off the oven...
--------------------
Preheating oven to 350°F...
Baking pizza for 15 minutes at 450°F...
Turning off the oven...
*///~

这个例子展示了模板方法模式如何帮助我们在保持烹饪流程基本框架不变的情况下,为不同的食物定制不同的烹饪步骤。

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

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

相关文章

C++linux下使用clog和重定向实现写日志

Clinux下使用clog和重定向实现写日志 实现文件基本功能测试编译运行额外知识点 实现文件 LogUtil.hpp /** * 通用日志实现 * lsl * 2024-06-04 */#ifndef LOGUTIL_HPP #define LOGUTIL_HPP #include<iostream> #include <time.h> #include <cstring> #defi…

LED驱动IC:HC2161,升压型LED恒流驱动ic,供应LED灯杯单节电池以上供电的LED灯串平板显示LED背光大功率LED照明

LED驱动IC&#xff1a; HC2161:升压型LED恒流驱动ic 概述&#xff1a;HC2161是一款高效率、高精度的升 压型大功率LED恒流驱动控制芯片。 HC2161内置高精度误差放大器&#xff0c;固 定关断时间控制电路&#xff0c;恒流驱动电路等&#xff0c; 特别适合大功率、多个高亮…

七年

七年 我&#xff0c;回来了&#xff0c;七年后。回看之前的文章&#xff0c;当初的情意浓浓&#xff0c;患得患失&#xff0c;真的是恍如隔世。 经历了重重波折&#xff0c;父母反对&#xff0c;奔赴广州&#xff0c;云南危机&#xff0c;房名危机&#xff0c;都没把我们拆散…

鸿蒙开发接口定制管理:【@ohos.configPolicy (配置策略)】

配置策略 配置策略提供按预先定义的定制配置层级获取对应定制配置目录和文件路径的能力。 说明&#xff1a; 本模块首批接口从API version 8开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 本模块接口均为系统接口&#xff0c;三方应用不支持调…

Kaggle平台进行Python版本降级

前言 最近在复现语音合成模型VITS&#xff0c;由于目前没有算力故去Kaggle白嫖运算资源。 VITS的运行环境要求如下 Cython0.29.21 librosa0.8.0 matplotlib3.3.1 numpy1.18.5 phonemizer2.2.1 scipy1.5.2 tensorboard2.3.0 torch1.6.0 torchvision0.7.0 Unidecode1.1.1截至2…

21.过拟合和欠拟合示例

1. 背景介绍 在机器学习和深度学习中&#xff0c;过拟合和欠拟合是两个非常重要的概念。过拟合指的是模型在训练数据上表现很好&#xff0c;但在新的测试数据上效果变差的情况。欠拟合则是指模型无法很好地拟合训练数据的情况。这两种情况都会导致模型无法很好地泛化&#xff…

视频号小店,常见的违规条例!98%的商家必犯的违规细节!

哈喽~我是电商月月 做电商&#xff0c;不管哪个平台都有属于自己的规则条例&#xff0c;这些违规细节&#xff0c;一定要提前了解 所以今天&#xff0c;月月就给大家分享一下&#xff0c;做视频号小店的话&#xff0c;有哪些常见的违规细节 这里我们分三点讲解 一&#xff…

【分享】两种方法禁止修改Word文档

对于比较重要的Word文件&#xff0c;不想被随意编辑修改&#xff0c;可以试试以下两个方法&#xff0c;不清楚的小伙伴&#xff0c;一起来看看吧&#xff01; 方法1&#xff1a;设置“只读方式” 我们可以给Word文档设置以“只读方式”打开&#xff0c;这样就算编辑修改了文档…

如何通过SD-WAN提升企业沟通效率

在数字化飞速发展的今天&#xff0c;企业对大数据和实时商业数据传输的需求日益增长。传统的专线连接技术已无法满足企业对快速部署商业应用和高效网络连接的需求。在这种背景下&#xff0c;SD-WAN成为提升企业网络沟通效率的关键技术。 SD-WAN的灵活部署模式 SD-WAN提供了高度…

6月软考新通知:24下集成大概率是中级蕞简单的一门

2024下半年软考6月新通知&#xff1a; 一、24下软考考试时间安排&#xff1a; 24下半年软考报名时间&#xff1a;8月19日-9月15日 24下半年软考考试时间&#xff1a;11月9-12日 24下半年软考成绩查询&#xff1a;12月中&#xff08;预计&#xff09; 二、考情分析 24上软考…

09_JavaWeb会话

1.会话 HTTP是一种无状态协议&#xff1b; HTTP协议对于发送过请求或者响应都不做持久化处理具体来说就是客户端发送请求&#xff0c;服务器接收请求&#xff0c;但是服务器自身不会记录每一条请求都是由哪一个客户端发出的&#xff1b; 会话管理是通过Cookie和Session配合解…

【排序】插入排序,希尔排序

前面我们讲述了冒泡排序和选择排序&#xff0c;我们本章讲的排序方法是插入排序&#xff0c;插入排序是希尔排序实现的基础函数&#xff0c;大家一定要好好理解插入排序的逻辑&#xff0c;这样才能在后面学习希尔排序的时候&#xff0c;更容易的去理解&#xff0c;我们直接开始…

关于无法通过脚本启动Kafka集群的解决办法

启动Kafka集群时&#xff0c;需要在每台个节点上启动启动服务&#xff0c;比较麻烦&#xff0c;通过写了以下脚本来进行启停&#xff1b;发现能正常使用停止功能&#xff0c;不能正常启动Kafka&#xff1b; Kafka启停脚本&#xff1a; ## 以防不能通过shell脚本启动Kafka服务…

富格林:揭露黑幕平台保障安全

富格林指出&#xff0c;很多黑幕平台都会将自己包装得光鲜亮丽后&#xff0c;再出来诱惑投资者&#xff0c;使得投资者资金安全得不到保障&#xff0c;有苦说不出。富格林表示&#xff0c;黑幕平台的套路其实是非常常见的&#xff0c;只要投资者熟知并能够分辨出&#xff0c;就…

C盘扩容——只能删除C盘右边的磁盘对C盘进行扩展

winR弹出命令框 输入&#xff1a;compmgmt.msc 进入磁盘管理页面 注意&#xff1a;被删除盘如果有重要数据信息&#xff0c;请备份。 或者删除之前转移至其他盘&#xff0c;否则删除之后&#xff0c;则无法找回。 尤其是安装的软件。 规范安装目录十分重要。 将C盘右边的磁盘&a…

最全 Inno Setup 教程-[FILE] Flag参数

【1】此参数是一个附加选项的集合。可以使用空格将多个选项分隔开。 【2】支持以下选项&#xff1a; 32位 当在“Source”和“DestDir”参数中使用{sys}常量时&#xff0c;将该常量映射到32位系统目录。将“regserver”和“regtypelib”标志设置为将文件视为32位&#xff0c;…

安防综合管理系统EasyCVR视频汇聚平台GA/T 1400协议中的关键消息交互示例

在当今的信息化时代&#xff0c;公共安全防范日益成为保障社会和谐稳定的关键。视频监控系统作为现代安全防范的重要手段&#xff0c;正不断在公安、交通、城市管理等领域发挥着越来越重要的作用。而GA/T 1400协议视图库&#xff0c;作为公安视频图像信息应用系统的标准&#x…

Vue3 子组件访问父组件的方法 - 父组件访问子组件的属性或方法 - 子组件修改父组件的值

一。子组件访问父组件的方法 //父组件 <DialogEditing close-dialog"handleClose" /> const handleClose () > {};//子组件 const emit defineEmits(["closeDialog"]); const close () > {emit("closeDialog"); // 使用 };二。父…

健身日记之倒立俯卧撑学习——起始日2024.6.4

文章目录 前言 自我介绍 昔日计划 新目标计划 瓶颈突破尝试 参考视频及文章 前言 有轻微健身基础&#xff0c;正式接触街健五大神技&#xff0c;立志在两年内解锁全部&#xff0c;将有机会的进行日常训练和目标肌群锻炼&#xff0c;这里向大家展示我的计划和安排&#xf…

opencv-python(五)

opencv的颜色通道中顺序是B&#xff0c;G&#xff0c;R。 图像属性 import cv2img cv2.imread(jk.jpg) print(fshape{img.shape}) print(fsize{img.size}) print(fdtype{img.dtype}) shape&#xff1a;图像像素的行&#xff0c;列&#xff0c;通道 size&#xff1a;行数 X …