在本主题中,继续讨论在上面的“公有集合示例:稻草盖的房子”中开始探讨的代码示例。在开始本主题之前,最好阅读上面的主题。
将 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 EmployeeStatic 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, .IDEnd With'给新雇员返回一个引用。Set EmployeeAdd = empNewEnd Function
EmployeeAdd 方法为新添加的 Employee 对象返回一个引用。这是一个好的实践,因为一旦创建了一个对象,很可能就想用它来做点什么事。
EmployeeCount、EmployeeDelete 以及 Employees 方法委派 Collection 对象的相应方法为代表。委派意味着 Collection 对象做全部工作。
'SmallBusiness类的方法。Public Function EmployeeCount() As LongEmployeeCount = mcolEmployees.CountEnd FunctionPublic Sub EmployeeDelete(ByVal Index As Variant)mcolEmployees.Remove IndexEnd SubPublic Function Employees(ByVal Index As Variant) _As EmployeeSet Employees = mcolEmployees.Item(Index)End Function
注意 可以将附加的功能添加到这些方法中。例如,如果一条索引项是无效的,能够引发自己的错误。
最后的方法是 Trouble。该方法试图将未初始化的 Employee 对象添加到集合中。能猜测到将会发生什么事吗?
'SmallBusiness类的方法。Public Sub Trouble()Dim x As New EmployeemcolEmployees.Add xEnd 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 IfcmdListEmployees.Value = TrueEnd SubPrivate Sub cmdListEmployees_Click()Dim lngCt As LonglstEmployees.ClearFor lngCt = 1 To sbMain.EmployeeCountWith sbMain.Employees(lngCt)lstEmployees.AddItem .ID & ", " & .Name _& ", " & .SalaryEnd WithNextEnd 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 方法的重复创建,仍然可能造成错误数据—甚至无效的对象—被插入到集合中,因为私有的变量在整个类模块中都是可见的。
详细信息 该示例在“创建自己的集合类:砖块盖的房子”中将继续讨论。