行为型 - 6. 迭代器模式

53 阅读2分钟

1. 迭代器模式的原理和代码实现

代器模式(Iterator Design Pattern),也叫作游标模式(Cursor Design Pattern)。迭代器模式将集合对象的遍历操作从集合类中拆分出来,放到迭代器类中,让两者的职责更加单一

迭代器的设计思路:

  • 迭代器中需要定义 hasNext()、currentItem()、next() 三个最基本的方法。
  • 待遍历的容器对象通过依赖注入传递到迭代器类中。
  • 容器通过 iterator() 方法来创建迭代器。

2. 迭代器模式的实现

type List interface {
   Iterator() Iterator
   Length() int
   GetItem(index int) int
}

type ArrayList struct {
   nums []int
}

func NewArrayList(vals ...int) *ArrayList {
   nums := make([]int, len(vals))
   for i, num := range vals {
      nums[i] = num
   }
   return &ArrayList{nums: nums}
}

func (a *ArrayList) Iterator() Iterator {
   return NewArrayListIterator(a)
}

func (a *ArrayList) Length() int {
   return len(a.nums)
}

func (a *ArrayList) GetItem(index int) int {
   return a.nums[index]
}


type Iterator interface {
   HasNext() bool
   Next()
   CurrentItem() int
}

type ArrayListIterator struct {
   cursor    int
   arrayList *ArrayList
}

func NewArrayListIterator(list *ArrayList) *ArrayListIterator {
   return &ArrayListIterator{
      cursor:    0,
      arrayList: list,
   }
}

func (a *ArrayListIterator) HasNext() bool {
   return a.cursor != a.arrayList.Length()
}

func (a *ArrayListIterator) Next() {
   a.cursor++
}

func (a *ArrayListIterator) CurrentItem() int {
   return a.arrayList.GetItem(a.cursor)
}

// 客户端代码实现
func TestList(t *testing.T) {
   list := NewArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
   iterator := list.Iterator()
   for iterator.HasNext() {
      t.Log(iterator.CurrentItem())
      iterator.Next()
   }
}

3. 迭代器模式的优势

一般来说,Golang 遍历集合数据的方式有 2 种:for 循环和 range 方式,后者底层就是使用迭代器模式实现的。

  • 对于复杂的数据结构(比如树、图)来说,有各种复杂的遍历方式。如果由客户端代码来实现这些遍历算法,势必增加开发成本,而且容易写错。如果将这部分遍历的逻辑写到容器类中,也会导致容器类代码的复杂性。应对复杂性的办法就是拆分,将遍历操作拆分到迭代器类。
  • 将游标指向的当前位置等信息,存储在迭代器类中,每个迭代器独享游标信息。这样,就可以创建多个不同的迭代器,同时对同一个容器进行遍历而互不影响。
  • 容器和迭代器都提供了抽象的接口,方便在开发的时候,基于接口而非具体的实现编程。需要切换新的遍历算法的时候,只需要切换一个新的迭代器类即可,其他代码都不需要修改。除此之外,添加新的遍历算法,只需要扩展新的迭代器类,也更符合开闭原则。