模板模式(上):剖析模板模式在JDK、Servlet、JUnit等中的应用

今天,我们再学习另外一种行为型设计模式,模板模式。我们多次强调,绝大部分设计模式的原理和实现,都非常简单,难的是掌握应用场景,搞清楚能解决什么问题。模板模式也不例外。模板模式主要是用来解决复用扩展两个问题。

模板模式的原理与实现

模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。

        原理很简单,代码实现就更加简单,我写了一个示例代码,如下所示。templateMethod()函数定义为final,是为了避免子类重写它。method1()和method2()定义为abstract,是为了强迫子类去实现。不过,这些都不是必须的,在实际的项目开发中,模板模式的代码实现比较灵活,待会儿讲到应用场景的时候,我们会有具体的体现。

public abstract class AbstractClass {public final void templateMethod() {//...method1();//...method2();//...}protected abstract void method1();protected abstract void method2();
}public class ConcreteClass1 extends AbstractClass {@Overrideprotected void method1() {//...}@Overrideprotected void method2() {//...}
}public class ConcreteClass2 extends AbstractClass {@Overrideprotected void method1() {//...}@Overrideprotected void method2() {//...}
}AbstractClass demo = ConcreteClass1();
demo.templateMethod();

模板模式作用一:复用

模板模式把一个算法中不变的流程抽象到父类的方法templateMethod()中,可变的部分method1()和method2()留给子类实现。

1.Java InputStream

Java IO类库中,有很多类的设计用到了模板模式,比如InputStream、OutputStream、Reader、Writer。我们拿InputStream来举例说明一下。

我把InputStream部分相关代码贴在了下面。在代码中,read()函数是一个模板方法,定义了读取数据的整个流程,并且暴露了一个可以由子类来定制的抽象方法。不过这个方法也被命名为了read(),只是参数跟模板方法不同。

public abstract class InputStream implements Closeable {//...省略其他代码...public int read(byte b[], int off, int len) throws IOException {if (b == null) {throw new NullPointerException();} else if (off < 0 || len < 0 || len > b.length - off) {throw new IndexOutOfBoundsException();} else if (len == 0) {return 0;}int c = read();if (c == -1) {return -1;}b[off] = (byte)c;int i = 1;try {for (; i < len ; i++) {c = read();if (c == -1) {break;}b[off + i] = (byte)c;}} catch (IOException ee) {}return i;}public abstract int read() throws IOException;
}public class ByteArrayInputStream extends InputStream {//...省略其他代码...@Overridepublic synchronized int read() {return (pos < count) ? (buf[pos++] & 0xff) : -1;}
}

模板模式作用二:扩展

模板模式常用在框架的开发中,让框架用户可以在不修改框架源码的情况下,定制化框架的功能。我们通过Java Servlet来解释一下。

1.Java Servlet

对于Java Web项目开发来说,常用的开发框架是SpringMVC。利用它,我们只需要关注业务代码的编写,底层的原理几乎不会涉及。但是,如果我们抛开这些高级框架来开发Web项目,必然会用到Servlet。实际上,使用比较底层的Servlet来开发Web项目也不难。我们只需要定义一个继承HttpServlet的类,并且重写其中的doGet()或doPost()方法来分别处理get和post请求。具体的代码示例如下所示:

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write("Hello World.");}
}

当我们在浏览器中输入网址(比如,http://127.0.0.1:8080/hello )的时候,Servlet容器会接收到相应的请求,并且根据URL和Servlet之间的映射关系,找到相应的Servlet(HelloServlet),然后执行它的service()方法。service()方法定义在父类HttpServlet中,它会调用doGet()或doPost()方法,然后输出数据(“Hello world”)到网页

我们现在来看,HttpServlet的service()函数长什么样子。

public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException
{HttpServletRequest  request;HttpServletResponse response;if (!(req instanceof HttpServletRequest &&res instanceof HttpServletResponse)) {throw new ServletException("non-HTTP request or response");}request = (HttpServletRequest) req;response = (HttpServletResponse) res;service(request, response);
}protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException
{String method = req.getMethod();if (method.equals(METHOD_GET)) {long lastModified = getLastModified(req);if (lastModified == -1) {// servlet doesn't support if-modified-since, no reason// to go through further expensive logicdoGet(req, resp);} else {long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);if (ifModifiedSince < lastModified) {// If the servlet mod time is later, call doGet()// Round down to the nearest second for a proper compare// A ifModifiedSince of -1 will always be lessmaybeSetLastModified(resp, lastModified);doGet(req, resp);} else {resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);}}} else if (method.equals(METHOD_HEAD)) {long lastModified = getLastModified(req);maybeSetLastModified(resp, lastModified);doHead(req, resp);} else if (method.equals(METHOD_POST)) {doPost(req, resp);} else if (method.equals(METHOD_PUT)) {doPut(req, resp);} else if (method.equals(METHOD_DELETE)) {doDelete(req, resp);} else if (method.equals(METHOD_OPTIONS)) {doOptions(req,resp);} else if (method.equals(METHOD_TRACE)) {doTrace(req,resp);} else {String errMsg = lStrings.getString("http.method_not_implemented");Object[] errArgs = new Object[1];errArgs[0] = method;errMsg = MessageFormat.format(errMsg, errArgs);resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);}
}

总结和反思:

  • 模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。这里的“算法”,我们可以理解为广义上的“业务逻辑”,并不特指数据结构和算法中的“算法”。这里的算法骨架就是“模板”,包含算法骨架的方法就是“模板方法”,这也是模板方法模式名字的由来。
  • 在模板模式经典的实现中,模板方法定义为final,可以避免被子类重写。需要子类重写的方法定义为abstract,可以强迫子类去实现。不过,在实际项目开发中,模板模式的实现比较灵活,以上两点都不是必须的。

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

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

相关文章

软件测试银行项目面试过程

今天参加了一场比较正式的面试&#xff0c;汇丰银行的视频面试。在这里把面试的流程记录一下&#xff0c;结果还不确定&#xff0c;但是面试也是自我学习和成长的过程&#xff0c;所以记录下来大家也可以互相探讨一下。 请你做一下自我介绍&#xff1f;&#xff08;汇丰要求英…

第108天:免杀对抗-Python混淆算法反序列化打包生成器Py2exeNuitka

知识点 #知识点&#xff1a; 1、Python-对执行代码做文章 2、Python-对shellcode做文章 3、Python-对代码打包器做文章#章节点&#xff1a; 编译代码面-ShellCode-混淆 编译代码面-编辑执行器-编写 编译代码面-分离加载器-编写 程序文件面-特征码定位-修改 程序文件面-加壳花指…

webpack打包之 copy-webpack-plugin

copy-webpack-plugin 打包复制文件插件。 1、什么时候要使用&#xff1f; 在离线应用中&#xff0c;前端所有文件都需在在本地&#xff0c;有些文件&#xff08;比如iconFont以及一些静态img)需要转为离线文件&#xff0c;这些文件可以直接引用更方便些&#xff0c;这就需要在打…

6.6Jmeter远程调度Linux机器Jmeter测试

1、配置Agent和启动 1.1、打开jmeter/bin目录下的jmeter.properties 1、server_port1099取消注释 2、remote_hosts127.0.0.1 改为remote_hosts127.0.0.1:1099 或者是remote_hostsAgent机的ip:1099 3、server.rmi.localport1099 4、server.rmi.ssl.disablefalse改为true&#x…

matlab 一维数组拼接,多个一维数组合并成一个一维数组

目录 matlab 一维数组拼接,多个一维数组合并成一个一维数组 1、一维数组直接合并 直接将两个矩阵合并&#xff1a; 2、一维数组交叉合并&#xff1a; 方法一&#xff1a; 方法二&#xff1a; 方法三&#xff1a; 方法四&#xff1a; 其他操作&#xff1a; matlab 一维…

店招新品之创意LED显示屏之LED魔方屏的特点

LED魔方屏也被称之为立方体LED显示屏、六面体LED显示屏等&#xff0c;每个显示面之间实现了无缝拼接&#xff0c;运用技术前沿的箱体设计工艺&#xff0c;通过跟实际安装场景相结合&#xff0c;打造出更具创意的LED显示屏产品&#xff0c;LED魔方屏以独特的造型吸引用户&#x…

OceanBase 4.1上云,OB Cloud助力企业降本增效

近日&#xff0c;OB Cloud 正式上线 OceanBase 4.1 新版本&#xff0c;支持企业在云上使用最新的 OceanBase 单机分布式一体化架构能力。去年8月OceanBase 宣布公有云服务全球开服&#xff0c;伴随4.1版本推出&#xff0c;OceanBase公有云正式命名为OB Cloud云数据库。 在本次…

基于springboot+Redis的前后端分离项目之分布式锁(四)-【黑马点评】

&#x1f381;&#x1f381;资源文件分享 链接&#xff1a;https://pan.baidu.com/s/1189u6u4icQYHg_9_7ovWmA?pwdeh11 提取码&#xff1a;eh11 分布式锁 分布式锁1 、基本原理和实现方式对比2 、Redis分布式锁的实现核心思路3 、实现分布式锁版本一4 、Redis分布式锁误删情况…

Ubuntu 22.04 配置 QGIS二次开发环境

源码地址&#xff0c;以及官方指导文档地址 源码地址&#xff1a;https://github.com/qgis/QGIS/releases QGIS 官方指导文档地址&#xff1a;https://github.com/qgis/QGIS/blob/release-3_26/INSTALL.md&#xff08;此处为3.26的指导文档&#xff0c;可以自行修改查看&#…

基于SpringBoot+Vue+微信小程序的电影平台

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 研究背景&#xff1a;…

Hyperledger Fabric测试网络的准备和基本使用

文章目录 相关安装启动测试网络创建channel打包链码安装链码包通过链码定义链码定义提交给通道调用链码关闭网络遇到的问题1.docker保持启动状态2.忘起测试网络了3.Java版本过高&#xff0c;推荐1.8 相关安装 npm、node、git、docker、docker-compose。docker保证一直运行 serv…

基于springboot的微信小程序宠物领养医院系统(源代码+数据库+10000字论文)085

基于springboot的微信小程序宠物领养医院系统(源代码数据库10000字论文)085 一、系统介绍 本项目有网页版和小程序端 本系统分为管理员、医生、用户三种角色 用户角色包含以下功能&#xff1a; 登录、注册、宠物领养、医生在线咨询、查看挂号、个人中心、密码修改、宠物寄…

【微信小程序】使用weui组件库来实现弹出一个确认的弹窗popup,其中包含图片和名称

在微信小程序中&#xff0c;你可以使用weui组件库来实现弹出一个确认的popup&#xff0c;并在其中包含图片和名称。以下是一个示例代码&#xff1a; 在wxml文件中&#xff0c;添加一个按钮来触发弹出确认popup&#xff1a; <button bindtap"showPopup">显示确…

linux 安装pytorch3d的坑

事实上&#xff0c;只要按照官方文档的说明就可以完美安装。其中坑的地方在于conda的管理可能会导致下载的版本不符合你的要求&#xff08;例如下载成了cpu版本、下载的cuda版本&#xff09;而同样尝试使用源码编译以及其他方式下载库都会导致同样的问题&#xff0c;这里主要的…

79、如何实现接口幂等性

如何实现接口的幂等性 唯一id。每次操作&#xff0c;都根据操作和内容生成唯一的id&#xff0c;在执行之前先判断id是否存在&#xff0c;如果不存在则执行后续操作&#xff0c;并且保存到数据库或者redis等。服务端提供发送token的接口&#xff0c;业务调用接口前先获取token,…

redis基本操作

string数据类型的命令操作 设置键值 使用append 命令设置键值&#xff0c;后面跟键的名字&#xff0c;可以先判断该建是否存在&#xff0c;存在将值追加在后面&#xff0c;不存在自动添加该建 append mykey hello读取键值 get mykey数值类型自减1 数值类型自加1 查看值的…

每天一道C语言编程练习(5):尼科彻斯定理

题目描述 验证尼科彻斯定理&#xff0c;即&#xff1a;任何一个整数m的立方都可以写成m个连续奇数之和。 输入格式 任一正整数 输出格式 该数的立方分解为一串连续奇数的和 样例输入 13 样例输出 13*13*132197157159161163165167169171173175177179181 代码如下&#…

spring boot面向切面编程aop

一、什么是AOP AOP&#xff0c;Aspect Oriented Programming&#xff0c;面向切面编程 举个例子来理解 如果我们的业务需要额外做三件事情&#xff0c;判断是否已经登录&#xff0c;记录日志&#xff0c;统计业务执行时长 传统的做法是这样子的&#xff1a; 而apo的实现是这…

【PHP面试题74】PHP有哪些魔术方法,如何使用他们?

文章目录 一、前言二、模式方法讲解2.1 __construct()2.2 __destruct()2.3 __get()和__set()2.4 __call()和__callStatic()2.5 __toString()2.6 __invoke() 三、总结 一、前言 本文已收录于PHP全栈系列专栏&#xff1a;PHP面试专区。 计划将全覆盖PHP开发领域所有的面试题&…

用于验证 JSON 数据是否符合指定 JSON Schema 的函数,并使用断言进行验证结果

PYTHON 用于验证 JSON 数据是否符合指定 JSON Schema 的函数&#xff0c;并使用断言进行验证结果 from jsonschema import validate, draft7_format_checker, SchemaError, ValidationErrordef jsonschema_assert(code, msg, schema, jsonSchema):assert code codeassert msg…