设计模式 模板方法模式
01.如果接到一个任务,要求设计不同型号的悍马车

02.设计一个悍马车的抽象类(模具,车模)
public abstract class HummerModel {/** 首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正* 是要能够发动起来,那这个实现要在实现类里了*/public abstract void start(); //能发动,那还要能停下来,那才是真本事public abstract void stop(); //喇叭会出声音,是滴滴叫,还是哔哔叫public abstract void alarm(); //引擎会轰隆隆的响,不响那是假的public abstract void engineBoom(); //那模型应该会跑吧,别管是人推的,还是电力驱动,总之要会跑public abstract void run();
}
03.具体不同版本的悍马,这个是抽象类的具体子类
H1 型号悍马的定义如下:
public class HummerH1Model extends HummerModel { @Override public void alarm() { System.out.println("悍马H1鸣笛..."); } @Overridepublic void engineBoom() { System.out.println("悍马H1引擎声音是这样在..."); } @Overridepublic void start() { System.out.println("悍马H1发动..."); }@Overridepublic void stop() { System.out.println("悍马H1停车..."); } /** 这个方法是很有意思的,它要跑,那肯定要启动,停止了等,也就是要调其他方法*/@Overridepublic void run() { //先发动汽车this.start(); //引擎开始轰鸣this.engineBoom(); //然后就开始跑了,跑的过程中遇到一条狗挡路,就按喇叭this.alarm(); //到达目的地就停车this.stop(); }
}
然后看悍马 H2 型号的实现:
public class HummerH2Model extends HummerModel { @Overridepublic void alarm() { System.out.println("悍马H2鸣笛..."); } @Overridepublic void engineBoom() { System.out.println("悍马H2引擎声音是这样在..."); } @Overridepublic void start() { System.out.println("悍马H2发动..."); } @Overridepublic void stop() { System.out.println("悍马H1停车..."); } /** H2要跑,那肯定要启动,停止了等,也就是要调其他方法*/@Overridepublic void run() { //先发动汽车this.start(); //引擎开始轰鸣this.engineBoom(); //然后就开始跑了,跑的过程中遇到一条狗挡路,就按喇叭this.alarm(); //到达目的地就停车this.stop(); }
}
然后程序写到这里,你就看到问题了,run 方法的实现应该在抽象类上(子类改写的都一样),不应该在实现类上,好,我们修改一下类图和实现:

就把 run 方法放到了抽象类中,那代码也相应的改变一下,先看 HummerModel.java:
04.修改后的抽象类
public abstract class HummerModel { /** 首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正* 是要能够发动起来,那这个实现要在实现类里了*/public abstract void start(); //能发动,那还要能停下来,那才是真本事public abstract void stop(); //喇叭会出声音,是滴滴叫,还是哔哔叫public abstract void alarm(); //引擎会轰隆隆的响,不响那是假的public abstract void engineBoom(); //那模型应该会跑吧,别管是人退的,还是电力驱动,总之要会跑public void run() { //先发动汽车this.start(); //引擎开始轰鸣this.engineBoom(); //然后就开始跑了,跑的过程中遇到一条狗挡路,就按喇叭this.alarm(); //到达目的地就停车this.stop(); }
}
05.修改后的抽象类的子类:
public class HummerH1Model extends HummerModel { @Override public void alarm() { System.out.println("悍马H1鸣笛..."); } @Overridepublic void engineBoom() { System.out.println("悍马H1引擎声音是这样在..."); } @Overridepublic void start() { System.out.println("悍马H1发动..."); }@Overridepublic void stop() { System.out.println("悍马H1停车..."); } }
public class HummerH2Model extends HummerModel { @Overridepublic void alarm() { System.out.println("悍马H2鸣笛..."); } @Overridepublic void engineBoom() { System.out.println("悍马H2引擎声音是这样在..."); } @Overridepublic void start() { System.out.println("悍马H2发动..."); } @Overridepublic void stop() { System.out.println("悍马H2停车..."); } }
06.运行程序

public class Client { public static void main(String[] args) { //客户开着H1型号,出去遛弯了HummerModel h1 = new HummerH1Model(); h1.run(); //汽车跑起来了;//客户开H2型号,出去玩耍了HummerModel h2 = new HummerH2Model(); h2.run(); }
}

07.客户只要在 run 的过程中,听到或看都成了呀,暴露那么多的方法干啥?把抽象方法保护起来

把抽象类上的四个方法设置为 protected 访问权限,好了,既然客户不关心这几个方法,而且这四个方法都是由子类来实现的,那就设置成 protected 模式。咦~,那还有个缺陷,run 方法既然子类都不修改,那是不是可以设置成 final 类型呢?是滴是滴,类图如下:

public abstract class HummerModel { /** 首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正* 是要能够发动起来,那这个实现要在实现类里了*/protected abstract void start(); //能发动,那还要能停下来,那才是真本事protected abstract void stop(); //喇叭会出声音,是滴滴叫,还是哔哔叫protected abstract void alarm(); //引擎会轰隆隆的响,不响那是假的protected abstract void engineBoom(); //那模型应该会跑吧,别管是人退的,还是电力驱动,总之要会跑final public void run() { //先发动汽车this.start(); //引擎开始轰鸣this.engineBoom(); //然后就开始跑了,跑的过程中遇到一条狗挡路,就按喇叭this.alarm(); //到达目的地就停车this.stop(); }
}
大家请看这个 run 方法,他定义了调用其他方法的顺序,并且子类是不能修改的,这个叫做模板方法;start、stop、alarm、engineBoom 这四个方法是子类必须实现的,而且这四个方法的修改对应了不同的类,这个叫做基本方法,基本方法又分为三种:在抽象类中实现了的基本方法叫做具体方法;在抽象类中没有实现,在子类中实现了叫做抽象方法,我们这四个基本方法都是抽象方法,由子类来实现的;还有一种叫做钩子方法,
。
“客户提出新要求了,那个喇叭想让它响就响,你看你设计的模型,车子一启动,喇叭就狂响,赶快修改一下”,确实是设计缺陷,呵呵,不过是我故意的,那我们怎么修改呢?看修改后的类图:

相关文章:
设计模式 模板方法模式
01.如果接到一个任务,要求设计不同型号的悍马车 02.设计一个悍马车的抽象类(模具,车模) public abstract class HummerModel {/** 首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动…...
【STM32嵌入式系统设计与开发】——6矩阵按键应用(4x4)
这里写目录标题 一、任务描述二、任务实施1、SingleKey工程文件夹创建2、函数编辑(1)主函数编辑(2)LED IO初始化函数(LED_Init())(3)开发板矩阵键盘IO初始化(ExpKeyBordInit())&…...
乐优商城(九)数据同步RabbitMQ
1. 项目问题分析 现在项目中有三个独立的微服务: 商品微服务:原始数据保存在 MySQL 中,从 MySQL 中增删改查商品数据。搜索微服务:原始数据保存在 ES 的索引库中,从 ES 中查询商品数据。商品详情微服务:做…...
XSS-labs详解
xss-labs下载地址https://github.com/do0dl3/xss-labs 进入靶场点击图片,开始我们的XSS之旅! Less-1 查看源码 代码从 URL 的 GET 参数中取得 "name" 的值,然后输出一个居中的标题,内容是 "欢迎用户" 后面…...
设计模式——模板方法模式封装.net Core读取不同类型的文件
1、模板方法模式 模板方法模式:定义一个操作中的算法骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 特点:通过把不变的行为搬移到超类,去除子类中重复的代…...
[思考记录]技术欠账
最近对某开发项目做回顾梳理,除了进一步思考整理相关概念和问题外,一个重要的任务就是清理“技术欠账”。 这个“技术欠账”是指在这个项目的初期,会有意无意偏向快速实现,想先做出来、用起来,进而在实现过程中做出…...
React - 实现菜单栏滚动
简介 本文将会基于react实现滚动菜单栏功能。 技术实现 实现效果 点击菜单,内容区域会自动滚动到对应卡片。内容区域滑动,指定菜单栏会被选中。 ScrollMenu.js import {useRef, useState} from "react"; import ./ScrollMenu.css;export co…...
线性筛选(欧拉筛选)-洛谷P3383
#include <bits/stdc.h> using namespace std; int main() {std::ios::sync_with_stdio(false); cin.tie(nullptr); //为了加速int n, q;cin >> n >> q; vector<int>num(n 1); //定义数字表vector<int>prime; //定义素数表数组num[1] …...
企业微信可以更换公司主体吗?
企业微信变更主体有什么作用?当我们的企业因为各种原因需要注销或已经注销,或者运营变更等情况,企业微信无法继续使用原主体继续使用时,可以申请企业主体变更,变更为新的主体。企业微信变更主体的条件有哪些࿱…...
Qt教程 — 3.6 深入了解Qt 控件:Display Widgets部件(2)
目录 1 Display Widgets简介 2 如何使用Display Widgets部件 2.1 QTextBrowser组件-简单的文本浏览器 2.2 QGraphicsView组件-简单的图像浏览器 Display Widgets将分为两篇文章介绍 文章1(Qt教程 — 3.5 深入了解Qt 控件:Display Widgets部件-CSDN…...
Golang案例开发之gopacket抓包三次握手四次分手(3)
文章目录 前言一、理论知识三次握手四次分手二、代码实践1.模拟客户端和服务器端2.三次握手代码3.四次分手代码验证代码完整代码总结前言 TCP通讯的三次握手和四次分手,有很多文章都在介绍了,当我们了解了gopacket这个工具的时候,我们当然是用代码实践一下,我们的理论。本…...
如何减少pdf的文件大小?pdf压缩工具介绍
文件发不出去,有时就会耽误工作进度,文件太大无法发送,这应该是大家在发送PDF时,常常会碰到的问题吧,那么PDF文档压缩大小怎么做呢?因此我们需要对pdf压缩后再发送,那么有没有好用的pdf压缩工具…...
TypeScript基础类型
string、number、bolean 直接在变量后面添加即可。 let myName: string Tomfunction sayHello(person: string) {return hello, person } let user Tom let array [1, 2, 3] console.log(sayHello(user))function greet(person: string, date: Date): string {console.lo…...
长安链智能合约标准协议第二草案——BNS与DID协议邀请社区用户评审
长安链智能合约标准协议 在智能合约编写过程中,不同的产品及开发人员对业务理解和编程习惯不同,即使同一业务所编写的合约在具体实现上也可能有很大差异,在运维或业务对接中面临较大的学习和理解成本,现有公链合约协议规范又不能完…...
安防监控视频汇聚平台EasyCVR接入海康Ehome设备,设备在线但视频无法播放是什么原因?
安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快,可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等,以及支持厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…...
【Python + Django】表结构创建
以员工管理系统为例。 事前呢,我们先把项目和app创建出来,详细步骤可以看我同栏目的第一篇、第二篇文章。 我知道你们是不会下来找的,就把链接贴在下面吧: 【Python Django】启动简单的文本页面-CSDN博客 【Python Django】…...
解锁编程潜能:ChatGPT如何革新软件开发
目录 一、背景 二、功能描述 三、总结 一、背景 在这个飞速发展的数字时代,软件开发的效率和质量成了衡量一个开发者能力的重要标准。随着人工智能技术的不断进步,越来越多的开发者开始寻找能够提升工作效率的新方法。我就是其中之一,最近…...
内网使用rustdesk进行远程协助
文章目录 前言一、搭建rustdesk中继服务器二、搭建文件下载服务器三、创建引导脚本四、使用 前言 内网没有互联网环境,没法使用互联网上有中继服务器的远程协助工具,如teamviewer、todesk、向日癸等;在内网进行远程维护可以自己搭建中继服务…...
linux内核input子系统概述
目录 一、input子系统二、关键数据结构和api2.1 数据结构2.1.1 input_dev2.1.2 input_handler2.1.3 input_event2.1.4 input_handle 2.2 api接口2.2.1 input_device 相关接口input_device 注册流程事件上报 2.2.2 input handle 相关接口注册 handle指定 handle 2.2.3 input han…...
【解决报错】vi/vim修改文件时报错:Found a swap file by the name xxxxx
目录 报错内容报错原因解决方法 报错内容 vim打开文件提示: Found a swap file by the name xxxxx报错原因 使用vi或vim编辑器编写代码时由于网络不稳定(或其他种种原因)断开了连接,编辑好的代码没有运行和保存,再次…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...
【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...
在树莓派上添加音频输入设备的几种方法
在树莓派上添加音频输入设备可以通过以下步骤完成,具体方法取决于设备类型(如USB麦克风、3.5mm接口麦克风或HDMI音频输入)。以下是详细指南: 1. 连接音频输入设备 USB麦克风/声卡:直接插入树莓派的USB接口。3.5mm麦克…...
【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL
ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...
