设计模式-模板模式及应用

article/2025/10/25 18:56:07

一般生活中我们办理一件事需要一套指定的流水线。例如银行办事,需要先去营业厅取号、排队、办理业务、综合评分。这一套固有的流程,取号、排队、综合评分等都是固定的,不同的客户会办理不同的业务,只有这块是没个客户不同的。类似于这种有固定的流水线,在软件设计的过程中,也是时长被使用到的。
定义这条流水线的方法为模板方法,执行到每个环节在调取相应方法的函数。在设计模式中,此类的问题为模板方法模式,下面就通过案例来说一下模板方法的使用。

定义及结构特点

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

1.模板模式的结构

  • 抽象类模板方法 定义模板执行顺序,需要执行的函数

  • 具体类 实现模板方法中的特定方法和钩子方法

抽象类中一般有三种类型方法:
1)、普通方法,定义固有的方法
2)、抽象方法,各子类对象需要自定义实现的方法,也就是定义中提到的,推迟到子类中实现的方法。
3)、钩子方法,一般是由子类决定是否重写,来决定模板方法中是否执行某块逻辑

2.模板方法结构图:
在这里插入图片描述

代码案例
1.案例1
  • 抽象类 指定模板
//抽象类
public abstract class AbstractClass {//模板方法 算法骨架public void templateMethod() {method1();abstractMethod();method2();}public void method1(){System.out.println("模板方法1");}public void method2(){System.out.println("模板方法2");}// 抽象方法 推迟到子类执行的方法protected abstract void abstractMethod();
}
  • 具体实现类1
public class ConcreteClass extends AbstractClass{@Overrideprotected void abstractMethod() {System.out.println("具体模板方法1");	}
}
  • 具体实现类2
public class ConcreteClass2 extends AbstractClass{@Overrideprotected void abstractMethod() {System.out.println("具体模板方法2");	}
}
  • 使用方
public class Client {public static void main(String[] args) {AbstractClass abstractClass = 	new ConcreteClass();abstractClass.templateMethod();System.out.println("----------------");AbstractClass abstractClass2 = 	new ConcreteClass2();abstractClass2.templateMethod();}
}

执行结果:

模板方法1
具体模板方法1
模板方法2
----------------
模板方法1
具体模板方法2
模板方法2

执行结果中,模板方法中的普通方法method1,method2,都是在抽象类中定义的,所有的子类对象执行模板方法时,都一样,自定义的部分在抽象方法中。这就是模板模式的基本骨架,很简单。

2.案例2

案例2,通过模拟网上购物的流程(选择商品、加入购物车、增加运费险、下单、付款),来体会一下模板模式的使用。包括钩子函数的使用。

  • 抽象类
//抽象流程
public abstract class AbstractFlow {// 购物流程模板public void shoppingTemplate() {choose();addShopStore();//钩子方法if (hook()) {freightInsurance();}order();pay();}// 商品选择public void choose() {System.out.println("商品选选择阶段");}// 加入购物车public void addShopStore() {System.out.println("加入购物车");}// 下单public void order() {System.out.println("下单...跳转付款页面");}// 运费险public void freightInsurance() {System.out.println("附加运费险");}//钩子函数public boolean hook() {return false;}// 付款protected abstract void pay();}
  • 具体实现类1 买一双鞋子 (无需运费险)
public class ShopShoeFlow extends AbstractFlow{@Overrideprotected void pay() {System.out.println("购买鞋子花费:500元");}
}

-具体实现类2 买一件T-shirt (需要运费险)

public class ShopTshirtFlow extends AbstractFlow {public boolean hook() {// 衣服的尺寸因为每个品牌都不标准,这里重写钩子函数,方便增加运费险return true;}@Overrideprotected void pay() {System.out.println("购买 T-shirt 花费:300元");	}
}
  • 使用方
public class Client {public static void main(String[] args) {// 购买鞋子AbstractFlow shopShoeFlow = new ShopShoeFlow();shopShoeFlow.shoppingTemplate();System.out.println("-----------");	// 购买一件T-shirtAbstractFlow shopTshirtFlow = new ShopTshirtFlow();shopTshirtFlow.shoppingTemplate();}
}

执行结果:

商品选选择阶段
加入购物车
下单...跳转付款页面
购买鞋子花费:500-----------
商品选选择阶段
加入购物车
附加运费险
下单...跳转付款页面
购买 T-shirt 花费:300

案例2结构:
在这里插入图片描述

我们买东西其他的流程都是一样,只有在最后付款的时候,因为不同的商品最终付款的价格也是不同的。所有把付款的方法推迟到购买具体商品的子类中实现;钩子方法的使用,钩子方法是控制整个模板算法骨架中,某一部分不是必须要在整个流程中的部分。这里可以通过子类重写钩子函数的方式,来控制注册分功能是否要在整个流程中执行。

对于鞋子来说一般我们鞋码都是固定的,不同品牌之间有细微的差别,基本不会买错。但是衣服来说的话,差别就很大,不同款式(运动、休闲)不同品牌之间的尺码都不一致。买错有更大的风险,所以对买衣服增加个运费险,通过重写钩子函数来实现。

模板模式的优缺点及应用场景

优点:

  • 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
  • 它在父类中提取了公共的部分代码,便于代码复用。
  • 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。

缺点:

  • 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统实现的复杂度;
  • 由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍,增加了代码的耦合性。
应用场景
  • 整个流程有固定的环境,但是其中某部分不同
  • 当多个子类存在公共的行为时,可以将其提取出来并集中到
  • 需要灵活的控制整个算法固件中各部分的执行流程(钩子函数)

模板模式在源码中的使用

1.Mybatis中模板模板方法的使用

列举一例,BaseExecutor提供了基本sql执行方法,实现了sql执行逻辑,骨架是定义好了,部分特定的实现是在子类的方法中实现的。如事务管理、缓存管理。

// 查询public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());if (closed) {throw new ExecutorException("Executor was closed.");}if (queryStack == 0 && ms.isFlushCacheRequired()) {clearLocalCache();}List<E> list;try {queryStack++;list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;if (list != null) {handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);} else {// 调用查询方法list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {queryStack--;}if (queryStack == 0) {for (DeferredLoad deferredLoad : deferredLoads) {deferredLoad.load();}// issue #601deferredLoads.clear();if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {// issue #482clearLocalCache();}}return list;}// 修改@Overridepublic int update(MappedStatement ms, Object parameter) throws SQLException {ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());if (closed) {throw new ExecutorException("Executor was closed.");}clearLocalCache();// 调用修改return doUpdate(ms, parameter);}//...略// 抽象方法protected abstract int doUpdate(MappedStatement ms, Object parameter)throws SQLException;protected abstract List<BatchResult> doFlushStatements(boolean isRollback)throws SQLException;protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)throws SQLException;protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)throws SQLException;

BaseExecutor类实现Executor接口中的query()、update()...等等,都是提供一个执行流程,最终调用了本类中的抽象方法,具体的实现是放在子类中实现。
在这里插入图片描述

子类的实现有四个,分别不同实现了抽象类中的方法,子类的功能:

  • SimpleExecutor 是 Mybatis 执行 Mapper 语句时默认使用的 Executor,提供最基本的 Mapper 语句执行功能,没有过多的封装。
  • ReuseExecutor 提供了 Statement 重用的功能,通过 statementMap 字段缓存使用过的 Statement 对象进行重用,可以减少 SQL 预编译以及创建和销毁 Statement 对象的开销,从而提高性能。
  • BatchExecutor 实现了批处理多条 SQL 语句的功能,在客户端缓存多条 SQL 并在合适的时机将多条 SQL 打包发送给数据库执行,从而减少网络方面的开销,提升系统的性能。
  • ClosedExecutor 是个类的一个内部类
2.Servlet中模式模式的使用

Servlet AIPJava Servlet制定的标准, 可以使用 javax.servletjavax.servlet.http 包创建. Java Servlet 是运行在Web服务器或应用服务器上的程序,它是作为来自Web浏览器或其他HTTP客户端的请求和HTTP服务器上的数据库或应用程序之间的中间层。
支持Http协议HttpServlet,HttpServlet 继承于 GenericServlet,实现了Servlet接口,HttpServlet提供了Http协议的请求。

//根据请求方式不同,调用不听方法
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String method = req.getMethod();long lastModified;if (method.equals("GET")) {lastModified = this.getLastModified(req);if (lastModified == -1L) {this.doGet(req, resp);} else {long ifModifiedSince;try {ifModifiedSince = req.getDateHeader("If-Modified-Since");} catch (IllegalArgumentException var9) {ifModifiedSince = -1L;}if (ifModifiedSince < lastModified / 1000L * 1000L) {this.maybeSetLastModified(resp, lastModified);this.doGet(req, resp);} else {resp.setStatus(304);}}} else if (method.equals("HEAD")) {lastModified = this.getLastModified(req);this.maybeSetLastModified(resp, lastModified);this.doHead(req, resp);} else if (method.equals("POST")) {this.doPost(req, resp);} else if (method.equals("PUT")) {this.doPut(req, resp);} else if (method.equals("DELETE")) {this.doDelete(req, resp);} else if (method.equals("OPTIONS")) {this.doOptions(req, resp);} else if (method.equals("TRACE")) {this.doTrace(req, resp);} else {String errMsg = lStrings.getString("http.method_not_implemented");Object[] errArgs = new Object[]{method};errMsg = MessageFormat.format(errMsg, errArgs);resp.sendError(501, errMsg);}}
// httpServlet dogetprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String protocol = req.getProtocol();String msg = lStrings.getString("http.method_get_not_supported");if (protocol.endsWith("1.1")) {resp.sendError(405, msg);} else {resp.sendError(400, msg);}}//... doPost doHead  doPut ..略

这里的模板方法就是Service(),根据不同的请求,调用HttpServlet中的指定方法,这里篇幅有限贴了 doGet()方法,发现没有任何实现。会报40*的错误,看下Httpservelet的子类。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-12LITC3h-1621262835394)(evernotecid://8E645ED8-D430-4766-B866-1F39DB4714E0/appyinxiangcom/13431112/ENResource/p1467)]

这里具体的方法都由子类来实现,HttpServlet不能被实例化是个抽象类,具体的方法请求都由子类来实现,HttpServlet在这里充当模板模式的抽象类,不同子类的实现为具体的实现类,重新不同请求方法。

✨✨ 欢迎🔔订阅个人的微信公众号 享及时博文更新
个人工作号


http://chatgpt.dhexx.cn/article/JY1Ty9qX.shtml

相关文章

Java设计模式——模板设计模式

模板设计模式 1.模板模式简介 模板模式&#xff08;Template &#xff09;&#xff1a;模板方法模式是类的行为模式。准备一个抽象类&#xff0c;将部分逻辑以具体方法以及具体构造函数的形式实现&#xff0c;然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以…

【设计模式】--模板模式

豆浆制作 编写制作豆浆的程序&#xff0c;说明如下: 制作豆浆的流程 选材—>添加配料—>浸泡—>放到豆浆机打碎通过添加不同的配料&#xff0c;可以制作出不同口味的豆浆选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的请使用 模板方法模式 …

Java 设计模式 --- Template 模式 Java Template 模式 Java 模板设计模式

Java 设计模式 --- Template 模式 Java Template 模式 Java 模板设计模式 一、概述 模板设计模式&#xff1a; 父类定义通用抽象的功能方法&#xff0c;子类负责具体的实现。 本文将以一个通用的定时任务&#xff0c;处理模式&#xff0c;来讲解java 模板设计模式。 假设定时任…

设计模式——模板设计模式

一 前言 在面向对象开发过程中&#xff0c;通常会遇到这样的一个问题&#xff0c;我们知道一个算法所需的关键步骤&#xff0c;并确定了这些步骤的执行顺序&#xff0c;但是&#xff0c;某些步骤的具体实现是未知的&#xff0c;或者说某些步骤的实现是会随着环境的变化而改变&…

java设计模式之模板方法设计模式

什么是模板方法设计模式 模板方法是基于继承实现的&#xff0c;在抽象父类中声明一个模板方法&#xff0c;并在模板方法中定义算法的执行步骤(即算法骨架)。在模板方法模式中&#xff0c;可以将子类共性的部分放在父类里实现&#xff0c;而特性部分在子类中实现&#xff0c;只需…

一文搞懂设计模式--模板模式

Hi&#xff0c;大家好。今年的天气实在是太热了&#xff0c;七月份的厦门晒得我觉得身上冒出了烤肉香&#xff0c;不知道各位是否安好&#xff0c;但是在再热的天气也不能阻止我们学习的热情&#xff08;doge&#xff09;。今天的主题是模板&#xff08;Template Method&#x…

设计模式 – 模板设计模式

模板设计模式 模板方法模式&#xff08;Template Method Pattern&#xff09;&#xff0c;又叫模板模式(Template Pattern)&#xff0c;在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现&#xff0c;但调用将以抽象类中定义的方式进行。 该模式的主…

【设计模式】模板模式

博主声明&#xff1a; 转载请在开头附加本文链接及作者信息&#xff0c;并标记为转载。本文由博主 威威喵 原创&#xff0c;请多支持与指教。 本文首发于此 博主&#xff1a;威威喵 | 博客主页&#xff1a;https://blog.csdn.net/smile_running 这是23种设计模式中的模板模…

设计模式之模板模式(C++)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 一、模板模式是什么&#xff1f; 模板模式是一种行为型的软件设计模式&#xff0c;在父类中定义了一个模板算法&#xff0c;只实现…

设计模式——模板模式

更多内容&#xff0c;前往 IT-BLOG 在面向对象程序设计过程中&#xff0c;程序员常常会遇到如下情况&#xff1a;设计一个系统时知道算法所需的关键步骤&#xff0c;且确定了这些步骤的执行顺序&#xff0c;但某些步骤的具体实现还未知&#xff0c;或者说某些步骤的实现与具体…

深入理解设计模式之模板模式

我们平时办理入职的流程是&#xff1a;填写入职登记表->打印简历->复印学历->复印身份证->签订劳动合同->建立花名册->办理工牌->安排工位。我平时在家里炒菜的流程是&#xff1a;洗锅->点火->热锅->上油->下原料->翻炒->放调料->出…

数据化风控与催收人员策略管理

在印象中催收也许是个玄学&#xff0c;一个做得好的催收员除了催收技巧外、你需要了解心理学、懂声音学、懂人性、懂风水易经&#xff0c;比如就有个认识的人对什么岁数的人在哪个时辰拨打电话才比较好&#xff0c;都有讲究…总之一切尽在不言中… 但催收的管理却与之相反&…

信贷产品年终总结之风控评分模型

叮咚&#xff0c;信贷年终总结的又一个专题来了&#xff0c;作为报告总结类的系列型文章&#xff0c;近期我们番茄知识星球平台陆续发布了相关年终总结专题&#xff0c;依次为客群特征画像、贷中行为分析、贷后逾期表现等&#xff0c;以上文章可详见之前陆续发布的内容。该业务…

实操信贷场景中的反欺诈模型

今天的文章&#xff0c;关于反欺诈模型的实操&#xff0c;之前有跟大家分享过相关内容&#xff0c;部分反欺诈的领域的童鞋感觉内容比较有帮助&#xff0c;今天就该内容进行讲解。本文介绍的产品适合在消费零售信贷及现金场景贷中的中短期产品&#xff0c;其中涉及的变量参考有…

信贷全生命周期监控报表开发

对于任何一家从事信贷业务的机构而言&#xff0c;监控报表的设计及搭建尤为重要。因为监控报表不仅涉及信贷风控的资产监控&#xff0c;更能反馈客群质量的变化、内部管理手段的调整、策略规则对客群质量的影响等方面。为此&#xff0c;番茄风控输出此系列文章&#xff0c;我们…

信贷全流程监控报表有哪些?

风险策略分析工作是风险管理的重要工作内容&#xff0c;其工作内容需要涉及风控领域中多个环节及细节内容&#xff0c;包含贷前策略调整、策略分析调优、贷中业务监控、贷后策略调整等模块内容&#xff0c;涉及相关模块工作细节及工作内容&#xff0c;我们将一一为大家梳理及分…

详解小微机构贷前准入规则

关注 “番茄风控大数据”&#xff0c;获取更多数据分析与风控大数据的实用干货 嗨同学们好&#xff0c;现在疫情的情况已经出现大反转&#xff0c;我们国内的疫情已经控制住&#xff0c;多个城市都出现了连续多年0增长&#xff0c;一二线城市的部分公共场合也陆续对外开放&…

催收评分卡是如何搭建的?

1、背景 在整个风控体系中&#xff0c;一般分为贷前、贷中和贷后&#xff0c;贷前一般包括反欺诈和A卡&#xff0c;贷中一般使用B卡&#xff0c;贷后一般使用C卡&#xff0c;随着现在监管的越来越严格&#xff0c;催收的合规性越来越重要&#xff0c;所以各持牌金融机构越来越…

贷款中介业务管理系统-具备完善的贷前审批及贷后监管功能

一、系统介绍 银行贷款中介平台&#xff08;助保贷平台&#xff09;是一套适用于当今时代的中介系统&#xff0c;对于借款人来说可以满足他们上学、培训、旅游、购物等。平台根据各借款人资质推介给各银行或用自有资金放款。贷款中介系统提供了一套完善的网络借贷系统&#xff…

一文带你了解助贷行业

原文作者&#xff1a;宁阿姨&#xff08;知乎ID&#xff09; 原文链接&#xff1a;https://zhuanlan.zhihu.com/p/485880345 注&#xff1a;仅限自己学习使用 日常消费用花呗用信用卡&#xff0c;个人周转用借呗或微粒贷&#xff0c;买车买房用车贷房贷&#xff0c;作为消费者…