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

Posted

技术标签:

【中文标题】Office 类模块(对象)工厂的 VBA【英文标题】:VBA for Office Class Module (Object) Factory 【发布时间】:2011-01-27 19:55:36 【问题描述】:

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

有没有人有任何在 VBA 中实现工厂设计的经验,甚至有可能吗? ...或者有什么巧妙的“技巧”可以帮助我实现相同的总体目标?

我在工厂设计方面的经验仅限于 C#,而且我从未在 VB 中做过,所以也许 VBA 中有些东西我缺少 VB 的通用性。

示例

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

因此,如果我的日期是 2009 年 7 月 15 日,这是一个类型 1 的日期,它将返回

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

如果我输入相同的日期但将其作为日期类型 2 我会得到 日期 1 为空,日期 2 为空,日期 3 为空,日期 4 为 06/10/2011,日期 5 为 07/10/2011

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

希望对你有所帮助。

【问题讨论】:

哇!访问 2008 !你是在哪里找到那个东西的 ?见锋利特辑? /facepalm 我的错误。 2007. 至少在我的辩护中,7 和 8 在键盘上是并排的 > 这些并不是真正的“工厂”特定的答案,但您可能会发现它们很有帮助:VBA 中的继承和对象创建。简而言之,这是一种痛苦。请参阅:***.com/questions/3669270/… 和 ***.com/questions/1731052/… 我认为用不那么抽象的问题你可能会得到更好的答案。我们知道 VBA 在一般情况下不能完全满足您的要求,但是如果您描述一个您想应用“工厂”方法的特定任务,也许老 Access 手可以为您提供建议如何更有效地编写代码。 【参考方案1】:

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

'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

等等等等

如果您总是使用 this 创建 MyObject 实例,那么这个“工厂模式”将替换构造函数。

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

你只需这样做:

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

有了这种东西,你就非常接近于拥有一个构造函数……或一个工厂……

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

【讨论】:

我就是这样做的。我很痛苦没有真正的构造函数。 . . 抱歉,我现在才有时间回到这个项目……是的,我知道你从哪里来,这确实有道理……但是工厂将不得不能够实际返回不同的对象......就像我有一个基本的“抽象”类,以及 4 - 6 个继承该类的类。工厂将根据提供给它的信息决定返回哪个继承类...【参考方案2】:

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

    创建一个名为 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
    创建一个实现类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

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

    现在让我们创建另一个实现 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

    这里是使这个工厂类工作的技巧,这样你就不需要使用新关键字来使用工厂,所以你可以使用依赖注入。这是允许您将工厂设置为单例的技巧。 现在...您需要从项目中删除 ExampleClass 和 BackwardsExampleClass,将其导出到文件夹中,在文本编辑器中打开每个 .cls 文件,将 Predeclared Attribute 设置为“True”,保存每个 .cls 文件,然后重新导入两个类文件到你的项目中。这样做是为这两个实现 IExampleClass 接口的“工厂”类创建一个默认实例。

    现在在直接窗格中输入:

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

    它会返回输出“John Smith”

    在直接窗格中输入下一步:

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

    它会返回输出“Smith, John”

【讨论】:

哈哈谢谢。我什至不再为我有那个项目的公司工作,更不用说在 VBA 中做任何事情了。希望如果将来有人想知道这个问题,您的回答可能会增加他们想要做的事情的视角。 很高兴,这是我第一次尝试回答关于 SE 的问题。希望将来对某人有意义。干杯。 您的代码并没有真正正确地初始化 ExampleClass 和 BackwardsExampleClass 的新实例,它只是重用了预先声明的实例。如果您查看您引用的文章,Create 方法需要在 With 块内设置 New ExampleClass 的属性,同时您正在设置现有预声明实例的属性。

以上是关于Office 类模块(对象)工厂的 VBA的主要内容,如果未能解决你的问题,请参考以下文章

使用Excel VBA发送带有图表对象的电子邮件 - Office 2013

①设计模式 简单工厂

VB类模块中属性的参数——VBA中Range对象的Value属性和Value2属性的一点区别

挑战下你的VBA水平(希尔排序法)!

旧版本的参考 Office 对象库...缺少 mso.dll

每天一个设计模式-1 简单工厂