This is the third day of my participation in the August More Text Challenge

The iterator pattern has never been written. I first encountered iterators many years ago when I learned STL in C++. At that time I felt that iterator was too much trouble, but later I got used to it and felt really sweet.

UML class diagrams location: www.processon.com/view/link/6…

This article is linked to: github.com/shidawuhen/…

Definition 1.

1.1 Iterator pattern

Iterator pattern: Provides a way to access the elements of an aggregate object sequentially without exposing the internal representation of the object.

UML:

1.2 analysis

As can be seen from UML, for Aggregate, its traversal capability is removed and Iterator is responsible for traversal.

Now, you might be wondering, why make things so complicated when you can solve them with a for loop?

If it is a normal array, you can use the for loop without Iterator. What if it’s a complex set? What if there are multiple traversal schemes for this set?

For example, for graph structure, there are two traversal modes, breadth first and depth first, which are both realized in graph set, whether it violates the principle of single responsibility?

So for complex structures, iterators have the following advantages:

  1. This split makes the collection and iterator responsibilities more single, in line with the single responsibility principle

  2. Iterator structure is uniform, easy to use, users do not need to know the traversal details can be traversal collection

  3. Following the open close principle, you can develop your own iterators as required without changing the collection class

  4. In line with Richter’s substitution principle, the iterative scheme can be easily replaced

Design ideas can be discovered through UML: Four basic methods need to be defined in iterators: first(), isDone(), currentItem(), and next(). The collection object to be traversed is passed to the iterator class through dependency injection. Iterators can be created using the CreateIterator() method.

2. Application scenarios

The iterator pattern is often used in libraries, which provide much of the infrastructure. In real business scenarios, you rarely need to write your own iterators. But the code still needs to be written, this time to write graph sets and depth-first traversal iterators, if you are interested in other types of graph iterators, you can write your own.

3. Code implementation

For a two-dimensional array, starting with [0,0], we traverse in breadth-first order. As for the

1 2

3, 4,

Breadth-first traversal is 1, 2, 3, and 4.

package main
import (
	"fmt"
	"math/rand"
)

/** * @author: Jason Pang * @description: an array with two dimensions */
type TwoDimensionalArray struct {
	row   int64
	col   int64
	array [][]int64
}

/** * @author: Jason Pang * @description: Set size * @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: Initializes data * @receiver t */
func (t *TwoDimensionalArray) InitDefault(a) {
	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: Formats output. Cannot be a pointer. * @receiver t * @return string */
func (t TwoDimensionalArray) String(a) 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: iterator interface */
type Iterator interface {
	First()
	IsDone() bool
	CurrentItem()
	Next()
}

type Pos struct {
	x, y int64
}

/** * @author: Jason Pang * @description: Breadth-first traversal */
type BFSIterator struct {
	data     *TwoDimensionalArray
	used     [][]bool
	queue    []Pos
	index    int64 //queue traversal position
	bfsIndex int64 // Record the position of breadth-first traversal
}

/** * @author: Jason Pang * @description: Assignment * @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([]Pos, 1)
	d.queue[0] = Pos{0.0}
	d.index = 0
	d.bfsIndex = 0
}

/** * @author: Jason Pang * @description: Initial data * @receiver d */
func (d *BFSIterator) First(a) {
	fmt.Println(d.data.array[0] [0])}/** * @author: Jason Pang * @description: Whether traversal ends * @receiver d * @return bool */
func (d *BFSIterator) IsDone(a) bool {
	if d.index == d.data.col*d.data.row {
		return true
	}
	return false
}

/** * @author: Jason Pang * @description: Current value * @receiver d */
func (d *BFSIterator) CurrentItem(a) {
	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(a) {
	if d.index >= d.data.row*d.data.col {
		fmt.Println("To the end.")
		return
	}
	// There are no more, we need to add more
	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, Pos{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, Pos{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, Pos{curI + 1, curJ})
				d.used[curI+1][curJ] = true
			}
			d.bfsIndex++
		}

	}
	d.index++
}

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

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

Copy the code

Output:

➜ myproject go run main.go

[151] 21

[51, 137, 120]

[16, 158, 148]

Zero: 10

1:151

2:137

3:51

4:21

5:120

6:16

7:148

8:158

The code is relatively simple, but you can still see the benefits of the iterator pattern.

  1. You can easily change the traversal algorithm

  2. TwoDimensionalArray can be inherited from the interface and programmed for the interface, with further extensibility

conclusion

The iterator mode may not be necessary for most r & D students, but it should be used frequently for students engaged in gay foundation and language. With the iterator pattern, it’s not about learning how to use it, it’s about feeling the core of the design pattern.

The last

If you like my article, you can follow my public account (Programmer Malatang)

My personal blog is shidawuhen.github. IO /

Review of previous articles:

  1. Design patterns

  2. recruitment

  3. thinking

  4. storage

  5. The algorithm series

  6. Reading notes

  7. Small tools

  8. architecture

  9. network

  10. The Go