在本主题中,继续讨论在上面的“公有集合示例:稻草盖的房子”中开始探讨的代码示例。在开始本主题之前,最好阅读上面的主题。
将 Employee 对象和 SmallBusiness 对象连接起来的一种更强健一些的办法是使 Collection 对象私有化。对于该示例来说,将要从上面的“公有集合”示例中重用窗体和大部分代码。
Employee 类模块没有改变。然而,SmallBusiness 类模块被完全翻新了。用下面的声明语句来取代公有的 Collection 对象的声明,并把下面几个段落中所描述的 Sub 和 Function 过程添加进去。
Option Explicit
Private mcolEmployees As New Collection
跟前面的一样,添加一个雇员的代码做大部分工作。(可以从前面示例中的 cmdEmployeeAdd_Click 事件过程中把点线之间的代码块取出来。)
重要的改动在于:Collection 对象的 Add 方法不能再从程序的任意模块来调用了,因为 mcolEmployees
是 Private。只能用 EmployeeAdd 方法来添加 Employee 对象,这将会使新对象正确地初始化:
'SmallBusiness
的类方法。Public Function EmployeeAdd(ByVal Name As String, _
ByVal Salary As Double) As Employee
' - - - - - - - - - - - - - - - -
Dim empNew As New Employee
Static intEmpNum As Integer
'
用With
语句,使代码更快,也更简洁'
(.ID
对应empNew.ID)
。With empNew
'
为新雇员产生一个唯一的ID
。intEmpNum = intEmpNum + 1
.ID = "E" & Format$(intEmpNum, "00000")
.Name = Name
.Salary = Salary
'
将Employee
对象引用添加到集合中,'
用ID
属性作为键。' - - - - - - - - - - - - - - - -
mcolEmployees.Add empNew, .ID
End With
'
给新雇员返回一个引用。Set EmployeeAdd = empNew
End Function
EmployeeAdd 方法为新添加的 Employee 对象返回一个引用。这是一个好的实践,因为一旦创建了一个对象,很可能就想用它来做点什么事。
EmployeeCount、EmployeeDelete 以及 Employees 方法委派 Collection 对象的相应方法为代表。委派意味着 Collection 对象做全部工作。
'SmallBusiness
类的方法。Public Function EmployeeCount() As Long
EmployeeCount = mcolEmployees.Count
End Function
Public Sub EmployeeDelete(ByVal Index As Variant)
mcolEmployees.Remove Index
End Sub
Public Function Employees(ByVal Index As Variant) _
As Employee
Set Employees = mcolEmployees.Item(Index)
End Function
注意 可以将附加的功能添加到这些方法中。例如,如果一条索引项是无效的,能够引发自己的错误。
最后的方法是 Trouble。该方法试图将未初始化的 Employee 对象添加到集合中。能猜测到将会发生什么事吗?
'SmallBusiness
类的方法。Public Sub Trouble()
Dim x As New Employee
mcolEmployees.Add x
End Sub
现在,将不得不对窗体模块作一些改动。可以使用前面示例所使用的同样的模块级声明,而且“关闭”按钮的 Click 事件也是相同的,但是,其它事件过程已经改变了—“添加”按钮的代码短了许多,而“删除”和“列出雇员的表”按钮的代码则已经改变为一种很小但却很有效的方式:
Private Sub cmdEmployeeAdd_Click() sbMain.EmployeeAdd txtName.Text, txtSalary.Text txtName.Text = "" txtSalary.Text = "" cmdListEmployees.Value = True End Sub Private Sub cmdEmployeeDelete_Click() '
检查一下,以确保有雇员被选中。If lstEmployees.ListIndex > -1 Then
'
前六个字符是ID
。sbMain.EmployeeDelete Left(lstEmployees.Text, 6)
End If
cmdListEmployees.Value = True
End Sub
Private Sub cmdListEmployees_Click()
Dim lngCt As Long
lstEmployees.Clear
For lngCt = 1 To sbMain.EmployeeCount
With sbMain.Employees(lngCt)
lstEmployees.AddItem .ID & ", " & .Name _
& ", " & .Salary
End With
Next
End Sub
但是,在 cmdListEmployees_Click 中的所有这些附加代码是什么?不幸的是,在追求强健性的过程中,就已经放弃了使用 For Each ... Next 来遍历集合中各项的能力,因为现在 Collection 对象已经声明为 Private。如果试图象下面这样编写代码,那么将会产生错误:
'
不会正常工作,因为Employees
不是一个真正的集合。For Each emp In sbMain.Employees
幸运的是,可以用 EmployeeCount 方法来为遍历范围分界。
“有问题”按钮也作了少许更改,但它仍然是“有问题”。
Private Sub cmdTrouble_Click()
sbMain.Trouble
End Sub
运行该工程,并用“添加”、“删除”以及“刷新表”按钮作一下试验。每件事情工作起来,都跟以前是一样的。
当单击“有问题”按钮时,还是不产生任何错误。但是,如果现在单击“刷新表”按钮的话,就可以看到未初始化的 Employee 对象已经以某种方式被添加到集合中了。
怎么会发生这种情况?使 Collection 对象私有化以后,就保护它不受程序中所有在 SmallBusiness 对象外部
的代码的影响了,但并没有防范内部
代码的影响。在这种情况下,SmallBusiness 对象就可能由于内部有大量代码而变得很大,而且很复杂。例如,它将很可能具有象 CustomerAdd、ProductAdd,等等这些方法。
代码错误,或者 EmployeeAdd 方法的重复创建,仍然可能造成错误数据—甚至无效的对象—被插入到集合中,因为私有的变量在整个类模块中都是可见的。
详细信息 该示例在“创建自己的集合类:砖块盖的房子”中将继续讨论。