VBA for Office类模块(对象)工厂

Pat*_*ick 1 inheritance ms-access vba abstract-class factory

我有一个相对复杂的Access 2007用VBA编写的应用程序(4个枚举,7个模块,38个类模块,86个表单,以及一大堆表和查询).我发现使用对象工厂设计是有益的,但到目前为止,我无法找到一种干净的方法来实现这种类型的功能,而无需在VB或C#中轻松实现的标准抽象/继承.

有没有人有过在VBA中实施工厂设计的经验,甚至可能吗?......还是有一个巧妙的"技巧"可以帮助我获得相同的总体目标?

我在工厂设计方面的经验仅限于C#,我从来没有在VB中完成它,所以也许VBA中有一些我不知道的VB常见的东西.

我将收到具体日期.根据该日期,我需要计算2到5个其他日期之间的任何地方.计算这些日期的规则根据输入日期的"类型"而变化.

因此,如果我的日期为07/15/2009,这是一个类型1日期,它将返回

07/15/2010日期1,07/15/2011日期2,07/15/2012日期3,06/10/2012,日期4和07/10/2012,日期5

如果我把相同的日期,但把它作为日期类型2我将得到null为日期1,null为日期2,null为日期3,06/10/2011为日期4和07/10/2011为日期5

因此,对于每组规则,最少有3个可能的最大值为6(现在这可以随时扩展)我将基本上输入一个开始日期...规则...并返回一个对象包含所有日期属性.

我希望这有点帮助.

awr*_*ley 6

我可能错过了问题的重点,但为什么不在标准模块中使用"工厂"方法/"构造函数":

'default constructor
    Public Function MyClassFactory() As MyClass
        Set MyClassFactory = New MyClass
    End Function
Run Code Online (Sandbox Code Playgroud)

或者,如果您需要带有参数的"构造函数":

'Constructor with parameters
    Public Function MyClassFactory(Param1 As ParamObject1, Param2 As ParamObject2) As MyClass
        Dim MyThing As MyClass
        Set MyThing = New MyClass

    'MyObjectInitializer is a Sub that does what a constructor should do
        MyThing.MyObjectInitializer Param1, Param2
        Set MyClassFactory = MyThing
    End Function
Run Code Online (Sandbox Code Playgroud)

等等

如果您始终使用此方法创建MyObject实例,则此"工厂模式"将替换构造函数.

您可以修改此代码以仅创建单例等.有时,VBA的缺点(例如,具有全局范围的标准模块)可以变成有用的东西.

要打电话给你,你只需:

Dim Thing As MyClass
Set Thing = MyClassFactory(Param1, Param2)
Run Code Online (Sandbox Code Playgroud)

有了这种东西,你很接近有一个构造函数......或者一个工厂......

我肯定错过了什么.我对Factory模式的理解可能过于简单,但是你可能不希望在VBA中过于复杂.如果您发现需要,可能存在设计问题.


bra*_*row 5

这是一种在 VBA 中实现工厂模式的方法,Rubberduck 网站上对此进行了很好的描述https://rubberduckvba.wordpress.com/2016/07/05/oop-vba-pt-2-factories-and-cheap-酒店/ . 这是我试图解释的。我知道可能有一种更简洁的方法来做到这一点,但我试图演示两件事:使用工厂模式和依赖注入来构造对象,而无需新建它们;以及在VBA中使用多态性的能力,使得抽象接口类可以有多种不同的实现。开放反馈。开始:

  1. 创建一个名为 IExampleClass 的简单接口类并在其上设置以下成员:
Option Explicit

Public Property Get Firstname() As String
End Property

Public Property Get Lastname() As String
End Property

Public Function ToString() As String
End Function
Run Code Online (Sandbox Code Playgroud)
  1. 创建一个实现类ExampleClass。请注意,它有一个 Create 方法。这是你的工厂方法。另请注意 Self getter,它允许 Create 方法使用非常简洁的语法:
Option Explicit     
Private Type TExample
    Firstname As String
    Lastname As String
End Type

Private this As TExample

Implements IExampleClass

Public Property Get Firstname() As String
    FirstName = this.Firstname
End Property

Public Property Let Firstname(Value As String)
    this.Firstname = Value
End Property

Public Property Get Lastname() As String
    Lastname = this.Lastname
End Property

Public Property Let Lastname(Value As String)
    this.Lastname = Value
End Property

Public Property Get Self() As IExampleClass
    Set Self = Me
End Property

Public Function Create(ByVal First As String, ByVal Last As String)
    With New ExampleClass
        this.Firstname = First
        this.Lastname = Last
        Set Create = Self
    End With
End Function

Private Property Get IExampleClass_Firstname() As String
    IExampleClass_Firstname = this.Firstname
End Property

Private Property Get IExampleClass_Lastname() As String
    IExampleClass_Lastname = this.Lastname
End Property

Private Function IExampleClass_ToString() As String
    IExampleClass_ToString = this.Firstname & " " & this.Lastname
End Function
Run Code Online (Sandbox Code Playgroud)

请注意,在此类中,Interface 中的每个成员的实现都具有私有签名,因此使用此ExampleClass 的代码只能从IExampleClass 接口(抽象)对象访问ToString 方法。

  1. 现在让我们创建另一个实现 IExampleClass 接口的类,将其命名为 BackwardsExampleClass:
   Option Explicit
   Private Type TExample
       Firstname As String
       Lastname As String
   End Type

   Private this As TExample

   Implements IExampleClass

   Public Property Get Firstname() As String
       Firstname = this.Firstname
   End Property

   Public Property Let Firstname(Value As String)
       this.Firstname = Value
   End Property

   Public Property Get Lastname() As String
       Lastname = this.Lastname
   End Property

   Public Property Let Lastname(Value As String)
       this.Lastname = Value
   End Property

   Public Property Get Self() As IExampleClass
       Set Self = Me
   End Property

   Public Function Create(ByVal First As String, ByVal Last As String)
       With New ExampleClass
           this.Firstname = First
           this.Lastname = Last
           Set Create = Self
       End With
   End Function

   Private Property Get IBackwardsExampleClass_Firstname() As String
       IExampleClass_Firstname = this.Firstname
   End Property

   Private Property Get IBackwardsExampleClass_Lastname() As String
       IExampleClass_Lastname = this.Lastname
   End Property

   Private Function IBackwardsExampleClass_ToString() As String
      IExampleClass_ToString = this.Lastname & ", " & this.Firstname
   End Function
Run Code Online (Sandbox Code Playgroud)
  1. 这是使这个工厂类工作的技巧,这样你就不需要使用 New 关键字来使用工厂,因此你可以使用依赖注入。这是允许您将工厂设置为单例的技巧。现在...您需要从项目中删除ExampleClass和BackwardsExampleClass,将其导出到文件夹中,在文本编辑器中打开每个.cls文件,将预声明属性设置为“True”,保存每个.cls文件,然后重新导入将两个类文件添加到您的项目中。其作用是创建这两个实现 IExampleClass 接口的“Factory”类的默认实例。

  2. 现在在立即窗格中输入:

       Debug.print ExampleClass.Create("John","Smith").ToString
    
    Run Code Online (Sandbox Code Playgroud)

    它将返回输出“John Smith”

  3. 下一步 在立即窗格中输入:

       Debug.print BackwardsExampleClass.Create("John","Smith").ToString
    
    Run Code Online (Sandbox Code Playgroud)

    它将返回输出“Smith, John”