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

设计模式浅析(九) ·模板方法模式

设计模式浅析(九) ·模板方法模式

日常叨逼叨

java设计模式浅析,如果觉得对你有帮助,记得一键三连,谢谢各位观众老爷😁😁


模板方法模式

概念

模板方法模式(Template Method Pattern)在Java中是一种非常实用的设计模式,它允许我们在一个方法中定义一个算法的骨架,同时允许子类在不改变算法结构的情况下重新定义某些步骤的具体内容。这种模式非常适合于那些算法的整体步骤固定,但某些步骤的具体实现可能因需求不同而有所变化的场景。

组成
  1. 抽象类(Abstract Class)
    • 定义了一个或多个抽象方法,这些抽象方法由具体子类来实现。
    • 包含一个模板方法,这个方法定义了算法的骨架,并调用了在抽象类中定义的抽象方法。
    • 模板方法通常被声明为final,以防止子类覆盖它。
    • 模板方法通常还包含一些普通方法,这些方法可以被具体子类直接使用或覆盖。
  2. 具体子类(Concrete Subclasses)
    • 实现了抽象类中定义的抽象方法,提供了这些抽象方法的具体实现。
    • 通过继承抽象类,子类可以重写普通方法,以提供不同的实现。
    • 子类可以通过调用父类的模板方法来执行算法。
  3. 客户端
    • 客户端代码创建具体类的实例,并调用模板方法来执行算法。

案例

继续以一家饮品店为例子:

A饮品店有咖啡(coffee)和茶(tea)两种饮品,对于其制作方法如下:

  • 咖啡冲泡法

    • (1)把水煮沸

    • (2)用沸水冲泡咖啡

    • (3)把咖啡倒进杯子

    • (4)加糖和牛奶

  • 茶水冲泡法

    • (1)把水煮沸

    • (2)用沸水浸泡茶叶

    • (3)把茶倒进杯子

    • (4)加柠檬

让我们扮演冲泡师傅,快速的创建茶水和咖啡

//coffee
public class Coffee {void prepareRecipe() {boilWater();brewCoffeeGrinds();pourInCup();addSugarAndMilk();}public void boilWater() {System.out.println("step 1 Boiling water");}public void brewCoffeeGrinds() {System.out.println("step 2 Dripping Coffee through filter");}public void pourInCup() {System.out.println("step 3 Pouring into cup");}public void addSugarAndMilk() {System.out.println("step 4 Adding Sugar and Milk");}
}
//Tea
public class Tea {void prepareRecipe() {boilwater();steepTeaBag();pourInCup();addLemon();}public void boilwater() {System.out.println("step 1 Boiling water");}public void steepTeaBag() {System.out.println("step 2 Steeping the tea");}public void addLemon() {System.out.println("step 3 Adding Lemon");}public void pourInCup() {System.out.println("step 4 Pouring into cup");}
}

仔细观察上述的代码,会发现coffee和tea 除了第2步骤和第4步骤不一样外,其他的步骤几乎一致,代码存在者较高的冗余。是不是可以将一些代码抽取出来?

那么第一次抽取之后,类图可能会变成这个样子

在这里插入图片描述

但是仔细想一想上面的制作方法

在这里插入图片描述

让我们来思考这一点:浸泡(steep)和冲泡(brew)差异其实不大。所以我们给它一个新的方法名称,比方说brew(),然后不管是泡茶或冲泡咖啡我们都用这个名称。

类似地,加糖和牛奶也和加柠檬很相似:都是在饮料中加入调料,由此,我们再做一次公共的提取。

首先,我们定义一个抽象类DrinksMake,在其中定义一个final的方法,规定具体的步骤:

abstract class DrinksMake {//模板方法public final void makeDrinks() {//step 1 烧水boiledWater();//step 2 冲泡brew();//step3 倒出到杯子中pourInCup();//step4 添加配料addTheIngredients();}protected abstract void addTheIngredients();protected abstract void brew();protected void boiledWater() {System.out.println("step1:boiled water!");}protected void pourInCup() {System.out.println("step3: pourInCup !");}}

然后,我们定义制作咖啡的具体实现以及茶水的具体实现

public class CoffeeMake extends DrinksMake {@Overrideprotected void addTheIngredients() {System.out.println("step 4 : add Sugar and Milk into Coffee");}@Overrideprotected void brew() {System.out.println("step 2 : brew Coffee");}
}
public class TeaMake extends DrinksMake {@Overrideprotected void addTheIngredients() {System.out.println("step 4 : add Lemon into tea");}@Overrideprotected void brew() {System.out.println("step 2 : brew tea");}
}

那么对于客户端来说,我们分别制作一杯咖啡和一杯茶

public class Client {public static void main(String[] args) {TeaMake teaMake = new TeaMake();CoffeeMake coffeeMake = new CoffeeMake();teaMake.makeDrinks();coffeeMake.makeDrinks();}
}

运行结果:

step1:boiled water!
step 2 : brew tea
step3: pourInCup !
step 4 : add Lemon into tea
step1:boiled water!
step 2 : brew Coffee
step3: pourInCup !
step 4 : add Sugar and Milk into Coffee

Process finished with exit code 0

至此我们实现了设计模式中的模板方法模式

模板方法模式中的钩子hook

我们在上述的抽象父类中再加入一个方法

void hook(){}
//但是这个方法暂时什么都不做

我们也可以有默认不做事的方法,我们称这神方法为hook(钩子)。子类可以视情况决定要不要覆盖它们。接下来就会知道钩子的实际用途。

试想一下,如果在step4,添加小料的时候,是不是应该询问一下顾客的意见?不能问不问就为顾客添加一些小料,这个是不太合理的,那么我们的代码可以这么修改:

abstract class DrinksMake {//模板方法public final void makeDrinks() {//step 1 烧水boiledWater();//step 2 冲泡brew();//step3 倒出到杯子中pourInCup();//step4 添加配料if (ifNeedIngredients()) {addTheIngredients();}}protected abstract void addTheIngredients();protected abstract void brew();protected void boiledWater() {System.out.println("step1:boiled water!");}protected void pourInCup() {System.out.println("step3: pourInCup !");}/**** 钩子方法,子类可以选择重写或者不重写*/boolean ifNeedIngredients() {return true;}}

创建一个类似于钩子的方法,默认返回是添加小料,可以在子类中进行重写进行相关的操作,我们可以在子类中进行询问顾客是否需要小料

public class CoffeeMakeWithHook extends DrinksMake {@Overrideprotected void addTheIngredients() {System.out.println("step 4 : add Sugar and Milk into Coffee");}@Overrideprotected void brew() {System.out.println("step 2 : brew Coffee");}@Overrideboolean ifNeedIngredients() {String answer = getUserInput();if (answer.toLowerCase().startsWith("y")) {return true;} elsereturn false;}//  让用户输入他们对调料的决定。根据用户的输入返回tue或falseprivate String getUserInput() {String answer = null;System.out.print("would you like milk and sugar with your coffee (y/n) ?");BufferedReader in = new BufferedReader(new InputStreamReader(System.in));try {answer = in.readLine();} catch (IOException e) {System.err.println("IO error trying to read your answer");}if (answer == null) {return "no";}return answer;}
}

再次创建客户端代码,进行coffeewithhook的调用

public class Client {public static void main(String[] args) {CoffeeMakeWithHook coffeeMake = new CoffeeMakeWithHook();coffeeMake.makeDrinks();}
}

运行结果如下:

不需要小料:

step1:boiled water!
step 2 : brew Coffee
step3: pourInCup !
would you like milk and sugar with your coffee (y/n) ?n

Process finished with exit code 0

需要小料:

step1:boiled water!
step 2 : brew Coffee
step3: pourInCup !
would you like milk and sugar with your coffee (y/n) ?y
step 4 : add Sugar and Milk into Coffee

Process finished with exit code 0

优缺点

优点:
  1. 代码复用:模板方法模式允许你定义一个算法的骨架,而将一些步骤延迟到子类中。这允许子类在不改变算法结构的情况下重定义某些步骤的具体内容,从而实现了代码的复用。
  2. 扩展性:由于模板方法模式将算法中的固定部分与可变部分分离,这使得子类可以通过扩展来增加新的行为,从而提高了代码的扩展性。
  3. 封装性:通过将不变的部分封装在父类中,模板方法模式隐藏了实现细节,只暴露必要的接口给子类。这有助于减少子类的耦合度。
  4. 行为控制:模板方法模式允许通过子类覆盖父类的钩子方法来决定某一特定步骤是否需要执行,从而提供了一种反向控制结构。
缺点:
  1. 代码可读性:由于模板方法模式中的钩子方法和抽象方法的调用顺序可能比较复杂,这可能会增加代码的阅读难度,尤其是对于新手来说。
  2. 继承的缺点:模板方法模式依赖于继承来实现代码复用,而继承本身有一些固有的缺点,如破坏封装性、子类与父类之间的强耦合等。
  3. 扩展限制:如果父类中的模板方法被声明为final,那么子类将无法覆盖该方法,这可能会限制子类的扩展性。

总的来说,Java模板方法模式在需要定义一系列复杂算法时非常有用,它能够有效地实现代码复用和扩展。然而,它也有一些缺点需要注意,如代码可读性和继承的局限性等。因此,在使用模板方法模式时,需要根据具体情况权衡其优缺点。


代码相关代码可以参考 代码仓库🌐

ps:本文原创,转载请注明出处


相关文章:

设计模式浅析(九) ·模板方法模式

设计模式浅析(九) 模板方法模式 日常叨逼叨 java设计模式浅析,如果觉得对你有帮助,记得一键三连,谢谢各位观众老爷😁😁 模板方法模式 概念 模板方法模式(Template Method Pattern)在Java中是…...

无用工作、UBI与AI

有些隐晦和黑暗的事实无法陈述,因为任何的系统中“无用”的结局都是被无情的抛弃和淘汰,AI监督下的人类结局更是如此。 什么是无用工作? 无用无效工作通常指的是那些看似忙碌但实际上对社会或个人没有实质性贡献的工作。这类工作可能包括以下…...

【监控】grafana图表使用快速上手

目录 1.前言 2.连接 3.图表 4.job和path 5.总结 1.前言 上一篇文章中,我们使用spring actuatorPrometheusgrafana实现了对一个spring boot应用的可视化监控。 【监控】Spring BootPrometheusGrafana实现可视化监控-CSDN博客 其中对grafana只是打开了一下&am…...

Django常用命令

一、新建一个新项目 django-admin startproject project_name二、新建一个app 在Django中的一个app代表一个功能模块。开发者可以将不同功能的模块放在不同的app中, 方便代码的复用。 python manage.py startapp appa_name三、数据迁移(更新数据库) 编写好了Model后&#x…...

【center-loss 中心损失函数】 原理及程序解释(更新中)

文章目录 前言问题引出open-set问题抛出 解决方法softmax函数、softmax-loss函数解决代码(center_loss.py)原理程序解释 如何梯度更新首先了解一下基本的梯度下降算法然后 前言 学习一下: 中心损失函数,用于用于深度人脸识别的特…...

什么是 HTTPS 证书?作用是什么?

HTTPS 证书,即超文本传输安全协议证书(Hypertext Transfer Protocol Secure),是网站安全的关键组成部分。它通过 SSL/TLS 加密协议,确保用户与网站之间的数据传输是加密和安全的。 什么是 HTTPS 证书? HT…...

为什么软考报名人数越来越多?

2020年软考报名人数404666人,广东省报考人数超过14万人。 ●2021年软考通信考试报名人数突破100万人,估计软考有90多万。 ●2022年软考通信考试共129万人,估计软考占了120多万人。 ●2023年软考具体报名人数没有公布,但工业和信…...

【投稿优惠|快速见刊】2024年图像,机器学习和人工智能国际会议(ICIMLAI 2024)

【投稿优惠|快速见刊】2024年图像,机器学习和人工智能国际会议(ICIMLAI 2024) 重要信息 会议官网:http://www.icimlai.com会议地址:深圳召开日期:2024.03.30截稿日期:2024.03.20 (先…...

html2canvas 将DOM节点转成图片

官网地址:html2canvas - Screenshots with JavaScript 将js文件保存到本地 可以新建一个txt文件,然后丢进去修改后缀名称即可。 在项目中引入js文件: import html2canvas from "../html2canvas.min.js" 这是我准备画的DOM节点。…...

【多线程】常见锁策略详解(面试常考题型)

目录 🌴 乐观锁 vs 悲观锁🎍重量级锁 vs 轻量级锁🍀自旋锁(Spin Lock)🎋公平锁 vs ⾮公平锁🌳可重⼊锁 vs 不可重⼊锁🎄读写锁⭕相关面试题 常⻅的锁策略 注意: 接下来讲解的锁策略不…...

Python列表操作函数

在Python中,列表(list)是一种可变的数据类型,它包含一系列有序的元素。Python提供了一系列内置的函数和方法来操作列表。以下是一些常用的Python列表操作函数和方法: 列表方法 append(x) 将元素x添加到列表的末尾。 …...

Qt注册类对象单例与单类型区别

1.实现类型SingletonTypeExample #ifndef SINGLETONTYPEEXAMPLE_H #define SINGLETONTYPEEXAMPLE_H#include <QObject>class SingletonTypeExample : public QObject {Q_OBJECT public://只能显示构造类对象explicit SingletonTypeExample(QObject *parent nullptr);//…...

Rocky Linux 运维工具yum

一、yum的简介 ​​yum​是用于在基于RPM包管理系统的包管理工具。用户可以通过 ​yum​来搜索、安装、更新和删除软件包&#xff0c;自动处理依赖关系&#xff0c;方便快捷地管理系统上的软件。 二、yum的参数说明 1、install 用于在系统的上安装一个或多个软件包 2、seach 用…...

linux下的ollama

refs: https://github.com/ollama/ollama/blob/main/docs/linux.md 1)安装 curl -fsSL https://ollama.com/install.sh | sh 2)修改服务配置&#xff0c;打开端口允许所有IP地址 refs(https://github.com/ollama/ollama/blob/main/docs/faq.md#where-are-models-stored) C…...

YOLOv9详细解读,改进提升全面分析(附YOLOv9结构图)

&#x1f951; Welcome to Aedream同学 s blog! &#x1f951; 文章目录 1. 概要1.1 模型结构上的改动:1.2 训练脚本上的改动&#xff1a; 2. 介绍2.1 背景2.2 主要贡献 3. 总体框架3.1 可编程梯度信息&#xff08;PGI&#xff09;3.1.1 辅助可逆分支3.1.2 多级辅助信息 3.2 Ge…...

html基础操练和进阶修炼宝典

文章目录 1.超链接标签2.跳锚点3.图片标签4.表格5.表格的方向属性6.子窗口7.音视频标签8.表单9.文件上传10.input属性 html修炼必经之路—各种类型标签详解加展示&#xff0c;关注点赞加收藏&#xff0c;防止迷路哦 1.超链接标签 <!DOCTYPE html> <html lang"en…...

从Mysql 数据库删除重复记录只保留其中一条(删除id最小的一条)

准备工作&#xff1a;新建表tb_coupon /*Navicat Premium Data TransferSource Server : rootlocalhostSource Server Type : MySQLSource Server Version : 50527Source Host : localhost:3306Source Schema : leyouTarget Server Type : My…...

从http到websocket

阅读本文之前&#xff0c;你最好已经做过一些websocket的简单应用 从http到websocket HTTP101HTTP 轮询、长轮询和流化其他技术1. 服务器发送事件2. SPDY3. web实时通信 互联网简史web和httpWebsocket协议1. 简介2. 初始握手3. 计算响应健值4. 消息格式5. WebSocket关闭握手 实…...

UE5 C++ Widget练习 Button 和 ProgressBar创建血条

一. 1.C创建一个继承Widget类的子类&#xff0c; 命名为MyUserWidget 2.加上Button 和 UserWidget的头文件 #include "CoreMinimal.h" #include "Components/Button.h" #include "Blueprint/UserWidget.h" #include "MyUserWidget.genera…...

抖店无货源违规频发,不能入驻?这个是真的吗?

我是电商珠珠 还没有踏入抖店这个电商行业的新手&#xff0c;单从别人的口中&#xff0c;听说了抖店无货源特别容易违规&#xff0c;还会被扣除全部的保证金&#xff0c;得不偿失之类的话。有的还专门劝诫新手不要做抖店&#xff0c;做了就会亏本之类的话&#xff0c;这搞得人…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录

ASP.NET Core 是一个跨平台的开源框架&#xff0c;用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录&#xff0c;以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

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

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

在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用

1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...