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

【设计模式】之模板方法模式

系列文章目录

  • 【设计模式】之策略模式

  • 【设计模式】之责任链模式

文章目录

系列文章目录

前言

一、什么是模板方法模式

定义

 角色 

二、为什么要使用模板方法模式

优点

缺点

三、案例

普通案例

模拟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();}
}

子类去继承Cook类实现里面的抽象方法去决定做什么菜。

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会将ServletRequestServletResponse强转为HttpServletRequestHttpServletResponse,并对方法类型进行判断派发。

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> 标签用于定义对话框&#xff0c;即一个独立的窗口&#xff0c;通常用来显示对话框、提示框、确认框等弹出式内容。在对话框中&#xff0c;可以包含文本、表单元素、按钮等内容&#xff0c;用户可以和这些内容进行交互。 3、兼容性 4、示例代码 …...

让外贸业绩翻倍的销售话术分享

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

观测与预测差值自动变化系统噪声Q的自适应UKF(AUKF_Q)MATLAB编写

简述 基于三维模型的UKF&#xff0c;设计一段时间的输入状态误差较大&#xff0c;此时通过对比预测的状态值与观测值的残差&#xff0c;在相应的情况下自适应扩大系统方差Q&#xff0c;构成自适应无迹卡尔曼滤波&#xff08;AUKF&#xff09;&#xff0c;与传统的UKF相比&…...

虚拟数据中心

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

解决Blender导出FBX文件到Unity坐标轴错误的问题

发现Blender的模型导入到Unity里面有问题,简单研究了下发现是坐标系不同,Unity使用的是左手坐标系,Blender使用的是右手坐标系 。 下面直接将如何解决 首先忽略Blender的右手坐标系以及Z轴朝上的事&#xff0c;依照unity坐标系情况修改模型物体的旋转&#xff0c;以Blender猴…...

基于微信小程序的校园二手闲置物品交易平台的设计与实现

基于微信小程序的校园二手闲置物品交易平台的设计与实现 “Design and Implementation of a Campus Second-Hand Marketplace Platform based on WeChat Mini Program” 完整下载链接:基于微信小程序的校园二手闲置物品交易平台的设计与实现 文章目录 基于微信小程序的校园二…...

java中多线程的3种实现方法

1.继承Thread类 优点&#xff1a;代码简单&#xff0c;可以直接使用Thread类里面的方法。 缺点&#xff1a;扩张性较差&#xff0c;应为在java中&#xff0c;一个类只能继承一个父类。 2.实现Runnable接口 3.实现Callable接口 2和3的优缺点是一样的 优点&#xff1a;扩展性强&…...

【Docker】docker compose服务编排

docker compose 简介 Dockerfile模板文件可以定义一个单独的应用容器&#xff0c;如果需要定义多个容器就需要服务编排。 docker swarm&#xff08;管理跨节点&#xff09; Dockerfile可以让用户管理一个单独的应用容器&#xff1b;而Compose则允许用户在一个模板&#xff08…...

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个策略

随着微信公众号在社交媒体领域的持续发展和普及&#xff0c;如何提升公众号的搜索排名&#xff0c;成为许多运营者关注的焦点。公众号排名SEO&#xff0c;即针对微信公众号进行搜索引擎优化&#xff0c;旨在提高公众号在搜索结果中的曝光率和点击率。下面&#xff0c;我们将深入…...

python烟花代码

在Python中&#xff0c;可以使用多种方式来模拟烟花效果&#xff0c;其中一种常见的方法是使用turtle图形库来绘制。以下是一个简单的示例&#xff0c;展示了如何使用turtle来创建一个烟花效果的动画&#xff1a; import turtle import random# 设置屏幕和背景 screen turtle…...

Python高级编程

描述 集合&#xff0c;列表生成式&#xff0c;生成器&#xff0c;迭代器&#xff0c;切片 Python 中的集合类型是一种无序、不重复的数据容器&#xff0c;用于存储可哈希&#xff08;hashable&#xff09;的元素。Python 提供了两种内置的集合类型&#xff1a;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软件、盒子模型

结构伪类选择器 定义&#xff1a;根据结构的元素关系来查找元素。 <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、瞬时并发量大&#xff0c;秒杀时会有大量用户在同一时间进行抢购&#xff0c;瞬时并发访问量突增几倍、甚至几十倍以上 2、库存量少&#xff0c;一般秒杀活动商品量很少&#xff0c;这就导致了只有极少量用户能成功购买到。 3、业务和流程较为常见&#xff0c;一般…...

#初始化列表

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

抖音小店达人佣金应该怎么结算呢?给达人设置多少佣金合适?

大家好&#xff0c;我是电商小V 咱们在运营抖音小店的时候一定会遇到被达人催促结算佣金的情况&#xff0c;咱们给达人合作的时候都会遇到过新手达人&#xff0c;就是给咱们直播带货之后催促咱们赶紧结算商品的佣金&#xff0c; 但是咱们都需要知道一点&#xff0c;那就是小店的…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

mongodb源码分析session执行handleRequest命令find过程

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令&#xff0c;把数据流转换成Message&#xff0c;状态转变流程是&#xff1a;State::Created 》 St…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

Rapidio门铃消息FIFO溢出机制

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

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...