赞
踩
迭代器模式从来没有写过,第一次接触迭代器,还是好多年前学C++的STL的时候。当时觉得用迭代器太麻烦了,后来用习惯了觉得真香。
UML类图位置:https://www.processon.com/view/link/60d29bf3e401fd49502afd25
本文代码链接为:https://github.com/shidawuhen/asap/blob/master/controller/design/23iterator.go
迭代器模式(Iterator):提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。
UML:
通过UML可以看出,对于集合Aggregate,其遍历能力被拆了出来,由Iterator负责遍历。
大家可能有疑问,可以用for循环解决的问题,为啥要搞得这么复杂呢?
其实主要看集合结构的复杂性,如果是普通的数组,可以不需要Iterator,直接使用for循环即可。如果是复杂的集合呢?对于这个集合需要有多种遍历方案呢?
如对于图结构,有广度优先、深度优先两种遍历方式,都在图集合里实现,是否感觉违背了职责单一原则。
所以对于复杂结构,迭代器有如下优势:
这种拆分,使集合和迭代器职责更加单一,符合单一职责原则
迭代器结构统一,方便使用,使用者无需知道遍历细节便可遍历集合
符合开闭原则,可以按照需求自己开发迭代器,无需改动集合类
符合里氏替换原则,可以方便的进行迭代方案的更换
通过UML可发现设计思路:迭代器中需要定义first()、isDone()、currentItem()、next() 四个最基本的方法。待遍历的集合对象通过依赖注入传递到迭代器类中。集合可通过CreateIterator() 方法来创建迭代器。
迭代器模式一般在library中使用的比较多,毕竟library提供的大多是基础结构。实际业务场景中,很少需要自己编写迭代器。但代码还是要写的,这次写图集合与深度优先遍历迭代器,大家如果对其它类型的图迭代器感兴趣的话,可自行编写。
对于二维数组,从[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
代码写的比较简单,但是仍然能看到迭代器模式的优点。
可以轻易的变更遍历算法
TwoDimensionalArray可继承于接口,面向接口编程,扩展性会进一步增强
迭代器模式可能对于大部分研发同学来说是不需要的,但对于搞基础框架、搞语言的同学来说应该经常会用。对于迭代器模式,并不是说学习怎么使用,更重要的一点是需要感知设计模式的思想内核。
大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)
我的个人博客为:https://shidawuhen.github.io/
往期文章回顾:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。