Go设计模式(23)-迭代器模式

Go 1.23 迭代器,统一标准,改善 Go 生态系统 本文详细介绍了 Go 1.23 版本中的迭代器。内容涵盖了引入 标准迭代器 的主要原因、迭代器的定义及其使用方法。此外,还讨论了 iter 包的功能扩展,以及 slices 和 maps 标准库中新增的与迭代器相关的函数。 有人认为,引入迭代器使 Go 变得更加复杂,因为迭代器的代码实现可能会影响可读性。对于刚接触 Go 迭代器的开发者来说,确实可能感到有些不适应。不过,Go 官方为了简化迭代器的使用,新增了 iter 包,并在 slices 和 maps 包中提供了许多便捷函数,以提升开发体验。 阅读详情

迭代器模式从来没有写过,第一次接触迭代器,还是好多年前学C++的STL的时候。当时觉得用迭代器太麻烦了,后来用习惯了觉得真香。

UML类图位置:https://www.processon.com/view/link/60d29bf3e401fd49502afd25

本文代码链接为:https://github.com/shidawuhen/asap/blob/master/controller/design/23iterator.go

1.定义

1.1 迭代器模式

迭代器模式(Iterator):提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。

UML:

图片

1.2分析

通过UML可以看出,对于集合Aggregate,其遍历能力被拆了出来,由Iterator负责遍历。

大家可能有疑问,可以用for循环解决的问题,为啥要搞得这么复杂呢?

其实主要看集合结构的复杂性,如果是普通的数组,可以不需要Iterator,直接使用for循环即可。如果是复杂的集合呢?对于这个集合需要有多种遍历方案呢?

如对于图结构,有广度优先、深度优先两种遍历方式,都在图集合里实现,是否感觉违背了职责单一原则。

所以对于复杂结构,迭代器有如下优势:

  1. 这种拆分,使集合和迭代器职责更加单一,符合单一职责原则

  2. 迭代器结构统一,方便使用,使用者无需知道遍历细节便可遍历集合

  3. 符合开闭原则,可以按照需求自己开发迭代器,无需改动集合类

  4. 符合里氏替换原则,可以方便的进行迭代方案的更换

通过UML可发现设计思路:迭代器中需要定义first()、isDone()、currentItem()、next() 四个最基本的方法。待遍历的集合对象通过依赖注入传递到迭代器类中。集合可通过CreateIterator() 方法来创建迭代器。

2.应用场景

迭代器模式一般在library中使用的比较多,毕竟library提供的大多是基础结构。实际业务场景中,很少需要自己编写迭代器。但代码还是要写的,这次写图集合与深度优先遍历迭代器,大家如果对其它类型的图迭代器感兴趣的话,可自行编写。

3.代码实现

对于二维数组,从[0,0]开始,以广度优先顺序遍历。如对于

1 2

3 4

广度优先遍历顺序为1 2 3 4。

package main

import (
	"fmt"
	"math/rand"
)

/**
 * @Author: Jason Pang
 * @Description: 二维数组,当做图集合,写个真正的图集合太麻烦了
 */
type TwoDimensionalArray struct {
	row   int64
	col   int64
	array [][]int64
}

/**
 * @Author: Jason Pang
 * @Description: 设置尺寸
 * @receiver t
 * @param row
 * @param col
 */
func (t *TwoDimensionalArray) SetSize(row int64, col int64) {
	t.row = row
	t.col = col
	t.array = make([][]int64, row)
	var i int64 = 0
	for i = 0; i < row; i++ {
		t.array[i] = make([]int64, col)
	}
}

/**
 * @Author: Jason Pang
 * @Description: 初始化数据
 * @receiver t
 */
func (t *TwoDimensionalArray) InitDefault() {
	if t.row <= 0 || t.col <= 0 {
		return
	}
	var i, j int64 = 0, 0
	for i = 0; i < t.row; i++ {
		for j = 0; j < t.col; j++ {
			t.array[i][j] = rand.Int63n(200)
		}
	}
}

/**
 * @Author: Jason Pang
 * @Description: 格式化输出。不能为指针。
 * @receiver t
 * @return string
 */
func (t TwoDimensionalArray) String() string {
	s := ""
	var i int64 = 0
	for i = 0; i < t.row; i++ {
		s += fmt.Sprintf("%v \n", t.array[i])
	}
	return s
}

/**
 * @Author: Jason Pang
 * @Description: 迭代器接口
 */
type Iterator interface {
	First()
	IsDone() bool
	CurrentItem()
	Next()
}

type BFSPos struct {
	x, y int64
}

/**
 * @Author: Jason Pang
 * @Description: 广度优先遍历
 */
type BFSIterator struct {
	data     *TwoDimensionalArray
	used     [][]bool
	queue    []BFSPos
	index    int64 //queue遍历的位置
	bfsIndex int64 //记录做广度优先遍历的位置
}

/**
 * @Author: Jason Pang
 * @Description: 赋值
 * @receiver d
 * @param data
 */
func (d *BFSIterator) Create(data *TwoDimensionalArray) {
	d.data = data
	var i int64 = 0
	d.used = make([][]bool, data.row)
	for i = 0; i < data.row; i++ {
		d.used[i] = make([]bool, data.col)
	}
	d.used[0][0] = true
	d.queue = make([]BFSPos, 1)
	d.queue[0] = BFSPos{0, 0}
	d.index = 0
	d.bfsIndex = 0
}

/**
 * @Author: Jason Pang
 * @Description: 初始数据
 * @receiver d
 */
func (d *BFSIterator) First() {
	fmt.Println(d.data.array[0][0])
}

/**
 * @Author: Jason Pang
 * @Description: 是否遍历结束
 * @receiver d
 * @return bool
 */
func (d *BFSIterator) IsDone() bool {
	if d.index == d.data.col*d.data.row {
		return true
	}
	return false
}

/**
 * @Author: Jason Pang
 * @Description: 当前数值
 * @receiver d
 */
func (d *BFSIterator) CurrentItem() {
	pos := d.queue[d.index]
	fmt.Println(d.index, ":", d.data.array[pos.x][pos.y])
}

/**
 * @Author: Jason Pang
 * @Description: 移动
 * @receiver d
 */
func (d *BFSIterator) Next() {
	if d.index >= d.data.row*d.data.col {
		fmt.Println("已到最后")
		return
	}
	//说明已经没有了,需要再加几个
	if d.index >= int64(len(d.queue))-1 {
		for d.bfsIndex < int64(len(d.queue)) && d.index < int64(len(d.queue)) {
			curI, curJ := d.queue[d.bfsIndex].x, d.queue[d.bfsIndex].y
			if curJ+1 < d.data.col && d.used[curI][curJ+1] == false {
				d.queue = append(d.queue, BFSPos{curI, curJ + 1})
				d.used[curI][curJ+1] = true
			}
			if curI+1 < d.data.row && curJ+1 < d.data.col && d.used[curI+1][curJ+1] == false {
				d.queue = append(d.queue, BFSPos{curI + 1, curJ + 1})
				d.used[curI+1][curJ+1] = true
			}
			if curI+1 < d.data.row && d.used[curI+1][curJ] == false {
				d.queue = append(d.queue, BFSPos{curI + 1, curJ})
				d.used[curI+1][curJ] = true
			}
			d.bfsIndex++
		}

	}
	d.index++
}

func main() {
	t := TwoDimensionalArray{}
	t.SetSize(3, 3)
	t.InitDefault()
	fmt.Printf("%s", t)

	iterator := BFSIterator{}
	iterator.Create(&t)
	for iterator.IsDone() != true {
		iterator.CurrentItem()
		iterator.Next()
	}
}

输出:

➜ myproject go run main.go

[10 151 21]

[51 137 120]

[158 148 16]

0 : 10

1 : 151

2 : 137

3 : 51

4 : 21

5 : 120

6 : 16

7 : 148

8 : 158

代码写的比较简单,但是仍然能看到迭代器模式的优点。

  1. 可以轻易的变更遍历算法

  2. TwoDimensionalArray可继承于接口,面向接口编程,扩展性会进一步增强

总结

迭代器模式可能对于大部分研发同学来说是不需要的,但对于搞基础框架、搞语言的同学来说应该经常会用。对于迭代器模式,并不是说学习怎么使用,更重要的一点是需要感知设计模式的思想内核。

最后

大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)

我的个人博客为:https://shidawuhen.github.io/

图片

往期文章回顾:

  1. 设计模式

  2. 招聘

  3. 思考

  4. 存储

  5. 算法系列

  6. 读书笔记

  7. 小工具

  8. 架构

  9. 网络

  10. Go语言

Go语言设计模式(0)前言:我回来了,我带着23种设计模式回来了 Go语言设计模式(0)前言:我回来了,我带着23种设计模式回来了 鸽了那么久,我终于回来了!我宣布从今天开始,高产的Martin Jiang又回来了,从今天开始我将努力做到日更,争取每天都能产出一篇高质量的笔记或者文章,直到我找到满意的工作为止。当然,找到工作并不意味着学习这个过程就结束了,只是找到工作之后的事情现在还未可知。 那么我要填的第一个坑就是鸽了大半年的Go语言实现23种设计模式。众所周知,懂得设计模式是每一个有志于从事服务端开发的萌新所必须掌握的知识;并且,设计模式在开发中也是很常用的(例如我之 阅读详情

相关推荐

Go语言中的迭代器方法

本文详细讲解了Go语言中的迭代器方法,并给出了相应的源代码示例。通过定义迭代器接口和实现迭代器的结构体,我们可以方便地遍历集合中的元素,提高代码的可读性和可维护性。通过定义迭代器接口和实现迭代器的结构体,我们可以灵活地处理不同类型的集合,并通过统一的方式进行遍历操作。假设我们有一个包含整数的切片,并且我们希望遍历该切片并打印每个元素的值。方法中,我们首先检查当前索引是否超出切片的长度,如果超出,则表示遍历结束,返回。否则,我们获取当前索引位置的元素值,并将索引递增,然后返回该值和。方法的接口来实现迭代器。

syntax_bug609的博客 291

golang中的迭代器和 iter 包

golang中的迭代器和 iter 包

猛犸象 2088

Go 设计模式中迭代器模式

迭代器模式的核心思想是将对集合的遍历操作封装到一个独立的迭代器对象中,该迭代器对象可以依次访问集合中的元素,而无需了解集合的具体实现方式。这样可以将集合对象的遍历算法与集合对象本身解耦,并且可以提供不同类型的迭代器来满足不同的遍历需求。迭代器模式可以定义多个不同类型的迭代器,每个迭代器可以实现不同的遍历算法,从而满足不同的遍历需求。迭代器模式(Iterator Pattern)是一种行为型设计模式,用于提供一种统一的方式来遍历集合对象中的元素,而无需暴露集合对象的内部结构。接口,返回具体的迭代器对象。

走向CTO的路上... 486

《golang设计模式》第三部分·行为型模式-04-迭代器模式(Iterator)

迭代器(Iterator)能够在不暴露聚合体内部表示的情况下,向客户端提供遍历聚合元素的方法。

架构+开发+运维 686

相衬成像介绍

相衬成像介绍

qq_41981768的博客 2913

Linux yum源配置以及yum命令讲解

前面为大家介绍了RPM二进制包安装软件,RPM包安装的最大的缺点也提到过,就是依赖性太强,一旦遇到依赖问题,则需要我们手动解决包之间具有依赖性的问题,今天为大家介绍一种可自动解决包之间依赖关系的安装方式:yum。 yum 全称“Yellow dog Updater, Modified”,是一个专门为了解决包的依赖关系而存在的软件包管理器。就好像 Windows 系统上我们通常可以通过各种软件管家实现软件的一键安装、升级和卸载,yum就是Linux 系统中的一键安装工具。yum 解决依赖关系的基本逻辑是:yu

九派Linux的博客 2283

【Go实现】实践GoF的23种设计模式:迭代器模式

迭代器模式主要用在访问对象集合的场景,能够向客户端隐藏集合的实现细节

ruanrunxue的博客 404

项目管理——14规划绩效域论文套路

规划绩效域论文套路

Flee Elf 1153

23种计模式之Python/Go实现

设计模式是面对各种问题进行提炼和抽象而形成的解决方案。这些设计方案是前人不断试验,考虑了封装性、复用性、效率、可修改、可移植等各种因素的高度总结。它不限于一种特定的语言,它是一种解决问题的思想和方法。由于高级语言的出现,让机器读懂你的意图已经不是最主要的“矛盾”,而让人读懂你的意图才是最重要。按照设计模式编写的代码,其可读性也会大大提升,利于团队项目的继承和扩展。设计模式可以分为三个大类:创建类设计模式、结构类设计模式、行为类设计模式下面用一个图片来整体描述一下设计模式之间的关系:单例模式、工厂模式(简单工

General_zy的博客 2871

CST新手必看:共面波导色散曲线绘制全流程(附参数设置详解)

本文为CST新手提供了绘制共面波导色散曲线的完整流程与参数设置详解。从模型搭建、周期性边界条件设置,到本征模求解器配置和参数扫描技巧,逐步解析关键步骤,并分享后处理与结果验证方法,帮助用户高效完成仿真任务。

j0k1l2m3n的博客 423

手把手教你学simulink(13.2)--构建一个单相并网逆变器Simulink仿真模型

基于Simulink的单相并网逆变器项目的详细仿真实例。这个项目将重点放在逆变器的详细建模、最大功率点跟踪(MPPT)控制策略、并网控制和仿真分析上。我们将使用MATLAB/Simulink来构建整个系统,并通过具体的代码和模块来实现各个功能。

MHD0815的博客 545

23种设计模式:迭代器模式

迭代器模式 1.介绍 概念 迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。 迭代器模式属于行为型模式。 主要作用 提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。 解决的问题 不同的方式来遍历整个整合对象。 使用场景 1、访问一个聚合对象的内容而无须暴露它的内部表示。 2、需要为聚合对象提供多种遍历方式。 3、为遍历不同的聚合结构提供一个统一的接口。 (引用

qq_49723651的博客 573

HFSS实战项目——高带外抑制特性SIW带通滤波器

本文对单级滤波器和双级滤波器的设计、工作原理及仿真结果进行了详细分析,并验证了双级SIW带通滤波器在性能方面的优越性,为微波通信系统中滤波器的设计和应用提供了重要参考。

2401_87464239的博客 1816

golang设计模式——迭代器模式

迭代器模式可能对于大部分研发同学来说是不需要的,但对于搞基础框架、搞语言的同学来说应该经常会用。对于迭代器模式,并不是说学习怎么使用,更重要的一点是需要感知设计模式的思想内核。

qq_53267860的博客 671

CANFD加速是什么?和CANFD有什么区别?

在使用CAN设备上位机的时候,已经选择了CANFD,但还有一个选项是“CANFD加速”,那CANFD加速和不加速有什么区别呢?刚接触CANFD小伙伴肯定一脸懵,本篇主要是给大家分享一下他们的区别,如果对你有帮助,请转发,收藏,点赞💐🎉👍😊

汽车电子软件领域知识分享 1035

Android功耗系列专题理论之十三:MTK平台待机功耗问题分析方法

摘要:本文详细分析了MTK平台待机功耗问题的调试方法,重点介绍了SPM电源管理机制、26M时钟控制逻辑以及系统深睡眠状态的判断标准。文章提供了完整的Debug流程,包括日志抓取、唤醒源分析、SCP睡眠状态检查以及时钟/LDO状态排查等实用技巧,并给出硬件漏电点和软件配置异常的排查方案。针对不同模块(Modem/Conn/SSPM等)提供了具体的电源管理绕过命令,适用于功耗问题调试和低功耗模式验证场景。

561

云原生系列Go语言篇-Go语言的23种设计模式

抽象工厂模式可通过接口可创建相关对象的工厂实现。

AlanHou的博客 592

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

实践GoF的设计模式:迭代器模式

迭代器模式主要用在访问对象集合的场景,能够向客户端隐藏集合的实现细节。

华为云官方博客 1108

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

GO 语言实现设计模式【全】

文章目录0.前言1.责任链模式概念示例登机过程测试程序运行结果2.命令模式概念示例电饭煲接收器电饭煲命令测试程序运行结果3.迭代器模式概念示例班级成员运行结果4.中介者模式概念示例飞机对象机场塔台测试程序运行结果5.备忘录模式概念示例闯关游戏测试程序运行结果6.观察者模式概念示例通知方式信用卡业务测试程序运行结果7.状态模式概念示例电池状态IPhone手机测试程序运行结果8.策略模式概念示例四季天气城市气候测试程序运行结果9.模板方法模式概念示例演员基类具体演员测试程序运行结果10.访问者模式概念示例员工.

Doublelv 的博客专栏。 3716

Go 23种设计模式

抽象工厂模式基于工厂方法模式。两者的区别在于:工厂方法模式是创建出一种产品,而抽象工厂模式是创建出一类产品。这二种都属于工厂模式,在设计上是相似的。假设,有一个存储工厂,提供redis和mysql两种存储数据的方式。如果使用工厂方法模式,我们就需要一个存储工厂,并提供SaveRedis方法和SaveMysql方法。如果此时业务还需要分成存储散文和古诗两种载体,这两种载体都可以进行redis和mysql存储。

快乐的搬砖的博客 2737

Golang设计模式——07迭代器模式

package Iterator type Iterator interface { Index() *int Value() interface{} HasNext() bool Next() } type ArrayIterator struct { array []interface{} index *int } func (a *ArrayIterator) Index() *int { return a.index } func (a *ArrayIterator) Value

清风 759

【转存】Go语言设计模式(推荐阅读)

Go语言设计与示例模式

艾利克斯冰的博客 2268

【Go实现】实践GoF的23种设计模式:访问者模式

访问者模式的目的是,解耦数据结构和算法,使得系统能够在不改变现有代码结构的基础上,为对象新增一种新的操作。

ruanrunxue的博客 639

go的设计模式

在2020年七月最后一个星期,我开始学习了go的设计模式 设计模式 是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结 分类 创建型( 单例模式、简单工厂模式、工厂方法模式、抽象厂模式、建造者模式、原型模式) 结构型(代理模式、适配器模式、装饰模式、桥接模式、组合模式、享元模式、外观模式) 行为型(观察者模式、模板方法模式、命令模式、状态模式、职责链模式、解释器模式、中介者模式、访问者模式、策略模式、备忘录模式、迭代器模式) 五大原则 1、开闭原则 开放扩展,关闭修改 开闭原则的主旨是为了

qq_44981657的博客 3488

设计模式-迭代器模式

设计模式-迭代器模式

qq_52785898的博客 238

Go设计模式终极指南:23种模式快速掌握

Go设计模式是解决常见软件设计问题的可复用方案,能够帮助开发者编写更优雅、可维护的Go代码。基于`bvwells/go-patterns`项目,本文为您提供完整的Go设计模式学习路径和实践指南。 ## 🧩 什么是Go设计模式? 设计模式就像是软件开发中的拼图块,每个模式都有其特定的用途和应用场景。当您将这些模式正确组合时,就能构建出健壮、可扩展的应用程序。 [![Go设计模式拼图](htt

gitblog_00728的博客 427
上一篇: 整数区间计算,我这么设计
下一篇: InnoDB redo、undo、binlog,是如何合作的
程序员麻辣烫
博客等级 码龄17年 2万+粉丝 351原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员麻辣烫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值