多态性
多态性是为不同的底层实现提供相同接口的能力
实现接口的能力允许完全将应用程序逻辑与 UI 或数据库或此工作表或该工作表分离。
假设你有一个表单本身实现的 ISomeView
接口:
Option Explicit
Public Property Get IsCancelled() As Boolean
End Property
Public Property Get Model() As ISomeModel
End Property
Public Property Set Model(ByVal value As ISomeModel)
End Property
Public Sub Show()
End Sub
表单的代码隐藏可能如下所示:
Option Explicit
Implements ISomeView
Private Type TView
IsCancelled As Boolean
Model As ISomeModel
End Type
Private this As TView
Private Property Get ISomeView_IsCancelled() As Boolean
ISomeView_IsCancelled = this.IsCancelled
End Property
Private Property Get ISomeView_Model() As ISomeModel
Set ISomeView_Model = this.Model
End Property
Private Property Set ISomeView_Model(ByVal value As ISomeModel)
Set this.Model = value
End Property
Private Sub ISomeView_Show()
Me.Show vbModal
End Sub
Private Sub SomeOtherSettingInput_Change()
this.Model.SomeOtherSetting = CBool(SomeOtherSettingInput.Value)
End Sub
'...other event handlers...
Private Sub OkButton_Click()
Me.Hide
End Sub
Private Sub CancelButton_Click()
this.IsCancelled = True
Me.Hide
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = VbQueryClose.vbFormControlMenu Then
Cancel = True
this.IsCancelled = True
Me.Hide
End If
End Sub
但是,没有什么禁止创建另一个实现 ISomeView
接口的类模块而不是用户表单 - 这可能是一个 SomeViewMock
类:
Option Explicit
Implements ISomeView
Private Type TView
IsCancelled As Boolean
Model As ISomeModel
End Type
Private this As TView
Public Property Get IsCancelled() As Boolean
IsCancelled = this.IsCancelled
End Property
Public Property Let IsCancelled(ByVal value As Boolean)
this.IsCancelled = value
End Property
Private Property Get ISomeView_IsCancelled() As Boolean
ISomeView_IsCancelled = this.IsCancelled
End Property
Private Property Get ISomeView_Model() As ISomeModel
Set ISomeView_Model = this.Model
End Property
Private Property Set ISomeView_Model(ByVal value As ISomeModel)
Set this.Model = value
End Property
Private Sub ISomeView_Show()
'do nothing
End Sub
现在我们可以更改使用 UserForm
的代码并使其在 ISomeView
接口上工作,例如通过将表单作为参数提供而不是实例化它:
Public Sub DoSomething(ByVal view As ISomeView)
With view
Set .Model = CreateViewModel
.Show
If .IsCancelled Then Exit Sub
ProcessUserData .Model
End With
End Sub
因为 DoSomething
方法取决于的接口(即,上抽象 ),而不是一个具体的类 (例如一个特定的 UserForm
),我们可以写出一个自动化单元测试,以确保当 view.IsCancelled
是 True
ProcessUserData
不执行,通过使我们的测试创建一个 SomeViewMock
实例,将其 IsCancelled
属性设置为 True
,并将其传递给 DoSomething
。
可测试代码取决于抽象
可以在 VBA 中编写单元测试,有些插件甚至可以将它集成到 IDE 中。但是当代码与工作表,数据库,表单或文件系统紧密耦合时,单元测试开始需要实际的工作表,数据库,表单或文件系统 - 而这些依赖性是新的失控失败这点可测试的代码应隔离,从而使单元测试并不需要一个实际的工作表,数据库,表格或文件系统。
通过编写针对接口的代码,以允许测试代码注入存根/模拟实现的方式(如上面的 SomeViewMock
示例),你可以在受控环境中编写测试,并模拟 42 个中的每一个可能发生的情况。表单数据上的用户交互的排列,甚至没有一次显示表单并手动单击表单控件。