Hi,大家好。今年的天气实在是太热了,七月份的厦门晒得我觉得身上冒出了烤肉香,不知道各位是否安好,但是在再热的天气也不能阻止我们学习的热情(doge)。今天的主题是模板(Template Method)模式,还是老样子,学习设计模式需要在具体的场景中学习,让我们先来构建一个问题的场景。

场景定义

今天产品经理给了你一个需求,需要构建一个小明上班的场景。小明每天8:00从家里出发,因为小明就租在公司附近,每天只需要骑15分钟的自行车就可以到达公司,然后从楼下大厅进入公司。

OK,就是这样一个简单的场景。我们先思考一下如何实现,如果是公司刚来的初级程序员一定会直接说:简单,让我来。接着噼里啪啦3分钟完成了这样一段程序

public class Main {public static void main(String[] args) {System.out.println("小明出门了");System.out.println("小明骑上自行车");System.out.println("小明从大厅进入公司");}
}

程序输出:

小明出门了
小明骑上自行车
小明从大厅进入公司

简单易懂,没有任何问题,新来的小家伙嘴角扬起了一丝得意的微笑,心想:我又完美的解决了一项任务,照这么下去老板迟早有一天能够发现我这块发光的金子。不知道老板有没有一位美丽又待秀闺中的女儿…

你作为一个已经有些许经验的程序猿,一下子就看出小伙子心里的小九九,摸了摸还算茂密的头发,无奈的摇摇头。心里想到:小伙子,我当年也是这么想的,你还是too young too naive。等着看吧,问题马上就要来了。

果不其然,不一会儿万恶的产品经理就再次出现,他说刚刚客户来了一个电话,要对需求进行一些补充:

小明虽然住在公司旁边,但是小明有一个缺点,爱睡懒觉。经常会赖床到8:50才出门,这时候小明就会打滴滴从地下停车场出发去公司。新来的小伙子这时候已经开始意识到有些不对劲,但是还是很快完成了任务。他又创建了一个类用来负责处理这种情况

public class Main2 {public static void main(String[] args) {System.out.println("小明从地下车库出门了");System.out.println("小明坐上了滴滴");System.out.println("小明从大厅进入公司");}
}

程序输出:

小明从地下车库出门了
小明坐上了滴滴
小明从大厅进入公司

这时候你一看时间6:00了,于是熟练的摸上你的包,唰的一下从公司门口溜了出去,回家喝上一杯快乐水,生活美滋滋。第二天早上,你才刚来到公司,就听见部门主管在办公室大发雷霆,将新来的小伙子骂了个狗血淋头。一问情况才知道昨晚小伙留下加班,产品经理又提出了更多的需求:

小明升职加薪,买房买车每天自己开车上班
小明年逾古稀,只能摇轮椅上班

小伙子通通创建一个新类来处理不同的情况,现在已经创建了大大小小十多个类,主管今天早上review代码的时候直接被绕晕了,其他模块需要调动接口时需要了解每一个类的细节,根本没法用。过了一会,你就看见小伙子哭丧着脸从主管办公室走了出来。你心里一阵叹息,准备日行一善来唤起小伙子的斗志。于是你准备教给他你当初被主管骂了四次才从这个老家伙那偷师学到的神兵利器,模板模式。

什么是模板模式

模板模式实际上是在父类中定义处理流程的框架,在子类中具体实现具体处理的模式。在上面的场景中,我们可以发现,虽然小明从家中出发去公司的方式各种各样,但是总体上可以分为三个步骤:

  • 出门

  • 选择交通工具

  • 到公司

因此,就可以将这三个方法作为抽象方法提升到父类中,而由不同的子类来实现具体需要的功能

让我们看看修改后的代码的类图

模板模式中出现的角色

实际上,模板模式中只有两个关键性角色

AbstractClass(抽象类)

AbstractClass角色(例子中的GoToCompany)不仅负责实现模板方法(goToCompany方法),还负责声明在模板方法中使用到的抽象方法。这些抽象方法由子类ConcreteClass角色负责实现。

ConcreteClass(具体类)

该角色负责实现AbstractClass角色中定义的抽象方法,这里的实现将会在AbstractClass的模板方法中调用。在该例子中,CommonGoToCompany和HurryGoToCompany都是具体类。

模板方法的具体实现

基础的概念都已经了解完毕,接下来就可以进入具体的代码实现:

GoToCompany的实现

public abstract class GoToCompany {/*** 定义抽象的出门动作*/public abstract void goOut();/*** 定义抽象的选择交通工具动作*/public abstract void onTheWay();/*** 定义抽象的到达公司动作*/public abstract void arrivalCompany();/*** 定义好处理流程的框架,模板方法*/public void goToCompany(){//出发this.goOut();//选择交通工具this.onTheWay();//到达公司this.arrivalCompany();}
}

CommonGoToCompany的实现

public class CommonGoToCompany extends GoToCompany{/*** 出门方式的具体实现*/@Overridepublic void goOut() {System.out.println("小明出门了");}/*** 选择交通工具的具体实现*/@Overridepublic void onTheWay() {System.out.println("小明骑上自行车");}/*** 进入公司方式的具体实现*/@Overridepublic void arrivalCompany() {System.out.println("小明从大厅进入公司");}
}

HurryGoToCompany的实现

public class HurryGoToCompany extends GoToCompany{/*** 出门方式的具体实现*/@Overridepublic void goOut() {System.out.println("小明从地下车库出门了");}/*** 选择交通工具的具体实现*/@Overridepublic void onTheWay() {System.out.println("小明坐上了滴滴");}/*** 进入公司方式的具体实现*/@Overridepublic void arrivalCompany() {System.out.println("小明从大厅进入公司");}
}

Main的实现

public class Main {public static void main(String[] args) {// 如果需要修改小明的上班方式,只需要将CommonGoToCompany修改为HurryGoToCompany即可GoToCompany goToCompany = new CommonGoToCompany();goToCompany.goToCompany();}
}

拓展思路

如果你能看到这里,使用模板模式对你来说已经毫无困难。那么让我们更进一步,思考一下使用模板模式究竟能带来什么好处?

使用了通用的处理逻辑

使用模板模式的优点在于,我们只需要在父类中编写一次处理流程,而无需在子类中再次编写。如果我们采用最初的方法通过复制粘贴来拓展新方法,如果未来发现处理问题的逻辑发生错误,我们就不得不将所有的类进行修改。而采用模板方法,我们只需要修改父类中的模板方法。

父类与子类一致性

在例子中无论是使用HurryGoToCompany还是CommonGoToCompany的具体实现,他们都被保存在父类GoToCompany申明的变量中。无论父类变量中保存了哪一个子类的实例,程序都可以正常工作,这满足了里氏替换原则(LSP,不是老色批)的要求(什么?不知道什么是里氏替换原则,快来这篇文章看一看)

往期文章

一文搞懂设计模式–外传

一文搞懂设计模式–迭代器模式

一文搞懂设计模式–适配器模式

一文搞懂设计模式--模板模式相关推荐

  1. 一文读懂设计模式--策略模式

    编写代码的时候,需要满足开闭原则,我们想要增加新的功能不能去改变源码,而应该在之前的基础上进行扩展. 加入客户有了新的需求,我们应该使用扩展的方式实现. 概述 策略模式,又叫算法簇模式,就是定义了不同 ...

  2. 一文搞懂MySQL数据库分库分表

    如果数据量过大,大家一般会分库分表.分库需要注意的内容比较少,但分表需要注意的内容就多了. 工作这几年没遇过数据量特别大的业务,那些过亿的数据,因为索引设置合理,单表性能没有影响,所以实战中一直没用过 ...

  3. ES6学习——一文搞懂ES6

    ES6学习--一文搞懂ES6 es6介绍 ES全称EcmaScript,是脚本语言的规范,而平时经常编写的EcmaScript的一种实现,所以ES新特性其实就是指JavaScript的新特性. 为什么 ...

  4. 【UE·蓝图底层篇】一文搞懂NativeClass、GeneratedClass、BlueprintClass、ParentClass

    本文将对蓝图类UBlueprint的几个UClass成员变量NativeClass.GeneratedClass.BlueprintClass.ParentClass进行比较深入的讲解,看完之后对蓝图 ...

  5. 一文搞懂 Cocos Creator 3.0 坐标转换原理

    一文搞懂 Cocos Creator 3.0 坐标转换原理 屏幕坐标 UI 触点坐标 UI 多分辨率适配方案 UI 触点获取 不同坐标之间的转换 屏幕坐标与 3D 节点世界坐标互转 3D 节点之间的坐 ...

  6. 一文搞懂Qt中的颜色渐变(QGradient Class)

    一文搞懂Qt中的颜色渐变(QGradient Class) 1, 快速开始! Qt中与颜色渐变有关的类是QGradient 其中它又有三个子类:QLinearGradient.QRadialGradi ...

  7. 一文搞懂指标采集利器 Telegraf

    作者| 姜闻名 来源|尔达 Erda 公众号 ​ 导读:为了让大家更好的了解 MSP 中 APM 系统的设计实现,我们决定编写一个<详聊微服务观测>系列文章,深入 APM 系统的产品.架构 ...

  8. 【通信协议】一文搞懂SPI

    SPI总线简介 SPI(Serial Peripheral Interface)是 Motorola 公司推出的一种同步串行接口技术,是一种高速的,全双工,同步的通信总线. 接口定义 SPI接口共有4 ...

  9. 《一文搞懂NMS发展历程》Soft-NMS、Weighted NMS、IoU-Net、Softer-NMS、Adaptive NMS、DIoU-NMS

    <一文搞懂NMS发展历程>Soft-NMS.Weighted NMS.IoU-Net.Softer-NMS.Adaptive NMS.DIoU-NMS 文章目录 <一文搞懂NMS发展 ...

最新文章

  1. 开发日记-20190517 关键词 函数式编程(一)
  2. 用syslinux启动u盘
  3. hashmap实现倒排索引——查询多个单词出现在多个句子中
  4. ArrayBlockingQueue原理分析-remove方法
  5. oracle 回滚 drop的表,使用ODU恢复被DROP的表 | 信春哥,系统稳,闭眼上线不回滚!...
  6. centos 卸载_CentOS「linux」学习笔记12:磁盘管理、分区挂载卸载操作
  7. mysql 开发基础系列22 SQL Model(带迁移事项)
  8. 向量外积_解析几何 -向量
  9. 人工智能中的深度结构学习 Learning deep architectures for AI - Yoshua Bengio
  10. Metadata 元数据信息详解
  11. intellij idea 破解 2017
  12. 【T+】畅捷通T+存货档案批量修改存货属性
  13. OpenGL ES 纹理
  14. 第十五章 - 垃圾回收相关算法
  15. linux getfattr中文乱码,Linux下快速解析nf_conntrack
  16. JAVA程序 通过IP地址 获取MAC地址
  17. 我凭什么能上北大——贺舒婷
  18. 哥大暴跌16名!2023 USNews 全美大学排名发布!
  19. 多种计算机编程语言简介
  20. 微信小程序以SpringBoot作为后端开发遇到的问题及解决思路总结

热门文章

  1. Win10关闭Hyper-V打开VT的正确步骤
  2. 02-关于画布、Artboard(画板)
  3. 第一次参加区赛的收获——By Yvonne
  4. ANYbotics /elevation_mapping 配置
  5. 旭元数艺:数创未来,智攀高峰
  6. Web 智能代码编辑器 WeBuilder 2022
  7. mysql 字符集 性能_MySQL字符集不一致导致性能下降25%,你敢信?
  8. 节点(属性、层级、操作)
  9. python数据分析就业前景_数据分析师找工作的秘诀,从读懂招聘 JD 开始
  10. 命令行检查win10自动唤醒原因