gpt4 book ai didi

excel - 在“代理”接口(interface)+类中实现工作表的代码后,可以正确处理工作表事件

转载 作者:行者123 更新时间:2023-12-02 17:18:38 27 4
gpt4 key购买 nike

我试图在this excellent example的基础上进一步完善,proxy classes已经实现了这些非常有见地的RubberduckVBA.com文章中讨论的最佳实践:


通过Utilizing the UserForm control从Excel工作簿/工作表中抽象出来;
Adding "Apply" logic to #2不会弄乱其默认实例的状态;
existing example


我想在given example中添加一个事件处理程序,该事件处理程序(为简单起见)报告Sheet2的“ A1”单元格中Sheet2的“更改”范围的左上单元格的值,以及“ A2”。我通常会像这样在Sheet2的代码中进行设置:

Private Sub Worksheet_Change(ByVal Target As Range)
Sheet1.Cells(1, 1).Value2 = Target.Cells(1, 1).Value2
Sheet1.Cells(1, 2).Value2 = CStr(Now)
End Sub


但是我想知道如何在 Battleship tutorial中最好地实现这一点,考虑到它是围绕MVP模式设计的,并通过代理接口利用了工作簿和工作表抽象,这意味着期望零/最小工作表代码。

我能够理解在很棒的 Adapter Pattern中如何实现事件处理,但是它的设计在一些重要方面有所不同:


“战列舰”遵循MVC设计模式,而我想像示例一样坚持使用MVP。
“战舰”通过“查看”类从其工作表中抽象出来,而我希望每个工作表都有一个单独的代理接口+类;
当我将视图和工作表代理实现与演示者结合在一起(如果可能的话,关于事件处理)时,“战役”部署 base project


考虑到这一点,我绝对希望看到一个代码示例,该示例将上述的“ Worksheet_Change”事件添加到 Worksheet proxy approach中,该事件已经实现了Workbook和Worksheet代理并遵循MVP模式。

即使没有代码示例,如果我将这些问题弄清楚,也会有很大帮助:


Adapter Pattern是否指示后面绝对应该有零个工作表代码?如果我像这样在Sheet2(而不是它的代理)内开始“ Worksheet_Change”事件实现,这将是朝错误方向迈出的一步:


Public Event SheetChanged(ByVal changedRange As Range)

Private Sub Worksheet_Change(ByVal Target As Range)
RaiseEvent SheetChanged(Target)
End Sub



如果不是绝对需要使用 Lazy Object/Weak Reference进行事件处理,那么使用“ IViewCommands”和“ IViewEvents”接口列出从Presenter发送到View的所有命令以及从View引发的事件是否仍然是一个好主意。并分别发送给主持人?
我认为我将需要使用 来公开事件。如果是这样,并假设我无需适配器就可以完成工作(请参见上面的#2),这是否意味着我的“ Sheet2Proxy”类将必须通过其“ IViewEvents”持有对Presenter的弱引用(再次参见# 2以上)界面?

最佳答案

您正在将“ prcc”类后面的Worksheet抽象出来;根据定义,它与工作表结合在一起,而您想要的是确保抽象是气密的,以免您看到的是泄漏的抽象,并最终将其他代码与Excel.Worksheet类型耦合,从而破坏了整个代码目的。

对于项目的其余部分,工作表代理类充当一个外观,用于操纵和理解有关特定Excel.Worksheet的所有知识:结果是,您现在可以使用两个模块来抽象工作表内容-工作表本身以及代理类:


后台工作表可以抽象ListObject /表,命名范围等。使用代理可以使用的Property Get成员。
工作表代理类从其余代码中抽象出工作表操作。


确实,这种方法对于实际的工作表代码隐藏并没有太多的余地/需要:我将开始对代理类中的所有内容进行编码,并且如果该模块过于冗长,或者如果我发现其抽象级别需要获取一个高一点,然后我将较低层的内容移到工作表的代码本身后面。

Workheet模块和其他文档模块不应实现接口-使工作表实现接口是混淆和崩溃VBA的好方法:不要这样做。因此,这可能是您的代码背后:

Option Explicit

Public Property Get SomeSpecificRange() As Range
Set SomeSpecificRange = Me.Names("SomeSpecificRange").RefersToRange
End Property


然后,代理类可以做到这一点:

Option Explicit
Private sheetUI As Sheet1
Private WithEvents sheet As Worksheet

Private Sub Class_Initialize()
Set sheet = Sheet1
Set sheetUI = Sheet1
End Sub

Private Sub sheet_Change(ByVal Target As Range)
If Intersect(Target, sheetUI.SomeSpecificRange) Then
'...
End If
End Sub


因此,代理类可以很好地处理工作表事件,而无需整个适配器。它还可以通过其公开的 Public成员处理来自演示者的命令。

但是代理类(又称“抽象工作表”)不是响应事件的正确地方:主持人需要主持表演。

因此,您可以使代理激发事件来响应工作表事件,并包装消息并将其转发给演示者:

Option Explicit
Public Event SomeSpecificRangeChanged()
Private sheetUI As Sheet1
Private WithEvents sheet As Worksheet

Private Sub Class_Initialize()
Set sheet = Sheet1
Set sheetUI = Sheet1
End Sub

Private Sub sheet_Change(ByVal Target As Range)
If Intersect(Target, sheetUI.SomeSpecificRange) Then
RaiseEvent SomeSpecificRangeChanged
End If
End Sub


然后,演示者可以处理代理类中的 SomeSpecificRangeChanged-提出一些UserForm,激发一些数据库查询,无论要求是什么:

Private WithEvents proxy As Sheet1Proxy

Private Sub Class_Initialize()
Set proxy = New Sheet1Proxy
End Sub

Private Sub proxy_SomeSpecificRangeChanged()
'business logic to run when SomeSpecificRange is changed
End Sub


问题是代理类与工作表耦合在一起,而现在演示者与代理相耦合:我们已经抽象了很多东西,但是仍然无法将工作表/代理依赖项交换为其他内容并测试演示者逻辑而不涉及工作表。

因此,我们创建了一个接口以使演示者与代理脱钩-例如 ISheet1Proxy ...,现在我们被困住了,因为我们无法在接口上公开事件。

这是适配器模式发挥作用的地方,它使我们能够为“命令”(演示者->视图)和“事件”(视图->演示者)形式化接口。

有了适配器,工作表/代理和演示者现在就完全分离了,现在您可以在没有任何 Excel.Worksheet的情况下实现演示者逻辑,而对于任何 Excel.RangeExcel.*则一无所知:每个工作表的交互都已正式化就像发送到视图/工作表/代理的某些“命令”,或者发送到演示者的某些“事件”一样,就像在Battleship项目中一样。

旁注,我发现并非总是需要 WeakReference东西来正确拆卸对象层次结构:这就是为什么在当前版本的Battleship代码中不再使用它。



显然,这是很多工作。对于OOP原理和学习编写可以进行单元测试的解耦代码来说,这是一个很好的实践……但是对于一个小型VBA项目而言,这绝对是对IMO的过大杀伤力。



所有这些都将 Excel.*类视为具体类型,就VBA而言,也可能是这种情况。但是,就.NET而言, Excel互操作类型都是所有接口,因此Rubberduck是 about to tremendously simplify everything,它为 Moq(一种非常流行的.NET模拟框架)提供了包装API:



这将消除将工作表与用户代码完全分离以使其完全可测试的需要-唯一的要求将是依赖项注入,即更喜欢这样:

Public Sub DoSomething(ByVal target As Range)
target.Value = 42
End Sub


在此:

Public Sub DoSomething()
Dim target As Range
Set target = Sheet1.Range("A1")
target.Value = 42
End Sub

关于excel - 在“代理”接口(interface)+类中实现工作表的代码后,可以正确处理工作表事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55689220/

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