【设计模式——学习笔记】23种设计模式——模板方法模式Template Method(原理讲解+应用场景介绍+案例介绍+Java代码实现)

文章目录

  • 介绍
    • 基本介绍
    • 使用说明
    • 应用场景
    • 登场角色
  • 案例实现
    • 案例一
      • 问题介绍
      • 实现
      • 模板方法模式的钩子方法
    • 案例二
      • 实现
  • 模板方法模式在IOC的源码分析
  • 总结
  • 思考
    • 思考一
    • 思考二
  • 文章说明

介绍

基本介绍

  • 模板方法模式,又叫模板模式,在一个抽象类中定义了一个执行它的其他方法的公开模板方法,子类可以按需重写抽象类的抽象方法
  • 简单说,模板方法模式 定义一个操作中的算法(或者说流程)的骨架,而将一些步骤下放到子类中实现,使得子类可以在不改变算法结构的基础上,可以重新定义算法的某些步骤
  • 该模式属于行为型模式

使用说明

在这里插入图片描述

【AbstractClass】

  • template方法规定了如何调用operation2operation3operation4这几个子方法,子方法可以是抽象方法(需要子类来实现),也可以是已经实现的方法

【ConcreteClass、ConcreteClassB】

  • 用来实现父类的抽象方法

应用场景

当要完成某个过程,该过程要执行一系列步骤,这一系列的步骤基本相同,但其个别步骤可能有不同的实现,通常考虑用模板方法模式来处理

登场角色

  • AbstractClass(抽象类):该角色负责实现模板方法,还声明在模板方法中所使用到的抽象方法
  • ConcreteClass(具体类):负责具体实现AbstractClass角色中定义的抽象方法。这里实现的方法将会在AbstractClass角色的模板方法中被调用
  • Client(客户端):使用具体类继承的模板方法

案例实现

案例一

问题介绍

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

  • 通过添加不同的配料,可以制作出不同口味的豆浆
  • 制作豆浆的流程:选材—>添加配料—>浸泡—>放到豆浆机打碎,这几个步骤对于制作每种口味的豆浆都是一样的

实现

【豆浆抽象类】

package com.atguigu.template;/*** 抽象类,表示豆浆*/
public abstract class SoyaMilk {/*** 模板方法, 模板方法可以做成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("第四步:黄豆和配料放到豆浆机去打碎  ");}
}

【红豆豆浆】

package com.atguigu.template;/*** 红豆豆浆*/
public class RedBeanSoyaMilk extends SoyaMilk {@Overridevoid addCondiments() {System.out.println(" 加入上好的红豆 ");}}

【花生豆浆】

package com.atguigu.template;/*** 花生豆浆*/
public class PeanutSoyaMilk extends SoyaMilk {@Overridevoid addCondiments() {System.out.println(" 加入上好的花生 ");}}

【主类】

package com.atguigu.template;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();}}

【运行】

----制作红豆豆浆----
第一步:选择好的新鲜黄豆  加入上好的红豆 
第三步:黄豆和配料开始浸泡, 需要3小时 
第四步:黄豆和配料放到豆浆机去打碎  
----制作花生豆浆----
第一步:选择好的新鲜黄豆  加入上好的花生 
第三步:黄豆和配料开始浸泡, 需要3小时 
第四步:黄豆和配料放到豆浆机去打碎  Process finished with exit code 0

模板方法模式的钩子方法

在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”。

应用场景:希望可以制作纯豆浆,需要添加任何配料,使用钩子方法改造上面的程序

【豆浆抽象类】

package com.atguigu.template.improve;//抽象类,表示豆浆
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("第四步:黄豆和配料放到豆浆机去打碎  ");}/*** 钩子方法,决定是否需要添加配料* @return*/boolean customerWantCondiments() {return true;}
}

【纯豆浆】

package com.atguigu.template.improve;/*** 纯豆浆*/
public class PureSoyaMilk extends SoyaMilk{@Overridevoid addCondiments() {//空实现}/*** 如果需要自定义钩子函数,就重写这个方法,不需要就不用重写* @return*/@Overrideboolean customerWantCondiments() {return false;}}

【主类】

package com.atguigu.template.improve;public class Client {public static void main(String[] args) {System.out.println("----制作纯豆浆----");SoyaMilk pureSoyaMilk = new PureSoyaMilk();pureSoyaMilk.make();}}

【运行】

----制作纯豆浆----
第一步:选择好的新鲜黄豆  
第三步:黄豆和配料开始浸泡, 需要3小时 
第四步:黄豆和配料放到豆浆机去打碎  Process finished with exit code 0

案例二

在这里插入图片描述

实现

【抽象类】

package com.atguigu.template.Sample;/*** 抽象类AbstractDisplay*/
public abstract class AbstractDisplay {/*** 交给子类去实现的抽象方法(1) open*/public abstract void open();/*** 交给子类去实现的抽象方法(2) print*/public abstract void print();/*** 交给子类去实现的抽象方法(3) close*/public abstract void close();/*** 本抽象类中实现的display方法,模板方法*/public final void display() {// 首先打开…open();// 循环调用5次printfor (int i = 0; i < 5; i++) {print();}// …最后关闭。这就是display方法所实现的功能close();}
}

【子类:CharDisplay】

package com.atguigu.template.Sample;/*** CharDisplay是AbstractDisplay的子类*/
public class CharDisplay extends AbstractDisplay {/*** 需要显示的字符*/private char ch;public CharDisplay(char ch) {// 保存在字段中this.ch = ch;}public void open() {// 显示开始字符"<<"System.out.print("<<");}public void print() {// 显示保存在字段ch中的字符System.out.print(ch);}public void close() {// 显示结束字符">>"System.out.println(">>");}
}

【子类:StringDisplay】

package com.atguigu.template.Sample;/*** StringDisplay也是AbstractDisplay的子类*/
public class StringDisplay extends AbstractDisplay {/*** 需要显示的字符串*/private String string;/*** 以字节为单位计算出的字符串长度*/private int width;public StringDisplay(String string) {this.string = string;// 将字符串的字节长度也保存在字段中,以供后面使用this.width = string.getBytes().length;}public void open() {// 调用该类的printLine方法画线printLine();}public void print() {// 给保存在字段中的字符串前后分别加上"|"并显示出来System.out.println("|" + string + "|");}public void close() {// 与open方法一样,调用printLine方法画线printLine();}/*** 被open和close方法调用。由于可见性是private,因此只能在本类中被调用*/private void printLine() {// 显示表示方框的角的"+"System.out.print("+");                for (int i = 0; i < width; i++) {// 显示width个"-",和"+"一起组成边框System.out.print("-");                       }// 显示表示方框的角的"+"System.out.println("+");                 }
}

【主类】

package com.atguigu.template.Sample;public class Main {public static void main(String[] args) {// 生成一个持有'H'的CharDisplay类的实例 AbstractDisplay d1 = new CharDisplay('H');// 生成一个持有"Hello, world."的StringDisplay类的实例 AbstractDisplay d2 = new StringDisplay("Hello, world.");// 生成一个持有"你好,世界。"的StringDisplay类的实例 AbstractDisplay d3 = new StringDisplay("你好,世界。");/** 由于d1、d2和d3都是AbstractDisplay类的子类* 可以调用继承的display方法* 实际的程序行为取决于CharDisplay类和StringDisplay类的具体实现*/d1.display();d2.display();d3.display();}}

【运行】

<<HHHHH>>
+-------------+
|Hello, world.|
|Hello, world.|
|Hello, world.|
|Hello, world.|
|Hello, world.|
+-------------+
+------------------+
|你好,世界。|
|你好,世界。|
|你好,世界。|
|你好,世界。|
|你好,世界。|
+------------------+Process finished with exit code 0

模板方法模式在IOC的源码分析

在这里插入图片描述

实现类有一个模板方法

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

另一个钩子方法

在这里插入图片描述

在这里插入图片描述

除此之外,java.io.InputStream类中也使用了Template Method

总结

【优点】

  • 既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现
  • 实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。如果模板方法有bug只需要修改一个类即可,不使用模板方法模式的话,就需要修改多个类的代码
  • 父类和子类具有一致性:在示例程序中,不论是CharDisplay的实例还是StringDisplay的实例,都是先保存在AbstractDisplay类型的变量中,然后再来调用display方法的。使用父类类型的变量保存子类实例的优点是,即使没有用instanceof等指定子类的种类程序也能正常工作。无论在父类类型的变量中保存哪个子类的实例,程序都可以正常工作,这种原则称为里氏替换原则

【不足】

  • 不足:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大
  • 一般模板方法都加上final关键字, 防止子类重写模板方法

思考

思考一

问:如果想要让示例程序中的open、print、close方法可以被具有继承关系的类和同一程序包中的类调用,但是不能被无关的其他类调用,应当怎么做呢?

答:使用protected关键字修饰这些方法,不要使用public。

【Java四种权限修饰符】

publicprotected(default)private
类本身
同一个包下的类×
不同包,但是我的子类××
不同包非子类×××

注:default不需要写出来,不写权限修饰符默认就是defaunt

思考二

问:Java中的接口与抽象类很相似。接口同样也是抽象方法的集合,但是在模板方法模式中,我们却无法使用接口来扮演AbstractClass 角色,请问这是为什么呢?

答:因为使用接口无法实现模板方法和其他的方法。

文章说明

  • 本文章为本人学习尚硅谷的学习笔记,文章中大部分内容来源于尚硅谷视频(点击学习尚硅谷相关课程),也有部分内容来自于自己的思考,发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识,如有侵权请联系删除,最后对尚硅谷的优质课程表示感谢。
  • 本人还同步阅读《图解设计模式》书籍(图解设计模式/(日)结城浩著;杨文轩译–北京:人民邮电出版社,2017.1),进而综合两者的内容,让知识点更加全面

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

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

相关文章

详解AMQP协议

目录 1.概述 1.1.简介 1.2.抽象模型 2.spring中的amqp 2.1.spring amqp 2.2.spring boot amqp 1.概述 1.1.简介 AMQP&#xff0c;Advanced Message Queuing Protocol&#xff0c;高级消息队列协议。 百度百科上的介绍&#xff1a; 一个提供统一消息服务的应用层标准高…

忘掉MacType吧,TtfAutoHint手工删除ttc、ttf字体的hinting,微软雅黑字体更显平滑

Windows的ClearType渲染字体方式&#xff0c;结合臭名昭著的hinting技术使微软雅黑字体备受争议&#xff0c;正所谓&#xff1a;成也hinting&#xff0c;败也hinting。 首先什么是hinting&#xff1f; Hinting 这个词一直都没有中文名称&#xff0c;我用粤语将它音译为“牵挺”…

数据管理基础知识

数据管理原则 数据管理与其他形式的资产管理的共同特征&#xff0c;涉及了解组织拥有哪些数据以及可以使用这些数据完成哪些工作&#xff0c;然后确定如何最好的使用数据资产来实现组织目标与其他流程一样&#xff0c;他必须平衡战略和运营需求&#xff0c;通过遵循一套原则&a…

四、Unity中颜色空间

Unity中的设置 通过点击菜单Edit->Project Settings->Player页签->Other Settings下的Rendering部分进行修改&#xff0c;参数Color Space可以选择Gamma或Linear。 当选择Gamma Space时&#xff0c;Unity不会做任何处理。当选择Linear Space时&#xff0c;引擎的渲染…

华为云CTS 使用场景

云审计服务 CTS 云审计服务&#xff08;Cloud Trace Service&#xff09;&#xff0c;帮助您监控并记录华为云账号的活动&#xff0c;包括通过控制台、API、开发者工具对云上产品和服务的访问和使用行为&#xff0c;提供对各种云资源操作记录的收集、存储和查询功能&#xff0…

【Linux 网络】 传输层协议之UDP协议

UDP协议 UDP协议的位置UDP协议的特点UDP协议的格式UDP使用注意事项 UDP协议的位置 在网络套接字编程时用到的各种接口&#xff0c;是位于应用层和传输层之间的一层系统调用接口&#xff0c;这些接口是由系统提供的。我们可以通过这些接口来搭建上层应用&#xff0c;比如HTTP协议…

jenkins pipeline项目

回到目录 将练习jenkins使用pipeline项目&#xff0c;结合k8s发布一个简单的springboot项目 前提&#xff1a;jenkins的环境和k8s环境都已经安装完成&#xff0c;提前准备了gitlab和一个简单的springboot项目 创建一个流水线项目 流水线中选择git&#xff0c;并选择gitlab的…

Linux系统jenkins+newman+postman持续集成环境搭建

1、首先安装nodejs 下载nodejs压缩包&#xff0c;下载地址&#xff1a;nodejs官网下载 建议不用下载最新的&#xff0c;我这里用的是推荐的v12.18版本 下载和解压命令 wget https://nodejs.org/dist/v12.18.3/node-v12.18.3-linux-x64.tar.xz解压安装包&#xff08;记得没有z&…

rv1109/1126 rknn 模型部署过程

rv1109/1126是瑞芯微出的嵌入式AI芯片&#xff0c;带有npu, 可以用于嵌入式人工智能应用。算法工程师训练出的算法要部署到芯片上&#xff0c;需要经过模型转换和量化&#xff0c;下面记录一下整个过程。 量化环境 模型量化需要安装rk的工具包&#xff1a; rockchip-linux/rk…

【Mybatis】Mybatis架构简介

文章目录 1.整体架构图2. 基础支撑层2.1 类型转换模块2.2 日志模块2.3 反射工具模块2.4 Binding 模块2.5 数据源模块2.6缓存模块2.7 解析器模块2.8 事务管理模块 3. 核心处理层3.1 配置解析3.2 SQL 解析与 scripting 模块3.3 SQL 执行3.4 插件 4. 接口层 1.整体架构图 MyBatis…

爬虫008_流程控制语句_if_if else_elif_for---python工作笔记026

然后我们再来看一下这里的,判断,可以看到 再看一个判断,这里的布尔类型 第二行有4个空格,python的格式 注意这里,输入的age是字符串,需要转一下才行 int可以写到int(intput("阿斯顿法师打发地方")) 这样也可以

集中/本地转发、AC、AP

1.ADSL ADSL MODEM&#xff08;ADSL 强制解调器&#xff09;俗称ADSL猫 ADSL是一种异步传输模式&#xff08;ATM)。ADSL是指使用电话线上网&#xff0c;需要专用的猫&#xff08;Modem)&#xff0c;在上网的时候高频和低频分离&#xff0c;所以上网电话两不耽误&#xff0c;速…

LBP特征笔记

LBP&#xff0c;局部二值模式&#xff08;Local Binary Pattern&#xff09;&#xff0c;是一种描述图像局部纹理特征的方式&#xff0c;具有旋转不变性和灰度不变性。首先由T. Ojala, M.Pietikinen, 和 D. Harwood 在1994年提出。 LBP特征描述 基础LBP算子 基础的LBP算子定义…

分布式ID性能评测:CosId VS 美团 Leaf

环境 MacBook Pro (M1)JDK 17JMH 1.36运行在本机 Docker 内的 mariadb:10.6.4 运行 CosId SegmentChainId 模式&#xff0c;基准测试代码&#xff1a; Benchmarkpublic long generate() {return segmentChainId.generate();}Leaf 基准测试代码&#xff1a; Benchmarkpublic l…

使用正则表达式 移除 HTML 标签后得到字符串

需求分析 后台返回的数据是 这样式的 需要讲html 标签替换 high_light_text: "<span stylecolor:red>OPPO</span> <span stylecolor:red>OPPO</span> 白色 01"使用正则表达式 function stripHTMLTags(htmlString) {return htmlString.rep…

stm32 舵机 cubemx

文章目录 前言一、cubemx配置二、代码1.serve.c2.serve.h3.主函数 总结 前言 stm32对舵机进行控制&#xff0c;很简单直接一个pwm就可以实现 pwm的周期是50HZ占空比分别对应 一个0.5ms的高电平对应于0度 一个1.5ms的高电平对应于90度 一个2.5ms的高电平对应于180度 因此&#…

音视频--DTMF信号发送及检测

参考资料 https://zh.wikipedia.org/wiki/%E5%8F%8C%E9%9F%B3%E5%A4%9A%E9%A2%91https://www.cnblogs.com/lijingcheng/p/4454932.html 1. DTMF是什么 1.1 DTMF定义 双音多频信号&#xff08;英语&#xff1a;Dual-Tone Multi-Frequency&#xff0c;简称&#xff1a;DTMF&a…

mac 下用brew快速安装CommandLineTools

有时候用git 就会提示安装CommandLineTools &#xff0c;xcode太大又不想安装&#xff0c;怎么办呢我们可以试下下面的方式 什么是Brew&#xff1a; Brew是Mac OS X下的一个包管理器&#xff0c;可以方便地安装、升级和卸载很多常用的软件包 在mac下如何安装呢&#xff1a; …

爆肝整理,Postman接口测试-参数关联实战(详细步骤)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 接口测试什么时候…

AlexNet卷积神经网络-笔记

AlexNet卷积神经网络-笔记 AlexNet卷积神经网络2012年提出 测试结果为&#xff1a; 通过运行结果可以发现&#xff0c; 在眼疾筛查数据集iChallenge-PM上使用AlexNet&#xff0c;loss能有效下降&#xff0c; 经过5个epoch的训练&#xff0c;在验证集上的准确率可以达到94%左右…