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