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属性的一点区别