设计模式学习-使用go实现模板模式
模板模式
定义
模板模式(TemplateMethod):定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。
模板方法模式就是提供一个代码复用平台,当不变和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。通过模板方法模式把这些行为搬到单一的地方,这样就帮助子类摆脱重复的不变行为的纠缠。
模板模式的作用
1、复用
所有的子类可以复用父类中提供的模板方法的代码
2、扩展
框架通过模板模式提供功能扩展点,让框架用户可以在不修改框架源码的情况下,基于扩展点定制化框架的功能
优点
1、封装不变部分,扩展可变部分;
2、提取公共代码,便于维护;
3、行为由父类控制,子类实现。
缺点
每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
适用范围
1、有多个子类共有的方法,且逻辑相同;
2、重要的、复杂的方法,可以考虑作为模板方法。
代码实现
考试时候试卷,对于试题部分,同一场考试内容都是一样的。试卷做完交卷只是我们每人个人填写的答案不同,那么试题就可以作为模板,我们只用去写答案。
type TestPaperImpl interface {
testQuestion1()
testQuestion2()
Answer1()
Answer2()
}
type testPaper struct {
}
func (t *testPaper) testQuestion1() {
fmt.Println("问题:中国有多少个民族")
}
func (t *testPaper) testQuestion2() {
fmt.Println("问题:中国有多大")
}
func (t *testPaper) Answer1() {
}
func (t *testPaper) Answer2() {
}
// 封装具体步骤
func doPaper(paper TestPaperImpl) {
paper.testQuestion1()
paper.Answer1()
paper.testQuestion2()
paper.Answer2()
}
type student1 struct {
*testPaper
}
func (s *student1) Answer1() {
fmt.Println("答案:56")
}
func (s *student1) Answer2() {
fmt.Println("答案:很大")
}
type student2 struct {
*testPaper
}
func (s *student2) Answer1() {
fmt.Println("答案:51")
}
func (s *student2) Answer2() {
fmt.Println("答案:不知道")
}
测试文件
func TestTestPaper(t *testing.T) {
st1 := &student1{}
doPaper(st1)
fmt.Println("++++++++++++++")
st2 := &student2{}
doPaper(st2)
}
结果
问题:中国有多少个民族
答案:56
问题:中国有多大
答案:很大
++++++++++++++
问题:中国有多少个民族
答案:51
问题:中国有多大
答案:不知道
结构图

回调
回调起到的作用和模板模式一样
相对于普通的函数调用来说,回调是一种双向调用关系。A类事先注册某个函数F到B类,A类在调用B类的P函数的时候,B类反过来调用A类注册给它的F函数。这里的F函数就是“回调函数”。A调用B,B反过来又调用A,这种调用机制就叫作“回调”
回调分为两种:
1、同步回调
在函数返回之前执行回调函数,同步回调看起来有点像模板模式
2、异步回调
在函数返回之后执行回调函数
如果做过支付的同学肯定很熟悉这个,例如微信支付,我们调用微信支付进行付款,成功之后我们的服务器会收到微信端支付的消息回调,然后进行支付成功之后的后续操作。
上面的考试例子使用回调实现
type testPaperCallback struct {
}
func (t *testPaperCallback) testQuestion1() {
fmt.Println("问题1:中国有多少个民族")
}
func (t *testPaperCallback) testQuestion2() {
fmt.Println("问题2:中国有多大")
}
func (t *testPaperCallback) SubCallback(callback CallbackImpl) {
t.testQuestion1()
t.testQuestion2()
callback.Callback()
}
type CallbackImpl interface {
Callback()
}
type student3 struct {
*testPaperCallback
}
func (s *student3) Callback() {
fmt.Println("答案1:56")
fmt.Println("答案2:测试")
}
func doPaperCallback(student *student3) {
student.SubCallback(&student3{})
}
结构图

模板模式 VS 回调
从应用场景上来看,同步回调跟模板模式几乎一致。它们都是在一个大的算法骨架中,自由替换其中的某个步骤,起到代码复用和扩展的目的。而异步回调跟模板模式有较大差别,更像是观察者模式。
从代码实现上来看,回调和模板模式完全不同。回调基于组合关系来实现,把一个对象传递给另一个对象,是一种对象之间的关系;模板模式基于继承关系来实现,子类重写父类的抽象方法,是一种类之间的关系。
回调可以使用匿名类来创建回调对象,可以不用事先定义类;而模板模式针对不同的实现都要定义不同的子类。
如果某个类中定义了多个模板方法,每个方法都有对应的抽象方法,那即便我们只用到其中的一个模板方法,子类也必须实现所有的抽象方法。而回调就更加灵活,我们只需要往用到的模板方法中注入回调对象即可。
参考
【文中代码】https://github.com/boilingfrog/design-pattern-learning/tree/master/模板模式
【大话设计模式】https://book.douban.com/subject/2334288/
【极客时间】https://time.geekbang.org/column/intro/100039001
【模板模式】https://boilingfrog.github.io/2021/11/20/使用go实现模板模式/
设计模式学习-使用go实现模板模式的更多相关文章
- Java设计模式学习笔记(二) 简单工厂模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 正文开始... 1. 简介 简单工厂模式不属于GoF23中设计模式之一,但在软件开发中应用也较为 ...
- Java设计模式学习笔记(三) 工厂方法模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 简介 上一篇博客介绍了简单工厂模式,简单工厂模式存在一个很严重的问题: 就是当系统需要引入 ...
- Java设计模式学习笔记(四) 抽象工厂模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 抽象工厂模式概述 工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问 ...
- C#设计模式学习笔记:(13)模板方法模式
本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7837716.html,记录一下学习过程以备后续查用. 一.引言 今天我们要讲行为型设计模式的第一个模式--模 ...
- 设计模式(十四)——模板模式(SpringIOC源码分析)
1 豆浆制作问题 编写制作豆浆的程序,说明如下: 1) 制作豆浆的流程 选材--->添加配料--->浸泡--->放到豆浆机打碎 2) 通过添加不同的配料,可以制作出不同口味的豆浆 3 ...
- 扯淡设计模式2:java,模板模式,
模板模式: package com.dayuanit.service; public abstract class UserService { public void login(String use ...
- C#设计模式学习笔记:(23)解释器模式
本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/8242238.html,记录一下学习过程以备后续查用. 一.引言 今天我们要讲行为型设计模式的第十一个模式-- ...
- javascript设计模式学习之十三——职责链模式
一.职责链的定义和使用场景 职责链模式的定义是,职责链模式将一系列可能会处理请求的对象连接成一条链,请求在这些对象之间一次传递,直到遇到一个可以处理它的对象.从而避免请求的发送者和接收者之间的耦合关系 ...
- C#设计模式学习笔记:(21)访问者模式
本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/8135083.html,记录一下学习过程以备后续查用. 一.引言 今天我们要讲行为型设计模式的第九个模式--访 ...
随机推荐
- node-gyp项目命名BUG
当我们编写node原生模块的时候,免不了对node-gyp项目进行命名,在node-gyp进行build的时候,会跟binding.gyp配置文件中的target_name生成对应的原生模块.但是,如 ...
- Linux Bash命令杂记(cut sort uniq wc tee)
Linux Bash命令杂记(cut sort uniq wc tee) 数据流重定向 标准输入(stdin):代码为0,使用<或<<: 标准输出(stdout):代码为1,使用&g ...
- 题解 「BZOJ2178」圆的面积并
题目传送门 题目大意 给出 \(n\) 个圆,求它们并的面积大小. \(n\le 10^3\) 思路 如果您不会自适应辛普森法,请戳这里学习 其实我们发现,如果我们设 \(f(x)\) 表示 \(x= ...
- 错误 Unresolved reference 'AF_INET' 解决办法
错误代码如下: import socketserer_socket = socket.socket(AF_INET, SOCK_DGAM) 错误信息: 原因分析: 1.AF_INET,SOCK_DGA ...
- vue.$nextTick实现原理
源码: const callbacks = [] let pending = false function flushCallbacks () { pending = false const copi ...
- vue基础-组件&插槽
组件 组件化的意义:封装(复用,把逻辑隐藏起来,提高可维护性),快速开发(搭积木) 约定:我们通常把那些除了HTML标签以外的自定义组件,才称为'组件',结论是,我们说"父组件"& ...
- 使用Google Fonts注意事项
Google Fonts是一个字体嵌入服务库. 这包括免费和开源字体系列.用于浏览库的交互式 Web 目录以及用于通过 CSS 和 Android 使用字体的 API. Google 字体库中的流行字 ...
- 【好好编程-技术博客】微信小程序开发中前后端的交互
微信小程序开发中前后端的交互 微信小程序的开发有点类似与普通网页的开发,但是也不尽然相同.小程序的主要开发语言是JavaScript,开发同普通的网页开发有很大的相似性,对于前端开发者而言,从网页开发 ...
- 设置nginx进程可打开最大的文件数
涉及到的nginx配置参数: worker_processes: 表示操作系统启动多少个工作进程在运行,一般这个参数设置成CPU核数的倍数 worker_connections:表示nginx的工作进 ...
- GPIO原理与配置(跑马灯,蜂鸣器,按键)
一.STM32 GPIO固件库函数配置方法 1. 根据需要在项目中删掉一些不用的固件库文件,保留有用的固件库文件 2. 在stm32f10x_conf.h中注释掉这些不用的头文件 3. STM32的I ...