我想创建一个函数,它接受一个类示例/类名称并返回一定数量的所述类的示例的集合,初始化和填充。到目前为止,我已经提出了这个解决方案:
Function getCollectionObj(className as string) As Collection
Dim obj As Variant
Dim result As Collection
Dim objType As String
Dim I
For I = 1 To 5
'selecting the type of a class based on string name
Select Case className
Case "Cls1"
Set obj = New Cls1
Case "Cls2"
Set obj = New Cls2
Case "DocumentStart"
Set obj = New Cls3
End Select
'some code handling the exemplar of the object.
result.add obj
Next I
Set getCollectionObj = result
End Function
Run Code Online (Sandbox Code Playgroud)
这样做的问题是我必须明确地将每个类放入select case其中,并且代码每次都必须选择和声明 cerrain 类,即使集合中的所有对象都应该是同一类的范例。我该如何改进?
编辑:我的测试代码(给出“对象需要错误”)
主要模块:
Private Function getCollectionObj(ByVal factory As IObjectFactory) As Collection
Dim I
For I = 1 To 5
result.Add factory.Create
Next I
End Function
Sub test()
Dim var As Object
Set var = getCollectionObj(New Class1ObjectFactory)
End Sub
Run Code Online (Sandbox Code Playgroud)
IObjectFactory 类:
Option Explicit
Public Function Create() As Object
End Function
Run Code Online (Sandbox Code Playgroud)
Cls1Factory 类(选择函数时,右侧下拉列表显示“创建”):
Option Explicit
Implements IObjectFactory
Private Function IObjectFactory_Create() As Object
' "Object required" error here
Set IObjectFactory_Create = New Cls1
End Function
Run Code Online (Sandbox Code Playgroud)
Cls1 类:
Option Explicit
Public I As String
Run Code Online (Sandbox Code Playgroud)
理想情况下,您不会(回答标题中的问题) - 例如,当类被重命名时,您的代码不会无声地中断。
有一种更好的方法,一旦出现问题,它就会立即为您提供编译时错误,并且可以很好地与静态代码分析和重构工具(例如Rubberduck)配合使用- 完全公开,这是我的网站,我管理着 Rubberduck 打开-源项目。
您可以通过定义抽象工厂接口来形式化创建类实例的任务。添加一个新的类模块,调用它:IObjectFactory
Option Explicit
Public Function Create() As Object
End Function
Run Code Online (Sandbox Code Playgroud)
现在添加一个新的类模块,调用它Cls1Factory并使其实现抽象工厂接口:
Option Explicit
Implements IObjectFactory
Private Function IObjectFactory_Create() As Object
Set IObjectFactory_Create = New Cls1
End Function
Run Code Online (Sandbox Code Playgroud)
添加一个新的类模块,调用它Cls2Factory并使其也实现抽象工厂接口:
Option Explicit
Implements IObjectFactory
Private Function IObjectFactory_Create() As Object
Set IObjectFactory_Create = New Cls2
End Function
Run Code Online (Sandbox Code Playgroud)
添加另一个可以创建Cls3实例的实现,称之为Cls3Factory.
然后按如下方式更改函数的签名:
Public Function getCollectionObj(ByVal factory As IObjectFactory) As Collection
Run Code Online (Sandbox Code Playgroud)
现在整个Select Case块变成了这个单一的语句:
result.Add factory.Create
Run Code Online (Sandbox Code Playgroud)
用于执行此操作的调用代码:
Set things1 = getCollectionObj("Cls1")
Set things2 = getCollectionObj("Cls2")
Set things3 = getCollectionObj("Cls3")
Run Code Online (Sandbox Code Playgroud)
现在需要这样做:
Set things1 = getCollectionObj(New Cls1Factory)
Set things2 = getCollectionObj(New Cls2Factory)
Set things3 = getCollectionObj(New Cls3Factory)
Run Code Online (Sandbox Code Playgroud)
该函数现在可以创建您想要的任何类的 5 个实例的集合,根本不需要修改它:如果您需要支持一个新类,只需为它实现一个新工厂,然后将它作为参数传递给函数。
回复:编辑
Rubberduck 的静态代码分析可以帮助您避免大量陷阱,就像您编辑的代码段中的陷阱一样:未分配函数返回值,在初始化之前访问结果变量,等等:
“需要对象”错误由其根本原因发出信号,此处按导致此特定错误的级联顺序:
Option Explicit未指定允许代码在result未声明的情况下执行。因为它没有被声明,所以它被分配为 aVariant/Empty直到它被分配......除非它从来没有,并且当一个成员调用 ( .Add) 临时针对该变体进行时,这就是 VBA在运行时尖叫并说“需要对象”的时候,因为该成员调用需要一个对象才能有效。但是 VBA 看到的只是一个Variant/Empty它刚刚在现场创建的指针。
运行时引发错误后,该.Create工厂方法调用返回,但之前返回的对象作为参数传递给一个偶然后期绑定 .Add成员通话。执行没有进入.Add方法,VBA没有成功Collection从result变量中解引用一个对象;但是因为调试器不允许我们单步执行操作符,所以当我们点击“DEBUG”时,我们会回到失败的语句上,并且整个result.Add factory.Create语句都被突出显示(而不是仅仅result.Add调用是这里实际失败的操作) .
因为在同一行上有两个语句,调试器并不完全处于它现在应该处于的确切状态。如果我们按下 F8,我们将被带回到工厂方法中,如果我们没有意识到发生了什么,那么很容易让调试器将我们带入调查完全正常的执行路径中。
分开声明:
Dim o As Object
Set o = factory.Create '<~ no problem here!
result.Add o '<~ object required
Run Code Online (Sandbox Code Playgroud)
现在调试器进入中断模式,并突出显示正确的失败语句。
为了完整起见,工作代码:
Option Explicit
Public Sub test()
Dim var As Collection
Set var = getCollectionObj(New Class1ObjectFactory)
Debug.Print var.Count
End Sub
Private Function getCollectionObj(ByVal factory As IObjectFactory) As Collection
Dim result As Collection
Set result = New Collection
Dim I As Long
For I = 1 To 5
result.Add factory.Create
Next I
Set getCollectionObj = result
End Function
Run Code Online (Sandbox Code Playgroud)