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

我的问题是有可能使用一个构造函数,该构造函数将知道正在创建该对象的对象(在这种情况下,Model知道什么实例化它的Collateral的构造函数)?
如果不是,我假设有人会建议我使用抽象工厂模式,如果是这样,它将是什么样子(恐怕在OOP方面我还是很环保)?

为了简单起见,假设以下内容:


Collateral具有属性A,B,C和Models_Collection
Collateral为其创建的每个Run调用过程Models(在Models_Collection中具有)
Model有一个名为Run的公共Sub,下面的所有类中都实现了该Sub
过程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


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

最佳答案

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

我倾向于在接口类中定义一个公共枚举,以跟踪已实现的类。每当您实现一个新代码时,都需要将其添加到枚举和Factory中。我喜欢这样,需要更多的维护,但是如果没有适当的思考,我们对此无能为力。

因此,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 ...

10-08 01:53