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

时间:2011-01-27 19:55:36

标签: ms-access vba inheritance abstract-class factory

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

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

我在工厂设计方面的经验仅限于C#,我从来没有在VB中完成它,所以也许VBA中有些东西是我所缺少的。

示例

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

所以如果我的日期是07/15/2009,这是一个类型1的日期,它会返回

07/15/2010 for date 1,07 / 15/2011 for date 2,07 / 15/2012 for date 3,06 / 10/2012 for date 4 and 07/10/2012 for date 5

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

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

我希望这有点帮助。

2 个答案:

答案 0 :(得分:5)

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

'default constructor
    Public Function MyClassFactory() As MyClass
        Set MyClassFactory = New MyClass
    End Function

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

'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

Etc等。

如果您始终使用此方法创建MyObject实例,则此“Factory Pattern”将替换构造函数。

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

要打电话给你,你就是这样做:

Dim Thing As MyClass
Set Thing = MyClassFactory(Param1, Param2)

对于这种事情,你非常接近建造者......或工厂......

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

答案 1 :(得分:1)

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

  1. 创建一个名为IExampleClass的简单接口类,并在其上设置以下成员:
  2. 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
    
    1. 创建一个实现类ExampleClass。请注意,这有一个Create方法。这是您的工厂方法。另请注意Self getter,它允许Create方法使用非常简洁的语法:
    2. 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
      

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

      1. 现在让我们创建另一个实现IExampleClass接口的类,称之为BackwardsExampleClass:
      2.    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
        
        1. 以下是使此工厂类工作的技巧,因此您不需要使用新关键字来使用工厂,因此您可以使用依赖注入。这是允许您将Factory设置为单例的技巧。 现在......您需要从项目中删除ExampleClass和BackwardsExampleClass,将其导出到文件夹,在文本编辑器中打开每个.cls文件,将Predeclared属性设置为“True”,保存每个.cls文件,然后重新导入将两个类文件放入您的项目中。这样做是为这两个实现IExampleClass接口的“Factory”类创建一个默认实例。

        2. 现在输入直接窗格:

             Debug.print ExampleClass.Create("John","Smith").ToString
          
               

          它将返回输出“John Smith”

        3. 下一步输入即时窗格:

             Debug.print BackwardsExampleClass.Create("John","Smith").ToString
          
               

          它将返回输出“Smith,John”