【设计模式】之模板方法模式
系列文章目录
【设计模式】之策略模式
【设计模式】之责任链模式
文章目录
系列文章目录
前言
一、什么是模板方法模式
定义
角色
二、为什么要使用模板方法模式
优点
缺点
三、案例
普通案例
模拟Servlet过程案例
总结
前言
今天给大家介绍23种设计模式中的模板方法模式,这个设计模式在Spring框架中还是挺常见的,手写Spring源码的小伙伴必须要掌握的设计模式之一,文章结构还是经典的三连问,是什么?为什么?怎么用?🌈
一、什么是模板方法模式
定义
模板方法模式法(Template Method)定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
角色
- 抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。
- 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
- 基本方法:是实现算法各个步骤的方法。基本方法又可以分为三种:
- 钩子方法(Hook Method) :在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。
- 具体方法(Concrete Method) :一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。
- 抽象方法(Abstract Method) :一个抽象方法由抽象类声明、由其具体子类实现。
- 具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。
二、为什么要使用模板方法模式
优点
- 代码复用:模板方法将公共的代码放在父类中,子类可以通过继承来复用这部分代码,从而避免在子类中重复编写相同的代码。
- 算法统一:模板方法确保了算法的整体结构在子类中是一致的,只是某些步骤的实现有所不同。这有助于保持算法的统一性和完整性。
- 灵活性:模板方法允许子类通过实现抽象方法来定制算法的特定步骤,从而提供了很大的灵活性。
- 扩展性:如果需要增加新的算法步骤,只需要在父类的模板方法中添加新的抽象方法,并让子类实现这些方法即可,无需修改现有的子类代码。
缺点
- 增加继承层次:使用模板方法设计模式会增加类的继承层次,这可能会导致系统变得复杂,并增加维护成本。
- 子类数量过多:如果算法中有很多可变步骤,那么可能需要定义很多抽象方法,从而导致子类数量过多。这会增加系统的复杂性,并可能导致“爆炸式”的子类增长。
- 违反单一职责原则:有时模板方法可能会包含过多的逻辑,导致父类承担了过多的职责。这违反了单一职责原则,使得父类变得难以维护和扩展。
因此,在使用模板方法设计模式时,需要权衡其优缺点,根据具体场景和需求来决定是否使用以及如何使用该模式。
三、案例
普通案例
炒菜步骤:打开油烟机->生火->炒菜->关火->关闭油烟机,我们就以这个步骤为例,不管炒什么菜都会经过 打开油烟机、生火、关火、关闭油烟机这四个步骤,因此,我们可以将这几个过程定义为一个模板方法,至于炒什么样的菜,让子类去决定。
public abstract class Cook {public void open(){System.out.println("打开抽油烟机");}public void openFire(){System.out.println("生火");}//设置为抽象方法,由每个对象重写这个方法(决定做什么菜)public abstract void doCook();public void closeFire(){System.out.println("关火");}public void close(){System.out.println("关闭抽油烟机");}//做饭模板方法public final void cook(){this.open();this.openFire();this.doCook();this.closeFire();this.close();}
}
public class CookPotato extends Cook{@Overridepublic void doCook() {System.out.println("酸辣土豆丝");}
}public class CookTomato extends Cook{@Overridepublic void doCook() {System.out.println("西红柿炒鸡蛋");}
}
@Testvoid test1(){Cook cookPotato = new CookPotato();cookPotato.cook();System.out.println("==========");Cook cookTomato = new CookTomato();cookTomato.cook();}/*
打开抽油烟机
生火
酸辣土豆丝
关火
关闭抽油烟机
==========
打开抽油烟机
生火
西红柿炒鸡蛋
关火
关闭抽油烟机
*/
模拟Servlet过程案例
我们平常使用的Servlet就是一个典型的模板方法的设计模式。
我们平常使用一般是继承HttpServlet这个类,然后去重写类面的一些方法,如:doGet、doPost、doPut等等
public class testServlet extends MyHttpServlet{@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) {System.out.println("执行doGet方法");}@Overridepublic void doPost(HttpServletRequest request, HttpServletResponse response) {System.out.println("执行doPost方法");}
}
而HttpServlet会继承GenericServlet这个类,HttpServlet会将ServletRequest和ServletResponse强转为HttpServletRequest和HttpServletResponse,并对方法类型进行判断派发。
public abstract class MyHttpServlet extends MyGenericServlet{@Overridepublic void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;switch (request.getMethod()){case "GET":this.doGet(request,response);break;case "POST":this.doPost(request,response);break;}}public abstract void doGet(HttpServletRequest request, HttpServletResponse response);public abstract void doPost(HttpServletRequest request, HttpServletResponse response);
}
GenericServlet实现Servlet这个接口,去实现servlet生命周期方法。
public abstract class MyGenericServlet implements Servlet {@Overridepublic void init(ServletConfig servletConfig) throws ServletException {System.out.println("正在初始化");}@Overridepublic ServletConfig getServletConfig() {return null;}@Overridepublic abstract void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException ;@Overridepublic String getServletInfo() {return null;}@Overridepublic void destroy() {System.out.println("正在销毁");}
}
整个大致过程可以总结为:GenericServlet过滤生命周期方法,将service()方法交给HttpServlet去实现,HttpServlet负责将实现具体的方法,最后就得到了我们常见的doGet等方法,我说的这个过程比较简单,其实Servlet整个过程还是挺复杂的,我就抛砖引玉,给大家熟悉下模板方法这个设计模式。
总结
不管哪一种方法模式,都各有利弊,我们平常在开发的过程中,一定要选择最优的解决方式,不可盲目使用。今天的分享就到这里,我们下期再见。✋
相关文章:
【设计模式】之模板方法模式
系列文章目录 【设计模式】之策略模式 【设计模式】之责任链模式 文章目录 系列文章目录 前言 一、什么是模板方法模式 定义 角色 二、为什么要使用模板方法模式 优点 缺点 三、案例 普通案例 模拟Servlet过程案例 总结 前言 今天给大家介绍23种设计模式中的模板方法模式&a…...

【系统架构师】-选择题(十一)
1、紧耦合多机系统一般通过(共享内存)实现多机间的通信。对称多处理器结构(SMP)属于( 紧耦合)系统。 松耦合多机系统又称间接耦合系统,—般是通过通道或通信线路实现计算机间的互连。 2、采用微内核的OS结构…...

前端开发攻略---介绍HTML中的<dialog>标签,浏览器的原生弹框。
1、演示 2、介绍 <dialog> 标签用于定义对话框,即一个独立的窗口,通常用来显示对话框、提示框、确认框等弹出式内容。在对话框中,可以包含文本、表单元素、按钮等内容,用户可以和这些内容进行交互。 3、兼容性 4、示例代码 …...

让外贸业绩翻倍的销售话术分享
业绩翻三倍的话术,今后无论你遇到挑剔、犹豫、理智的顾客,都能轻松搞定。点赞存起来慢慢看,以免找不到。 与客户有效沟通技巧5的20句金句 业绩翻 3 倍,今后无论你遇到挑剔、犹豫、理智的顾客,都能轻松搞定。点赞存起来…...

观测与预测差值自动变化系统噪声Q的自适应UKF(AUKF_Q)MATLAB编写
简述 基于三维模型的UKF,设计一段时间的输入状态误差较大,此时通过对比预测的状态值与观测值的残差,在相应的情况下自适应扩大系统方差Q,构成自适应无迹卡尔曼滤波(AUKF),与传统的UKF相比&…...

虚拟数据中心
创建数据中心和连接宿主机 DRS:收集群集内所有主机和虚拟机的资源使用情况信息,并根据特定的运行状况给出建议或迁移虚拟机HA:如果一台主机出现故障,则该主机上运行的所有虚拟机都将立即在同一群集的其他主机上重新启动EVC:增强型vMotionVirtual SAN:集中…...

解决Blender导出FBX文件到Unity坐标轴错误的问题
发现Blender的模型导入到Unity里面有问题,简单研究了下发现是坐标系不同,Unity使用的是左手坐标系,Blender使用的是右手坐标系 。 下面直接将如何解决 首先忽略Blender的右手坐标系以及Z轴朝上的事,依照unity坐标系情况修改模型物体的旋转,以Blender猴…...
基于微信小程序的校园二手闲置物品交易平台的设计与实现
基于微信小程序的校园二手闲置物品交易平台的设计与实现 “Design and Implementation of a Campus Second-Hand Marketplace Platform based on WeChat Mini Program” 完整下载链接:基于微信小程序的校园二手闲置物品交易平台的设计与实现 文章目录 基于微信小程序的校园二…...
java中多线程的3种实现方法
1.继承Thread类 优点:代码简单,可以直接使用Thread类里面的方法。 缺点:扩张性较差,应为在java中,一个类只能继承一个父类。 2.实现Runnable接口 3.实现Callable接口 2和3的优缺点是一样的 优点:扩展性强&…...

【Docker】docker compose服务编排
docker compose 简介 Dockerfile模板文件可以定义一个单独的应用容器,如果需要定义多个容器就需要服务编排。 docker swarm(管理跨节点) Dockerfile可以让用户管理一个单独的应用容器;而Compose则允许用户在一个模板(…...
elementui的el-select+el-tree+el-input实现可搜索的下拉树组件
部分实现代码如下 <template> <div><el-selectv-model"item.TableName"placeholder"请选择":disabled"!item.disabled"visible-change"handleVisible"ref"TableName"><el-input placeholder"请输…...

微信公众号排名 SEO的5个策略
随着微信公众号在社交媒体领域的持续发展和普及,如何提升公众号的搜索排名,成为许多运营者关注的焦点。公众号排名SEO,即针对微信公众号进行搜索引擎优化,旨在提高公众号在搜索结果中的曝光率和点击率。下面,我们将深入…...
python烟花代码
在Python中,可以使用多种方式来模拟烟花效果,其中一种常见的方法是使用turtle图形库来绘制。以下是一个简单的示例,展示了如何使用turtle来创建一个烟花效果的动画: import turtle import random# 设置屏幕和背景 screen turtle…...
Python高级编程
描述 集合,列表生成式,生成器,迭代器,切片 Python 中的集合类型是一种无序、不重复的数据容器,用于存储可哈希(hashable)的元素。Python 提供了两种内置的集合类型:set 和 frozens…...

leetCode75. 颜色分类
leetCode75. 颜色分类 题目思路 代码 class Solution { public:void sortColors(vector<int>& nums) {for(int i 0, j 0, k nums.size() - 1; i < k;){if(nums[i] 0) swap(nums[i],nums[j]);else if(nums[i] 2) swap(nums[i],nums[k--]);else if(nums[i] …...

选择器、pxcook软件、盒子模型
结构伪类选择器 定义:根据结构的元素关系来查找元素。 <title>Document</title><style>li:first-child{color:aqua ;}li:last-child{color: aqua;}li:nth-child(3){color: aqua;}</style> </head> <body><ul><li>…...
商城系统秒杀功能设计思想
业务特点 1、瞬时并发量大,秒杀时会有大量用户在同一时间进行抢购,瞬时并发访问量突增几倍、甚至几十倍以上 2、库存量少,一般秒杀活动商品量很少,这就导致了只有极少量用户能成功购买到。 3、业务和流程较为常见,一般…...

#初始化列表
1.再谈构造函数 1.1构造函数的组成 构造函数包括函数体赋值与初始化列表初始化。 1.2函数体赋值 class Date { public:Date(int year, int month, int day){_year year;_month month;_day day;} private:int _year;int _month;int _day; };虽然上述构造函数调用之后&…...

Vue-组件中的data
一个组件的data选项必须是一个函数。保证每个组件实例,维护独立的一份数据对象。如下图: 组件一旦封装好了,可以使用多次,比如数字框组件使用了三次: 每次创建新的组件实例,都会重新执行一次data函数&#…...

抖音小店达人佣金应该怎么结算呢?给达人设置多少佣金合适?
大家好,我是电商小V 咱们在运营抖音小店的时候一定会遇到被达人催促结算佣金的情况,咱们给达人合作的时候都会遇到过新手达人,就是给咱们直播带货之后催促咱们赶紧结算商品的佣金, 但是咱们都需要知道一点,那就是小店的…...

从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...

短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...

【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案
在大数据时代,海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构,在处理大规模数据抓取任务时展现出强大的能力。然而,随着业务规模的不断扩大和数据抓取需求的日益复杂,传统…...