设计模式-模板模式及应用
一般生活中我们办理一件事需要一套指定的流水线。例如银行办事,需要先去营业厅取号、排队、办理业务、综合评分。这一套固有的流程,取号、排队、综合评分等都是固定的,不同的客户会办理不同的业务,只有这块是没个客户不同的。类似于这种有固定的流水线,在软件设计的过程中,也是时长被使用到的。
定义这条流水线的方法为模板方法,执行到每个环节在调取相应方法的函数。在设计模式中,此类的问题为模板方法模式,下面就通过案例来说一下模板方法的使用。
定义及结构特点
看下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 AIP
是Java Servlet
制定的标准, 可以使用 javax.servlet
和 javax.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的子类。
这里具体的方法都由子类来实现,HttpServlet
不能被实例化是个抽象类,具体的方法请求都由子类来实现,HttpServlet
在这里充当模板模式的抽象类,不同子类的实现为具体的实现类,重新不同请求方法。
✨✨ 欢迎
设计模式-模板模式及应用相关推荐
- 12. 星际争霸之php设计模式--模板模式
题记 ============================================================================== 本php设计模式专辑来源于博客(jy ...
- Java设计模式-模板模式
Java设计模式-模板模式 什么是模板模式? 模板模式,顾名思义,就是通过模板拓印的方式. 定义模板,就是定义框架.结构.原型.定义一个我们共同遵守的约定. 定义模板,我们的剩余工作就是对其进行充实. ...
- Java设计模式—模板模式(Template)
模板模式 业务需求 编写豆浆制作程序 选材-添加配料-浸泡-豆浆机打碎 选材.浸泡.打碎这几步对于制作不同豆浆都是一样的 比较简单,这里不再使用传统方法,直接上设计模式 模板模式基本介绍 模板模式,在 ...
- 【设计模式】Java设计模式 - 模板模式
[设计模式]Java设计模式 - 模板模式
- C语言实现设计模式—模板模式
文章目录 C语言实现设计模式-模板模式 模板模式介绍 UML用例说明 实际场景使用 框架结构[共用框架]-通用的串口协议分析函数 不同实现-数据类型区别 C语言实现设计模式-模板模式 模板模式介绍 在 ...
- 16、Python与设计模式--模板模式
一.股票查询客户端 投资股票是种常见的理财方式,我国股民越来越多,实时查询股票的需求也越来越大.今天,我们通过一个简单的股票查询客户端来认识一种简单的设计模式:模板模式. 根据股票代码来查询股价分为如 ...
- PHP设计模式——模板模式
声明:本系列博客参考资料<大话设计模式>,作者程杰. 模板模式准备一个抽象类,将部分逻辑以具体方法以及具体构造形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑.不同的子类可以以不同的 ...
- 设计模式 模板模式和策略模式
模板模式和策略模式,有相识的地方,都是通过对钩子方法的调用,来实现一个业务的完整逻辑. 所以这里我将两种模式放在一起介绍,比较容易加深对这两种模式的理解. 模板模式 在模板模式(Template Pa ...
- java设计模式————模板模式,手撸一个JDBCTemplate
模板模式(Template Method Pattern) 定义一个算法的骨架,并允许子类为一个或者多个步骤提供实现. 模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤. 属于行为 ...
- 一文搞懂设计模式--模板模式
Hi,大家好.今年的天气实在是太热了,七月份的厦门晒得我觉得身上冒出了烤肉香,不知道各位是否安好,但是在再热的天气也不能阻止我们学习的热情(doge).今天的主题是模板(Template Method ...
最新文章
- TypeError: the JSON object must be str, bytes or bytearray, not dict
- java在注解中绑定方法参数的解决方案
- C语言中冒泡排序及优化
- Unity联网游戏基础原理与字节数组
- html多选框 jquery,jQuery Select多选
- 按条件分类_保税仓储企业能否同时存储非保货物?“仓储货物安装台分类监管”如何申请?...
- MSP430F5529 DriverLib 库函数学习笔记(八)模数转换模块(ADC12)
- thinkphp v5.0.11漏洞_ThinkPHP(5.1.x ~ 5.1.31 5.0.x ~ 5.0.23)GetShell漏洞
- 测试linux和window下 jdk最大能使用多大内存
- 开源不等于免费!谷歌如何通过安卓开源成为移动时代霸主? | 涛滔不绝
- lombok slfj 中_lombok @Slf4j注解
- 将vue,H5项目打包成app,apk安装包
- 教你3招解决PDF文字无法复制的所有问题!!!
- 宾夕法尼亚大学发明了第一代电子管计算机,新手计算机基础入门
- oracle报错ora-12162,sqlplus登录提示:ORA-12162错误
- Result的类型分析和总结
- ADO,ADO.NET
- java 撤销恢复按钮_java文本的撤销和恢复
- 学习python需要很多数学知识吗_Python数据分析需要学习哪方面的数学知识
- Python 保留小数位
热门文章
- 游戏研发运营工作介绍【esyi杂谈】
- 在Ubuntu主机上使用USB无线网卡
- Linux相关知识的第四回合
- python制作送心小人动画,利用Python如何画一颗心、小人发射爱心
- 成功并不是你想像的那么难
- angularjs 获取复选框的值_侠客行第二季来袭,教你如何快速获取侠名值_DNF游戏新闻 - 地下城与勇士 - DNF...
- 基于PP-PicoDet的钢铁缺陷检测
- 怎样把做的HTML做成链接,如何将自己做的HTML文档变成超链接
- 目前最大的人像抠图数据集P3M-10k开源了!助力隐私保护的人像抠图研究
- 元宇宙至少需要1万倍算力?