第 16 章 模板方法模式

1、豆浆制作问题

编写制作豆浆的程序, 说明如下:

  1. 制作豆浆的流程:选材—>添加配料—>浸泡—>放到豆浆机打碎
  2. 通过添加不同的配料, 可以制作出不同口味的豆浆
  3. 选材、 浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的
  4. 请使用模板方法模式完成 (说明:因为模板方法模式,比较简单,很容易就想到这个方案, 因此就直接使用,不再使用传统的方案来引出模板方法模式)

2、模板方法模式基本介绍

  1. 模板方法模式(Template Method Pattern) , 又叫模板模式(Template Pattern), 在一个抽象类公开定义了执行它的方法的模板,它的子类可以按需要重写方法实现, 但调用将以抽象类中定义的方式进行
  2. 简单说, 模板方法模式定义一个操作中的算法(流程)的骨架, 而将一些步骤延迟到子类中, 使得子类可以不改变一个算法的结构, 就可以重定义该算法的某些特定步骤
  3. 模板方法设计模式属于行为型模式

3、模板方法模式原理类图

  1. AbstractClass 为抽象类, 类中实现了template()模板方法, 该方法定义了算法的骨架, 具体子类需要去实现抽象方法 operation 2,3,4
  2. ConcreteClass实现抽象方法 operation 2,3,4,以完成算法中特定子类的步骤

4、模板方法模式解决豆浆制作问题

应用实例要求

  1. 对于编写制作豆浆的程序, 说明如下:
  2. 制作豆浆的流程:选材—>添加配料—>浸泡—>放到豆浆机打碎
  3. 通过添加不同的配料, 可以制作出不同口味的豆浆
  4. 选材、 浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的

类图


代码实现

  1. SoyaMilk:抽象类,定义制作豆浆的模板方法,对于不同口味的豆浆,子类重写 addCondiments() 方法即可

    //抽象类,表示豆浆
    public abstract class SoyaMilk {// 模板方法, make , 模板方法可以做成final , 不让子类去覆盖final void make() {select();addCondiments();soak();beat();}// 选材料void select() {System.out.println("第一步:选择好的新鲜黄豆  ");}// 添加不同的配料, 抽象方法, 子类具体实现abstract void addCondiments();// 浸泡void soak() {System.out.println("第三步, 黄豆和配料开始浸泡, 需要3小时 ");}void beat() {System.out.println("第四步:黄豆和配料放到豆浆机去打碎  ");}
    }
    
  2. RedBeanSoyaMilk:红豆口味的豆浆,重写 addCondiments() 方法,添加红豆

    public class RedBeanSoyaMilk extends SoyaMilk {@Overridevoid addCondiments() {System.out.println(" 加入上好的红豆 ");}}
    
  3. PeanutSoyaMilk:花生口味的豆浆,重写 addCondiments() 方法,添加花生

    public class PeanutSoyaMilk extends SoyaMilk {@Overridevoid addCondiments() {System.out.println(" 加入上好的花生 ");}}
    
  4. Client:客户端

    public class Client {public static void main(String[] args) {// 制作红豆豆浆System.out.println("----制作红豆豆浆----");SoyaMilk redBeanSoyaMilk = new RedBeanSoyaMilk();redBeanSoyaMilk.make();// 制作花生豆浆System.out.println("----制作花生豆浆----");SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();peanutSoyaMilk.make();}}
    

5、模板方法模式的钩子方法

钩子方法

  1. 在模板方法模式的父类中, 我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”
  2. 还是用上面做豆浆的例子来讲解,比如,我们还希望制作纯豆浆,不添加任何的配料, 请使用钩子方法对前面的模板方法进行改造

代码演示

  1. SoyaMilk:添加 customerWantCondiments() 方法用于判断是否需要添加配料

    //抽象类,表示豆浆
    public abstract class SoyaMilk {// 模板方法, make , 模板方法可以做成final , 不让子类去覆盖.final void make() {select();if (customerWantCondiments()) {addCondiments();}soak();beat();}// 选材料void select() {System.out.println("第一步:选择好的新鲜黄豆  ");}// 添加不同的配料, 抽象方法, 子类具体实现abstract void addCondiments();// 浸泡void soak() {System.out.println("第三步, 黄豆和配料开始浸泡, 需要3小时 ");}void beat() {System.out.println("第四步:黄豆和配料放到豆浆机去打碎  ");}// 钩子方法,决定是否需要添加配料boolean customerWantCondiments() {return true;}
    }
    
  2. PureSoyaMilk:纯豆浆无需添加配料,所以 customerWantCondiments() 返回 false,空实现 addCondiments() 方法

    public class PureSoyaMilk extends SoyaMilk{@Overridevoid addCondiments() {//空实现}@Overrideboolean customerWantCondiments() {return false;}}
    
  3. Client:客户端

    public class Client {public static void main(String[] args) {// 制作红豆豆浆System.out.println("----制作红豆豆浆----");SoyaMilk redBeanSoyaMilk = new RedBeanSoyaMilk();redBeanSoyaMilk.make();// 制作花生豆浆System.out.println("----制作花生豆浆----");SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();peanutSoyaMilk.make();// 制作纯豆浆System.out.println("----制作纯豆浆----");SoyaMilk pureSoyaMilk = new PureSoyaMilk();pureSoyaMilk.make();}}
    

6、Spring 框架中的模板方法模式

类图

源码追踪

ConfigurableApplicationContext 中定义了抽象方法 refresh()

public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {// ...void refresh() throws BeansException, IllegalStateException;// ...

AbstractApplicationContext

  1. AbstractApplicationContext 实现了 ConfigurableApplicationContext 接口,重写了 refresh() 方法,AbstractApplicationContext 类中的 refresh() 方法就是模板方法

    public abstract class AbstractApplicationContext extends DefaultResourceLoaderimplements ConfigurableApplicationContext, DisposableBean {// ...@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}catch (BeansException ex) {// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}}}// ...
    
  2. obtainFreshBeanFactory() 方法中调用了 refreshBeanFactory() 方法和 getBeanFactory() 方法

  3. refreshBeanFactory() 方法和 getBeanFactory() 方法都是 AbstractApplicationContext 类中定义的抽象方法

  4. AbstractApplicationContext 类中定义了一些钩子方法: postProcessBeanFactory(beanFactory) 方法和 onRefresh() 方法,这些方法默认都是空实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aSK8fDQk-1600500059244)(第 16 章 模板方法模式.assets/image-20200828005824394.png)] —

AbstractRefreshableApplicationContext

AbstractRefreshableApplicationContext 继承 AbstractApplicationContext 类,并实现了一些方法的具体逻辑,比如

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {// ...@Overridepublic final ConfigurableListableBeanFactory getBeanFactory() {synchronized (this.beanFactoryMonitor) {if (this.beanFactory == null) {throw new IllegalStateException("BeanFactory not initialized or already closed - " +"call 'refresh' before accessing beans via the ApplicationContext");}return this.beanFactory;}}// ...

7、模板方法模式的注意事项

模板方法模式的注意事项和细节

  1. 基本思想是:将算法的具体实现流程编写在抽象父类中,某些具体的方法实现由子类重写。 需要修改算法时, 只要修改父类的模板方法或者已经实现的某些步骤流程, 子类就会继承这些修改
  2. 实现了最大化代码复用。 父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
  3. 既统一了算法, 也提供了很大的灵活性。 父类的模板方法确保了算法的结构保持不变, 同时由子类提供部分步骤的实现。
  4. 该模式的不足之处: 每一个不同的具体实现都需要一个子类实现, 导致类的个数增加, 使得系统更加庞大
  5. 一般模板方法都加上 final 关键字, 防止子类重写模板方法
  6. 模板方法模式使用场景: 当要完成在某个过程, 该过程要执行一系列步骤 , 这一系列的步骤基本相同, 但其个别步骤在实现时 可能不同, 通常考虑用模板方法模式来处理

第 16 章 模板方法模式相关推荐

  1. SaaS模式、技术与案例详解——第16章 SaaS模式可行性分析

    [本章导读语] 模型可以澄清相互间的关系,识别出关键元素,有意识地减少可能 引起的混淆. ________凯文.福斯伯格,<可视化项目管理> SaaS系统包括SaaS平台及应用系统,Saa ...

  2. 【笔记整理】图解设计模式 | 第16章 Mediator模式(只有一个仲裁者)

    [笔记整理]图解设计模式 | 导航 定义 组员向仲裁者报告,仲裁者向组员下达指示. 当发生麻烦事情的时候,通知仲裁者:当发生涉及全体组员的事情时,也通知仲裁者. 当仲裁者下达指示时,组员会立即执行.团 ...

  3. ASM:《X86汇编语言-从实模式到保护模式》第16章:Intel处理器的分页机制和动态页面分配...

    第16章讲的是分页机制和动态页面分配的问题,说实话这个一开始接触是会把人绕晕的,但是这个的确太重要了,有了分页机制内存管理就变得很简单,而且能直接实现平坦模式. ★PART1:Intel X86基础分 ...

  4. 【设计模式】第十三章:模板方法模式详解及应用案例

    系列文章 [设计模式]七大设计原则 [设计模式]第一章:单例模式 [设计模式]第二章:工厂模式 [设计模式]第三章:建造者模式 [设计模式]第四章:原型模式 [设计模式]第五章:适配器模式 [设计模式 ...

  5. 《JAVA编程思想》学习笔记:第16章(数组)

    目录 Java编程思想(一)第1~4章:概述 Java编程思想(二)第5章:初始化和清理 Java编程思想(三)第6章:访问权限 Java编程思想(四)第7章:复用类 Java编程思想(五)第8章:多 ...

  6. 设计模式之模板方法模式(Template Method)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...

  7. IA-32系统编程指南 - 第三章 保护模式的内存管理【1】

    第三章 保护模式的内存管理[1] [作者:lion3875 原创文章 参考文献<Intel 64 and IA-32 system programming guide>] IA-32保护模 ...

  8. 第19章 解释器模式(Interpreter Pattern)

    原文 第19章 解释器模式(Interpreter Pattern) 解释器模式 导读:解释器模式,平常用的比较的少,所以在写这个模式之前在博客园搜索了一番,看完之后那叫一个头大.篇幅很长,我鼓足了劲 ...

  9. mySQL 教程 第16章 MySQL复制

    第16章 MySQL复制 复制解决的问题 概述:你的网站访问量非常大,对系统的稳定性非常高,那么可以使用mysql功能的复制功能,复制是指将主要的数据库的DDL和DML操作通过二进制日志传到复制服务器 ...

最新文章

  1. Facebook加入AI芯片大战,挖走Google芯片产品开发负责人
  2. 第24讲 | 比特币专题(一)历史与货币
  3. python小整数池与大整数池
  4. 华为云.NET Core支持情况调查
  5. 一个致命的 Redis 命令,导致公司损失 400 万
  6. linux 读分区表文件,Linux 磁盘分区表、文件系统查看和操作
  7. css 总结内容用到的绝对居中的几种方式
  8. (39)时钟抖动约束
  9. mysql 批量数据导入报错_Mybatis 批量插入数据 关于Oracle 批量插入报错:ORA
  10. 网页框架模版(上、下(左、中、右))
  11. Gather more plan statistics by gather_plan_statistics hint
  12. linux cam软件,基于Linux图形交互式线切割CAM软件实现技术研究
  13. pandas读取xlsx文件
  14. 计算机专业中的量词,什么是量词?说明量词的分类和语法特征.
  15. Vim简单介绍和使用方法
  16. 不规则图形面积的计算
  17. H3C模拟器中文路径问题(提示:“当前系统用户名中包含非ASCII字符”! )方法尝试:
  18. python密码学pdf_Python密码学编程 ([美]斯维加特) 中文完整pdf扫描版[199MB]
  19. android支持色彩管理软件,色彩管理软件SpectraMagicTM SpectraMagicTM NX
  20. 一只喵的西行记-4 蛋蛋的忧桑

热门文章

  1. 中红外传感器行业调研报告 - 市场现状分析与发展前景预测(2021-2027年)
  2. 2021年中国超轻型直升机市场趋势报告、技术动态创新及2027年市场预测
  3. 第6章 见缝插圆(《C和C++游戏趣味编程》配套教学视频)
  4. c语言10怎么打开文件,Lecture 10 C语言文件操作
  5. linux建模工具有哪些,linux uml 免费 建模工具
  6. 微软再损一将!继Nat Friedman后,另一Xamarin联合创始人也已离职
  7. 漫谈 MinIO 集群扩容方法
  8. 跨越鸿沟,IIoT 如何更融合与开放?
  9. 太慢不能忍!CPU 又拿硬盘和网卡开刀了!
  10. “编程能力差,90%输在了数学上!”骨灰级开发:其实你们都是瞎努力!