当前位置:   article > 正文

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

go 迭代器

迭代器模式从来没有写过,第一次接触迭代器,还是好多年前学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。

  1. package main
  2. import (
  3. "fmt"
  4. "math/rand"
  5. )
  6. /**
  7. * @Author: Jason Pang
  8. * @Description: 二维数组,当做图集合,写个真正的图集合太麻烦了
  9. */
  10. type TwoDimensionalArray struct {
  11. row int64
  12. col int64
  13. array [][]int64
  14. }
  15. /**
  16. * @Author: Jason Pang
  17. * @Description: 设置尺寸
  18. * @receiver t
  19. * @param row
  20. * @param col
  21. */
  22. func (t *TwoDimensionalArray) SetSize(row int64, col int64) {
  23. t.row = row
  24. t.col = col
  25. t.array = make([][]int64, row)
  26. var i int64 = 0
  27. for i = 0; i < row; i++ {
  28. t.array[i] = make([]int64, col)
  29. }
  30. }
  31. /**
  32. * @Author: Jason Pang
  33. * @Description: 初始化数据
  34. * @receiver t
  35. */
  36. func (t *TwoDimensionalArray) InitDefault() {
  37. if t.row <= 0 || t.col <= 0 {
  38. return
  39. }
  40. var i, j int64 = 0, 0
  41. for i = 0; i < t.row; i++ {
  42. for j = 0; j < t.col; j++ {
  43. t.array[i][j] = rand.Int63n(200)
  44. }
  45. }
  46. }
  47. /**
  48. * @Author: Jason Pang
  49. * @Description: 格式化输出。不能为指针。
  50. * @receiver t
  51. * @return string
  52. */
  53. func (t TwoDimensionalArray) String() string {
  54. s := ""
  55. var i int64 = 0
  56. for i = 0; i < t.row; i++ {
  57. s += fmt.Sprintf("%v \n", t.array[i])
  58. }
  59. return s
  60. }
  61. /**
  62. * @Author: Jason Pang
  63. * @Description: 迭代器接口
  64. */
  65. type Iterator interface {
  66. First()
  67. IsDone() bool
  68. CurrentItem()
  69. Next()
  70. }
  71. type BFSPos struct {
  72. x, y int64
  73. }
  74. /**
  75. * @Author: Jason Pang
  76. * @Description: 广度优先遍历
  77. */
  78. type BFSIterator struct {
  79. data *TwoDimensionalArray
  80. used [][]bool
  81. queue []BFSPos
  82. index int64 //queue遍历的位置
  83. bfsIndex int64 //记录做广度优先遍历的位置
  84. }
  85. /**
  86. * @Author: Jason Pang
  87. * @Description: 赋值
  88. * @receiver d
  89. * @param data
  90. */
  91. func (d *BFSIterator) Create(data *TwoDimensionalArray) {
  92. d.data = data
  93. var i int64 = 0
  94. d.used = make([][]bool, data.row)
  95. for i = 0; i < data.row; i++ {
  96. d.used[i] = make([]bool, data.col)
  97. }
  98. d.used[0][0] = true
  99. d.queue = make([]BFSPos, 1)
  100. d.queue[0] = BFSPos{0, 0}
  101. d.index = 0
  102. d.bfsIndex = 0
  103. }
  104. /**
  105. * @Author: Jason Pang
  106. * @Description: 初始数据
  107. * @receiver d
  108. */
  109. func (d *BFSIterator) First() {
  110. fmt.Println(d.data.array[0][0])
  111. }
  112. /**
  113. * @Author: Jason Pang
  114. * @Description: 是否遍历结束
  115. * @receiver d
  116. * @return bool
  117. */
  118. func (d *BFSIterator) IsDone() bool {
  119. if d.index == d.data.col*d.data.row {
  120. return true
  121. }
  122. return false
  123. }
  124. /**
  125. * @Author: Jason Pang
  126. * @Description: 当前数值
  127. * @receiver d
  128. */
  129. func (d *BFSIterator) CurrentItem() {
  130. pos := d.queue[d.index]
  131. fmt.Println(d.index, ":", d.data.array[pos.x][pos.y])
  132. }
  133. /**
  134. * @Author: Jason Pang
  135. * @Description: 移动
  136. * @receiver d
  137. */
  138. func (d *BFSIterator) Next() {
  139. if d.index >= d.data.row*d.data.col {
  140. fmt.Println("已到最后")
  141. return
  142. }
  143. //说明已经没有了,需要再加几个
  144. if d.index >= int64(len(d.queue))-1 {
  145. for d.bfsIndex < int64(len(d.queue)) && d.index < int64(len(d.queue)) {
  146. curI, curJ := d.queue[d.bfsIndex].x, d.queue[d.bfsIndex].y
  147. if curJ+1 < d.data.col && d.used[curI][curJ+1] == false {
  148. d.queue = append(d.queue, BFSPos{curI, curJ + 1})
  149. d.used[curI][curJ+1] = true
  150. }
  151. if curI+1 < d.data.row && curJ+1 < d.data.col && d.used[curI+1][curJ+1] == false {
  152. d.queue = append(d.queue, BFSPos{curI + 1, curJ + 1})
  153. d.used[curI+1][curJ+1] = true
  154. }
  155. if curI+1 < d.data.row && d.used[curI+1][curJ] == false {
  156. d.queue = append(d.queue, BFSPos{curI + 1, curJ})
  157. d.used[curI+1][curJ] = true
  158. }
  159. d.bfsIndex++
  160. }
  161. }
  162. d.index++
  163. }
  164. func main() {
  165. t := TwoDimensionalArray{}
  166. t.SetSize(3, 3)
  167. t.InitDefault()
  168. fmt.Printf("%s", t)
  169. iterator := BFSIterator{}
  170. iterator.Create(&t)
  171. for iterator.IsDone() != true {
  172. iterator.CurrentItem()
  173. iterator.Next()
  174. }
  175. }

输出:

➜ 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语言

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/507443
推荐阅读
相关标签
  

闽ICP备14008679号