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

时间:2015-03-26 19:32:04

标签: vba oop excel-vba factory factory-pattern

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

我的问题是可以使用一个能够知道创建它的对象的构造函数(在这种情况下Model构造函数知道Collateral实例化了什么)? 如果没有,我认为有人会建议我使用抽象工厂模式,如果是这样,它会是什么样子(我担心在OOP时我仍然是绿色的)?

为简单起见,假设如下:

  • Collateral具有属性A,B,C,Models_Collection
  • Collateral为其创建的每个Run调用过程Models(在Models_Collection中有)
  • Model有一个名为Run的公共子,它在所有类中实现
  • 程序Run处理Collateral
  • Model_A要求属性A初始化
  • Model_B要求属性B初始化
  • Model_AB要求属性A,B初始化

以下是我认为应如下所示的简化代码:

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

1 个答案:

答案 0 :(得分:3)

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

我倾向于如何解决这个问题,在Interface类中定义一个公共枚举,用于跟踪已实现的类。无论何时实施新的,您都需要将其添加到枚举和工厂中。它比我更喜欢维护,但如果没有适当的反思,我们就无法做到这一点。

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

现在,我会先发制人地回答你的下一个问题,因为我觉得你已经开始提出问题了。

  

我怎样才能准确说出我的模型类型?

嗯,为此你有几个选项在this code review Q & A中有点详细说明。这取决于你的用例,但在这里它们是。

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

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