如何借助 VBA 中初始化对象的属性来初始化对象

How Can I Initialize an object with help of properties of initializing object in VBA

我有两个对象需要相互交互,一个叫做 Collateral,另一个叫做 ModelModel是一个抽象Class是由Model_AModel_BModel_AB实现的。每个 Collat​​eral 对象都有一个 models 的集合作为其属性之一。为了初始化每个 Model,我将需要使用来自 Collateral 的信息(还有另一个对象我们称之为 User_Input),该信息将随着 Model 的实现而变化。

我的问题是,是否可以使用一个构造函数来了解创建它的对象(在本例中 Model 构造函数知道 Collateral 实例化了它)? 如果不是,我假设有人会建议我使用抽象工厂模式,如果是的话,它会是什么样子(恐怕我在 OOP 方面仍然是绿色的)?

为简单起见,假设如下:

这是我假设的简化代码:

抵押品

Dim A, B, C as Variant
Dim Model_Collection as Collection
Sub New_Model( Model_Type as String)
    Model_Collection.Add(Model_Implementation)
End Sub
Sub Execute_Models()
    For Each Model in Model_Collection
        Model.Run(Me)
    Next Model
End Sub

型号

    Sub Run()
    End

Model_A

Implements Model
Sub Class_Initialize()
    'Some code that takes property A from Collateral that Created this object
Sub Run(Collateral as Collateral)
    'Some Code
End Sub

Model_B

Implements Model
Sub Class_Initialize()
    'Some code that takes property B from Collateral that Created this object
Sub Run(Collateral as Collateral)
    'Some Code
End Sub

Model_AB

Implements Model
Sub Class_Initialize()
    'Some code that takes property A, and B from Collateral that Created this object
Sub Run(Collateral as Collateral)
    'Some Code
End Sub

首先,让我们回答你的问题。您如何动态创建实现相同接口的不同 class 的实例?正如所指出的,VBA 没有任何构造函数,所以你是对的。这里需要工厂模式。

我倾向于在接口 class 中定义一个 public 枚举来跟踪已实现的 classes。任何时候你实现一个新的,你都需要将它添加到你的枚举和工厂。这比我喜欢的要多一些维护,但如果没有适当的反思,我们对此无能为力。

因此,IModel 接口:

Public Enum EModel
    ModelA
    ModelB
    ModelC
End Enum

Public Sub Run
End Sub

您的模型本身保持不变。然后回到你的 Collateral 像这样实现你的 New_Model

private models as Collection

Public Sub New_Model(ByVal type As EModel) As IModel
    dim model As IModel
    Select Case type
        Case EModel.ModelA: Set model = New ModelA
        Case EModel.ModelB: Set model = New ModelB
        Case EModel.ModelC: Set model = New ModelC
    End Select

    models.Add model
End Sub

请注意,在您的示例中使用枚举比使用字符串更好,这样它会在编译时而不是运行时检查错误。 (这消除了拼写错误的可能性。)


如果是我实现这个,我会创建一个实际的单独 class ModelFactory。然后 Collateral 会调用模型工厂来获取它需要的东西。我认为它很好地分离了关注点。

根据您的要求,实现看起来像这样。

 Public Function CreateModel(Optional A As Variant, Optional B As Variant, Optional C As Variant)
     If Not A Is Nothing Then
         If B Is Nothing Then
             Set CreateModel = New ModelA
             Exit Function
         Else
             Set CreateModel = New ModelC
             Exit Function
         End If
     End If

     If Not B Is Nothing Then
         Set CreateModel = New ModelB
         Exit Function
     End If
 End Function

请注意,这完全消除了枚举和指定类型的需要。工厂知道根据可用的参数创建什么。

然后你的 Collateral class 只需调用工厂并提供它所有的东西。

Private A,B,C
Private models As Collection
Private factory As ModelFactory

Private Sub Class_Initialize()
    Set factory = New ModelFactory
End Sub

Public Sub New_Model()
    models.Add factory.CreateModel(A,B,C)
End Sub

现在,我要抢先回答你的下一个问题,因为我觉得你已经快要问了。

How can I tell exactly what type of model I have?

好吧,为此您有一些选项,在 this code review Q & A 中有一些详细说明。这取决于您的用例,但它们就在这里。

  • TypeName(arg) - Returns 对象的字符串名称。例如:

    Dim model As IModel
    Set model = New ModelA
    
    Debug.Print TypeName(model) '=> "ModelA"
    
  • TypeOfIs - 更严格地检查变量的类型。详细信息在我链接到的问题中,但这里有一个例子。

    Dim model as IModel
    Set model = SomeFunctionThatReturnsAnIModel()
    
    If TypeOf model Is ModelA Then
        ' take some specific action for ModelA types
    Else If TypeOf model Is ModelB Then
        ' ModelB type specific action
    Else If ...