结构图
角色
- 迭代器(Iterator)角色:负责定义和遍历元素的接口。
- 具体迭代器(Concrete Iterator)角色:实现迭代器接口,并要记录遍历中的当前位置。
- 容器(Container)角色:容器角色负责提供创建具体迭代器角色的接口。
- 具体容器(Concrete Container)角色:具体容器角色实现创建具体迭代器角色的接口——这个具体迭代器角色于该容器的结构相关。
动机
在软件构建过程中集合对象内部结构常常变化各异。但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部程序代码透明的访问其中包含的元素; 同时这种“透明遍历”也为“同一种算法在多种集合对象上进行操作” 提供了可能。
使用面向对象技术将这种遍历机制抽象抽象为“迭代器”为“应对变化中的集合对象”提供了一种优雅的方式。
意图
提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。
示意性代码
示意性代码
Module MainApp
Sub Main()
Dim a As New ConcreteAggregate
a(0) = "Item A"
a(1) = "Item B"
a(2) = "Item C"
a(3) = "Item D"
'Create Iterator and provide aggregate
Dim i As New ConcreteIterator(a)
Console.WriteLine("Iterating over collection -----")
Dim item As Object = i.First()
While item IsNot Nothing
Console.WriteLine(item)
item = i.Next()
End While
'Wait for user
Console.Read()
End Sub
End Module
'"Aggregate"
Public MustInherit Class Aggregate
Public MustOverride Function CreateIterator() As Iterator
End Class
'"ConcreteAggregate"
Public Class ConcreteAggregate
Inherits Aggregate
Private items As New ArrayList
Public Overrides Function CreateIterator() As Iterator
Return New ConcreteIterator(Me)
End Function
'Property
Public ReadOnly Property Count() As Integer
Get
Return items.Count
End Get
End Property
'Indexer
Default Public Property Item(ByVal Index As Integer) As Object
Get
Return items(Index)
End Get
Set(ByVal value As Object)
items.Insert(Index, value)
End Set
End Property
End Class
'"Iterator"
Public MustInherit Class Iterator
Public MustOverride Function First() As Object
Public MustOverride Function [Next]() As Object
Public MustOverride Function IsDone() As Boolean
Public MustOverride Function CurrentItem() As Object
End Class
'"ConcreteIterator"
Public Class ConcreteIterator
Inherits Iterator
Private aggregate As ConcreteAggregate
Private current As Integer = 0
'Constructor
Public Sub New(ByVal aggregate As ConcreteAggregate)
Me.aggregate = aggregate
End Sub
Public Overrides Function First() As Object
Return aggregate(0)
End Function
Public Overrides Function [Next]() As Object
Dim ret As Object = Nothing
If current < aggregate.Count - 1 Then
ret = aggregate(System.Threading.Interlocked.Increment(current))
End If
Return ret
End Function
Public Overrides Function CurrentItem() As Object
Return aggregate(current)
End Function
Public Overrides Function IsDone() As Boolean
Return current >= aggregate.Count
End Function
End Class
Module MainApp
Sub Main()
Dim a As New ConcreteAggregate
a(0) = "Item A"
a(1) = "Item B"
a(2) = "Item C"
a(3) = "Item D"
'Create Iterator and provide aggregate
Dim i As New ConcreteIterator(a)
Console.WriteLine("Iterating over collection -----")
Dim item As Object = i.First()
While item IsNot Nothing
Console.WriteLine(item)
item = i.Next()
End While
'Wait for user
Console.Read()
End Sub
End Module
'"Aggregate"
Public MustInherit Class Aggregate
Public MustOverride Function CreateIterator() As Iterator
End Class
'"ConcreteAggregate"
Public Class ConcreteAggregate
Inherits Aggregate
Private items As New ArrayList
Public Overrides Function CreateIterator() As Iterator
Return New ConcreteIterator(Me)
End Function
'Property
Public ReadOnly Property Count() As Integer
Get
Return items.Count
End Get
End Property
'Indexer
Default Public Property Item(ByVal Index As Integer) As Object
Get
Return items(Index)
End Get
Set(ByVal value As Object)
items.Insert(Index, value)
End Set
End Property
End Class
'"Iterator"
Public MustInherit Class Iterator
Public MustOverride Function First() As Object
Public MustOverride Function [Next]() As Object
Public MustOverride Function IsDone() As Boolean
Public MustOverride Function CurrentItem() As Object
End Class
'"ConcreteIterator"
Public Class ConcreteIterator
Inherits Iterator
Private aggregate As ConcreteAggregate
Private current As Integer = 0
'Constructor
Public Sub New(ByVal aggregate As ConcreteAggregate)
Me.aggregate = aggregate
End Sub
Public Overrides Function First() As Object
Return aggregate(0)
End Function
Public Overrides Function [Next]() As Object
Dim ret As Object = Nothing
If current < aggregate.Count - 1 Then
ret = aggregate(System.Threading.Interlocked.Increment(current))
End If
Return ret
End Function
Public Overrides Function CurrentItem() As Object
Return aggregate(current)
End Function
Public Overrides Function IsDone() As Boolean
Return current >= aggregate.Count
End Function
End Class
一个实例
下面的迭代器代码用来演示跳过某些项目遍历一个集合。
实例代码
' MainApp test application
Module MainApp
Sub Main()
' Build a collection
Dim collection As New Collection()
collection(0) = New Item("Item 0")
collection(1) = New Item("Item 1")
collection(2) = New Item("Item 2")
collection(3) = New Item("Item 3")
collection(4) = New Item("Item 4")
collection(5) = New Item("Item 5")
collection(6) = New Item("Item 6")
collection(7) = New Item("Item 7")
collection(8) = New Item("Item 8")
' Create iterator
Dim iterator As New Iterator(collection)
' Skip every other item
iterator.[Step] = 2
Console.WriteLine("Iterating over collection:")
Dim item As Item = iterator.First()
While Not iterator.IsDone
Console.WriteLine(item.Name)
item = iterator.[Next]()
End While
' Wait for user
Console.Read()
End Sub
End Module
Public Class Item
Private m_name As String
' Constructor
Public Sub New(ByVal name As String)
Me.m_name = name
End Sub
' Property
Public ReadOnly Property Name() As String
Get
Return m_name
End Get
End Property
End Class
' "Aggregate"
Public Interface IAbstractCollection
Function CreateIterator() As Iterator
End Interface
' "ConcreteAggregate"
Public Class Collection
Implements IAbstractCollection
Private items As New ArrayList()
Public Function CreateIterator() As Iterator Implements IAbstractCollection.CreateIterator
Return New Iterator(Me)
End Function
' Property
Public ReadOnly Property Count() As Integer
Get
Return items.Count
End Get
End Property
' Indexer
Default Public Property Item(ByVal index As Integer) As Object
Get
Return items(index)
End Get
Set(ByVal value As Object)
items.Add(value)
End Set
End Property
End Class
' "Iterator"
Public Interface IAbstractIterator
Function First() As Item
Function [Next]() As Item
ReadOnly Property IsDone() As Boolean
ReadOnly Property CurrentItem() As Item
End Interface
' "ConcreteIterator"
Public Class Iterator
Implements IAbstractIterator
Private collection As Collection
Private current As Integer = 0
Private m_step As Integer = 1
' Constructor
Public Sub New(ByVal collection As Collection)
Me.collection = collection
End Sub
Public Function First() As Item Implements IAbstractIterator.First
current = 0
Return TryCast(collection(current), Item)
End Function
Public Function [Next]() As Item Implements IAbstractIterator.[Next]
current += m_step
If Not IsDone Then
Return TryCast(collection(current), Item)
Else
Return Nothing
End If
End Function
' Properties
Public Property [Step]() As Integer
Get
Return m_step
End Get
Set(ByVal value As Integer)
m_step = value
End Set
End Property
Public ReadOnly Property CurrentItem() As Item Implements IAbstractIterator.CurrentItem
Get
Return TryCast(collection(current), Item)
End Get
End Property
Public ReadOnly Property IsDone() As Boolean Implements IAbstractIterator.IsDone
Get
Return current >= collection.Count
End Get
End Property
End Class
' MainApp test application
Module MainApp
Sub Main()
' Build a collection
Dim collection As New Collection()
collection(0) = New Item("Item 0")
collection(1) = New Item("Item 1")
collection(2) = New Item("Item 2")
collection(3) = New Item("Item 3")
collection(4) = New Item("Item 4")
collection(5) = New Item("Item 5")
collection(6) = New Item("Item 6")
collection(7) = New Item("Item 7")
collection(8) = New Item("Item 8")
' Create iterator
Dim iterator As New Iterator(collection)
' Skip every other item
iterator.[Step] = 2
Console.WriteLine("Iterating over collection:")
Dim item As Item = iterator.First()
While Not iterator.IsDone
Console.WriteLine(item.Name)
item = iterator.[Next]()
End While
' Wait for user
Console.Read()
End Sub
End Module
Public Class Item
Private m_name As String
' Constructor
Public Sub New(ByVal name As String)
Me.m_name = name
End Sub
' Property
Public ReadOnly Property Name() As String
Get
Return m_name
End Get
End Property
End Class
' "Aggregate"
Public Interface IAbstractCollection
Function CreateIterator() As Iterator
End Interface
' "ConcreteAggregate"
Public Class Collection
Implements IAbstractCollection
Private items As New ArrayList()
Public Function CreateIterator() As Iterator Implements IAbstractCollection.CreateIterator
Return New Iterator(Me)
End Function
' Property
Public ReadOnly Property Count() As Integer
Get
Return items.Count
End Get
End Property
' Indexer
Default Public Property Item(ByVal index As Integer) As Object
Get
Return items(index)
End Get
Set(ByVal value As Object)
items.Add(value)
End Set
End Property
End Class
' "Iterator"
Public Interface IAbstractIterator
Function First() As Item
Function [Next]() As Item
ReadOnly Property IsDone() As Boolean
ReadOnly Property CurrentItem() As Item
End Interface
' "ConcreteIterator"
Public Class Iterator
Implements IAbstractIterator
Private collection As Collection
Private current As Integer = 0
Private m_step As Integer = 1
' Constructor
Public Sub New(ByVal collection As Collection)
Me.collection = collection
End Sub
Public Function First() As Item Implements IAbstractIterator.First
current = 0
Return TryCast(collection(current), Item)
End Function
Public Function [Next]() As Item Implements IAbstractIterator.[Next]
current += m_step
If Not IsDone Then
Return TryCast(collection(current), Item)
Else
Return Nothing
End If
End Function
' Properties
Public Property [Step]() As Integer
Get
Return m_step
End Get
Set(ByVal value As Integer)
m_step = value
End Set
End Property
Public ReadOnly Property CurrentItem() As Item Implements IAbstractIterator.CurrentItem
Get
Return TryCast(collection(current), Item)
End Get
End Property
Public ReadOnly Property IsDone() As Boolean Implements IAbstractIterator.IsDone
Get
Return current >= collection.Count
End Get
End Property
End Class
Iterator Pattern模式的几个要点:
1、迭代抽象:访问一个聚合对象的内容而无需暴露它的内部信息。
2、迭代多态:为遍历不同的集合结构提供一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作。
3、迭代器健壮性考虑:遍历的同时更改迭代器所在的集合结构,会导致问题。
我的理解
封装集合对象的内部结构和遍历集合的算法,支持集合和算法的变化。
参考资料
《C#面向对象设计模式纵横谈系列课程(18)》 李建中老师