gpt4 book ai didi

vba - 在 VBA 中,应该避免在类模块中修改文档的代码

转载 作者:行者123 更新时间:2023-12-05 01:26:24 25 4
gpt4 key购买 nike

我开始在 VBA 中使用类,并欣赏 SO 上已经提供的一些精彩信息。

据我所知,似乎缺少的是对类中的代码应该做什么的解释,或者,正如我怀疑的那样,不应该做什么。例如:

假设我有一个文档并希望插入/修改一个表格。在这个例子中,我想:

  • 检查表是否存在
  • 如果表不存在:
  • 在特定位置添加表格
  • 向表中添加信息(即添加行)
  • 如果表确实存在
  • 向/从表中添加/删除信息
  • 根据某些标准对表格进行排序

  • 关于“排序”,我认为类模块非常适合根据某些标准确定信息应放入表格中的顺序。

    但理想情况下:
  • 是否应该使用类模块(或第二类模块)来检查和编辑文档?

  • 或者
  • 使用常规模块最好进行检查和/或编辑吗?

  • 还是没关系?如果有首选方法,那么每种方法的优点/缺点是什么?

    最佳答案

    首先,感谢进入 OOP 的精彩兔子洞!

    简答:这取决于。

    (非常)长答案:

    您将希望避免从 Application.Worksheets 中提取工作表 [在编译时存在]。 (或 Application.Sheets)集合,并使用该表的 CodeName反而。 VBA 创建一个全局范围的对象引用供您使用,以每个工作表的 CodeName 命名。 .

    这就是这段代码的编译方式,使用 Sheet1未在任何地方声明:

    Option Explicit

    Sub Test()
    Debug.Print Sheet1.CodeName
    End Sub

    使用全局范围的“自由”对象变量在工作表的代码隐藏之外的任何地方实现特定于工作表的功能的问题在于,单独的模块现在与 Sheet1 耦合。目的。

    取决于工作表的类模块。任何工作表。

    您需要集中的、有凝聚力的模块 - 高凝聚力。并且低耦合。

    通过在另一个模块(无论是标准模块还是类模块)中编写特定于工作表的代码,您正在创建依赖项并增加耦合,这会降低可测试性 - 考虑 Class1 中的此代码:
    Public Sub DoSomething()
    With Sheet1
    ' do stuff
    End With
    End Sub

    现在 Class1只能使用 Sheet1 .这样会更好:
    Public Sub DoSomething(ByVal sheet As Worksheet)
    With sheet
    ' do stuff
    End With
    End Sub

    这里发生了什么?依赖注入(inject)。我们依赖于特定的工作表,但不是针对特定对象进行编码,而是告诉世界“给我任何工作表,我会用它做我的事情”。那是在方法级别。

    如果一个类意味着使用单个特定的工作表,并公开多个对该工作表执行各种操作的方法,则具有 ByVal sheet As Worksheet每个方法上的参数都没有多大意义。

    相反,您会将其作为属性注入(inject):
    Private mSheet As Worksheet

    Public Property Get Sheet() As Worksheet
    Set Sheet = mSheet
    End Property

    Public Property Set Sheet(ByVal value As Worksheet)
    Set mSheet = value
    End Property

    现在该类的所有方法都可以使用 Sheet ...唯一的问题是使用该类的客户端代码现在需要记住 Set那个 Sheet属性,否则可能会出现错误。这是糟糕的设计 IMO。

    一种解决方案可能是将依赖注入(inject)原则更进一步,实际上依赖于抽象;我们将要为该类公开的接口(interface)形式化,使用另一个用作接口(interface)的类模块——即 IClass1类没有实现任何东西,它只是为公开的内容定义 stub :
    '@Interface
    Option Explicit

    Public Property Get Sheet() As Worksheet
    End Property

    Public Sub DoSomething()
    End Sub

    我们的 Class1 class 模块现在可以实现该接口(interface),如果您一直在关注这一点,希望我不会在这里迷失您:

    NOTE: Module and member attributes are not visible in the VBE. They're represented here with their corresponding Rubberduck annotations.


    '@PredeclaredId
    '@Exposed
    Option Explicit
    Implements IClass1

    Private mSheet As Worksheet

    Public Function Create(ByVal pSheet As Worksheet) As IClass1
    With New Class1
    Set .Sheet = pSheet
    Set Create = .Self
    End With
    End Function

    Friend Property Get Self() As IClass1
    Set Self = Me
    End Property

    Private Property Get IClass1_Sheet() As Worksheet
    Set IClass1_Sheet = mSheet
    End Property

    Private Sub IClass1_DoSomething()
    'implementation goes here
    End Sub

    Class1 class 模块提供了两个接口(interface):
  • Class1成员,可从 PredeclaredId 访问实例:
  • Create(ByVal pSheet As Worksheet) As IClass1
  • Self() As IClass1
  • IClass1成员,可从 IClass1 访问界面:
  • Sheet() As Worksheet
  • DoSomething()

  • 现在调用代码可以是这样的:
    Dim foo As IClass1
    Set foo = Class1.Create(Sheet1)
    Debug.Assert foo.Sheet Is Sheet1
    foo.DoSomething

    因为它是针对 IClass1 写的接口(interface),调用代码只能“看到” SheetDoSomething成员。因为 VB_PredeclaredId Class1 的属性, Create功能可以通过 Class1 访问默认实例,非常像 Sheet1无需创建实例即可访问(用户窗体类也具有该默认实例)。

    这是工厂设计模式:我们使用默认实例作为工厂,其作用是创建和初始化 IClass1 的实现。接口(interface),其中 Class1恰好正在实现。

    Class1Sheet1 完全解耦, 拥有 Class1 绝对没有错负责它初始化的任何工作表上需要发生的一切。

    耦合得到照顾。凝聚力完全是另一个问题:如果你发现 Class1正在长出头发和触须,并且对许多事情负责,你甚至都不知道它是为了什么而写的,很可能是单一责任原则正在遭受打击,而 IClass1 interface 有太多不相关的成员,以至于 Interface Segregation Principle 也受到打击,其原因可能是因为接口(interface)在设计时没有考虑到 Open/Closed Principle。

    以上无法用标准模块实现。标准模块不能很好地与 OOP 配合使用,这意味着更紧密的耦合,从而降低了可测试性。

    特尔;博士:

    没有一种“正确”的方式来设计任何东西。
  • 如果您的代码可以处理与特定工作表的紧密耦合,更喜欢在该工作表的代码隐藏中实现该工作表的功能,以获得更好的凝聚力 .仍然使用专门的对象(类)来执行专门的任务:如果您的工作表代码隐藏负责设置数据库连接、通过网络发送参数化查询、检索结果并将它们转储到工作表中,那么您就在做Wrong™ 现在在不访问数据库的情况下单独测试该代码是不可能的。
  • 如果您的代码更复杂并且无法承受与特定工作表的紧密耦合,或者如果该工作表在编译时不存在,在可以处理任何工作表的类中实现功能 ,并有一个类负责该运行时创建的工作表的模型。

  • IMO 标准模块应该只用于公开入口点(宏、UDF、Rubberduck 测试方法,以及 Option Private Module 和一些常见的实用函数),并且包含相当少的代码,仅初始化对象及其依赖项,然后是类一直往下。

    关于vba - 在 VBA 中,应该避免在类模块中修改文档的代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45025168/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com