如何在VBA中利用初始化对象的属性来初始化一个对象?

3
我有两个对象需要互相交互,一个被称为Collateral,另一个被称为ModelModel是一个抽象类,由Model_AModel_BModel_AB实现。每个Collateral对象都有一个models集合作为其属性之一。为了初始化每个Model,我将需要使用来自Collateral(还有另一个对象,我们称之为User_Input)的信息,该信息将随着Model的实现而变化。
我的问题是是否可以使用构造函数来知道创建它的对象(在这种情况下,Model构造函数知道哪个Collateral实例化它)?如果不行,我想有人会建议我使用抽象工厂模式,如果这样的话,它会是什么样子(我担心当涉及到面向对象编程时,我仍然是个新手)?
为简单起见,请假设以下内容:
  • Collateral具有属性A、B、C、Models_Collection
  • Collateral为其创建的每个Models(在Models_Collection中)调用过程Run
  • Model具有名为Run的公共子,该子在下面的所有类中实现
  • 过程Run操作Collateral
  • Model_A需要属性A进行初始化
  • Model_B需要属性B进行初始化
  • Model_AB需要属性A、B进行初始化
以下是我认为的代码示例:

Collateral

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

模型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

模型_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

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

1
我喜欢你正在做的事情 - 在VBA中使用OOP总是很好的。VBA没有构造函数,所以你是对的,你需要工厂;Class_Initialize不能带参数,并且在类中的任何其他代码之前运行 - 所以你不能将其用作构造函数。 - Mathieu Guindon
只是想澄清一下。您希望“Collateral”创建“Model”对象吗?将它们创建在“Collateral”之外并将其传递给它是否可行? - RubberDuck
Mat的Mug: 感谢您的及时回复,您能否提供工厂的外观建议? - sgp667
橡皮鸭:根据Mat's Mug的观察,看起来我必须这样做。 - sgp667
@sgp667 我已经收藏了这篇帖子,如果还没有回答,我会在几个小时后回来看看 :) - Mathieu Guindon
太好了,谢谢你的帮助 :) - sgp667
1个回答

3

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

我的做法是在接口类中定义一个公共枚举,以跟踪已经实现的类。每次你实现一个新的类时,你都需要将其添加到你的枚举和工厂中。这需要一些维护工作,但是如果没有适当的反射,我们就无能为力。

所以,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

请注意,最好使用枚举而不是字符串,如您的示例中所示,因此它会在编译时检查错误而不是运行时。 (这消除了拼写错误的机会。)
如果我实现这个功能,我会创建一个实际的单独类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类只需调用工厂并提供其所拥有的任何内容即可。
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

现在,我提前回答您下一个问题,因为我感觉您已经快要问了。

我该如何确定我的模型类型?

嗯,对于这个问题,您有几个选项,这些选项在这个代码审查问答中有详细介绍。具体取决于您的使用情况,但以下是它们。

  • TypeName(arg) - Returns the string name of the object. For example:

    Dim model As IModel
    Set model = New ModelA
    
    Debug.Print TypeName(model) '=> "ModelA"
    
  • TypeOf and Is - Checks the type of a variable a bit more strongly. Details are in the question I linked to, but here is an example.

    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 ...
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接