面向对象编程经常会用到作为容器的对象,使用LotusScript时因为基本上是和单一的数据对象NotesDocument打交道,用作容器的就是数据库的视图或包含搜索结果的文档集合。但有时也需要某个通用容器来容纳其他自定义的对象。此时一般可考虑用数组,或者像20. 面向对象的LotusScript(三)之NArray介绍的编写一个基于动态数组的容器类。本文给出另一种容器的实现方式,对外的接口也像NArray一样是一个Collection,内部实现则不依赖于数组,而采用链接的节点。由于LotusScript的数组在包含数组时有层次上的限制,所以本容器类在理论上适用范围更广(虽然极少遇到这样极端的情况,基本上还是作为一个普通的容器类使用)。
'先定义一个用作节点的类
Class LinkedNode
Public PreviousNode As LinkedNode
Public NextNode As LinkedNode
Public Value As Variant
'prevNode As LinkedNode, nextNode As LinkedNode
Sub New(value As Variant)
Call SetValue(me.Value, value)
End Sub
Sub Append(nnode As LinkedNode)
Set me.NextNode=nnode
Set nnode.PreviousNode=Me
End Sub
Function IsEqualTo(node As LinkedNode) As Boolean
If node Is Me Then
IsEqualTo=True
Exit Function
End If
If Equals(node.Value, me.Value) Then
IsEqualTo=True
Else
IsEqualTo=False
End If
End Function
End Class
'接下来是链接集合类
Class LinkedCollection
Private msize As Integer
Private anchor As LinkedNode
Private current As LinkedNode
'Public Parent As LinkedCollection
Sub New()
Set anchor=New LinkedNode(Null)
Call anchor.Append(anchor)
Call Me.Reset()
'Set anchor.PreviousNode=anchor
'Set anchor.NextNode=anchor
End Sub
Property Get Size As Integer
Size=msize
End Property
Function FirstNode() As LinkedNode
Dim node As LinkedNode
Set node=anchor.NextNode
If Not anchor.NextNode Is anchor Then
Set FirstNode=anchor.NextNode
End If
End Function
Function LastNode() As LinkedNode
If Not anchor.PreviousNode Is anchor Then
Set LastNode = anchor.PreviousNode
End If
End Function
Sub Reset()
Set current=anchor
End Sub
Function HasNext() As Boolean
HasNext=Not current.NextNode Is anchor
End Function
Function Next() As LinkedNode
Set current=current.NextNode
If current Is anchor Then
Set Me.Next=Nothing
'Error 4000, "LinkedList: no such element."
End If
Set Me.Next=current
End Function
Function Add(value As Variant)
Dim node As New LinkedNode(value)
Call anchor.PreviousNode.Append(node)
Call node.Append(anchor)
msize=msize+1
End Function
Function Contains(value As Variant) As Boolean
If Find(value) Is Nothing Then
Contains=False
Else
Contains=True
End If
End Function
Function ContainsNode(node As LinkedNode) As Boolean
Call Me.Reset()
While HasNext()
If Me.Next() Is node Then
ContainsNode=True
End If
Wend
ContainsNode=False
End Function
Function Remove(value As Variant) As Boolean
Dim node As LinkedNode
Set node=Find(value)
If node Is Nothing Then
Me.Remove=False
Else
Call DirectRemoveNode(node)
Me.Remove=True
End If
End Function
Function RemoveCurrent() As Boolean
If current Is anchor Then
RemoveCurrent=False
Else
Call DirectRemoveNode(current)
Set current=current.PreviousNode
End If
End Function
Private Sub DirectRemoveNode(node As LinkedNode)
Call node.PreviousNode.Append(node.NextNode)
msize=msize-1
End Sub
Function Find(value As Variant) As LinkedNode
Dim node As LinkedNode
Call Me.Reset()
While HasNext()
Set node=Me.Next()
If Equals(value, node.Value) Then
Set Find=node
Exit Function
End If
Wend
End Function
Function Clone() As LinkedCollection
Dim col As New LinkedCollection
'Dim node As LinkedNode
Call Me.Reset()
While HasNext()
Call col.Add(Me.Next().Value)
Wend
'Set col.Parent=me.Parent
Set Clone=col
End Function
Function AddArray(array As Variant)
ForAll e In array
Add(e)
End ForAll
End Function
'The node value of two LinkedCollection instances must not point to their
'ancestors of LinkedCollection in the same hierarchy level. Or an infinite loop
'will occur during comparing these two instances.
Function IsEqualTo(lc As Variant) As Boolean
If Not InstanceOf(lc, TypeName(Me)) Then
IsEqualTo=False
Exit Function
End If
If lc Is Me Then
IsEqualTo=True
Exit Function
End If
If lc.Size >< me.Size Then
IsEqualTo=False
Exit Function
End If
Dim c1 As LinkedCollection, c2 As LinkedCollection
Set c1=Me.Clone()
Set c2=lc.Clone()
Call c1.Reset()
Call c2.Reset()
While c1.HasNext()
If c2.Remove(c1.Next().Value) Then
Call c1.RemoveCurrent()
Else
IsEqualTo=False
Exit Function
End If
Wend
IsEqualTo=True
End Function
End Class
该集合类的方法从名称上都可以看出含义,包括添加单个值、添加数组、克隆、判断是否包含某值、查找包含某值的节点、返回首个节点、判断是否还有下个节点、用于比较等值的IsEqualTo、返回末个节点、返回下个节点、删除包含某值的节点、删除当前节点、重置内部指针以及返回集合包含节点的数量。
其中Reset、HasNext和Next三个方法合起来用于遍历集合内所有的节点:
Call col.Reset()
If col.HasNext() Then
Set node=col.Next()
value=node.Value '或Set value=node.Value
End If
注意LinkedCollection和LinkedNode类都有一个IsEqualTo方法,可以被87. 再谈变体型Variant里介绍的计算相等性的Equals函数调用。
另外在LinkedCollection的链接实现方式上,采用了一个特殊的初始节点作为“锚”,使得对于从零到任意多个节点,在返回首位节点、添加节点、判断是否有下个节点等操作时,都有一致简单的逻辑。