Can we use Interfaces and Events together at the same time?

前端 未结 4 2067
情歌与酒
情歌与酒 2020-12-04 09:08

I\'m still trying to wrap my head around how Interfaces and Events work together (if at all?) in VBA. I\'m about to build a large application in Microsoft Access, and I want

4条回答
  •  一整个雨季
    2020-12-04 09:51

    This is a perfect use-case for an Adapter: internally adapting the semantics for a set of contracts (interfaces) and exposing them as its own external API; possibly according to some other contract.

    Define class modules IViewEvents:

    Option Compare Database
    Option Explicit
    
    Private Const mModuleName   As String = "IViewEvents"
    
    Public Sub OnBeforeDoSomething(ByVal Data As Object, ByRef Cancel As Boolean):  End Sub
    Public Sub OnAfterDoSomething(ByVal Data As Object):                            End Sub
    
    Private Sub Class_Initialize()
        Err.Raise 5, mModuleName, AccessError(5) & "-Interface class must not be instantiated."
    End Sub
    

    IViewCommands:

    Option Compare Database
    Option Explicit
    
    Private Const mModuleName   As String = "IViewCommands"
    
    Public Sub DoSomething(ByVal arg1 As String, ByVal arg2 As Long):   End Sub
    
    Private Sub Class_Initialize()
        Err.Raise 5, mModuleName, AccessError(5) & "-Interface class must not be instantiated."
    End Sub
    

    ViewAdapter:

    Option Compare Database
    Option Explicit
    
    Private Const mModuleName   As String = "ViewAdapter"
    
    Public Event BeforeDoSomething(ByVal Data As Object, ByRef Cancel As Boolean)
    Public Event AfterDoSomething(ByVal Data As Object)
    
    Private mView       As IViewCommands
    
    Implements IViewCommands
    Implements IViewEvents
    
    Public Function Initialize(View As IViewCommands) As ViewAdapter
        Set mView = View
        Set Initialize = Me
    End Function
    
    Private Sub IViewCommands_DoSomething(ByVal arg1 As String, ByVal arg2 As Long)
        mView.DoSomething arg1, arg2
    End Sub
    
    Private Sub IViewEvents_OnBeforeDoSomething(ByVal Data As Object, ByRef Cancel As Boolean)
        RaiseEvent BeforeDoSomething(Data, Cancel)
    End Sub
    Private Sub IViewEvents_OnAfterDoSomething(ByVal Data As Object)
        RaiseEvent AfterDoSomething(Data)
    End Sub
    

    and Controller:

    Option Compare Database
    Option Explicit
    
    Private Const mModuleName       As String = "Controller"
    
    Private WithEvents mViewAdapter As ViewAdapter
    
    Private mData As Object
    
    Public Function Initialize(ViewAdapter As ViewAdapter) As Controller
        Set mViewAdapter = ViewAdapter
        Set Initialize = Me
    End Function
    
    Private Sub mViewAdapter_AfterDoSomething(ByVal Data As Object)
        ' Do stuff
    End Sub
    
    Private Sub mViewAdapter_BeforeDoSomething(ByVal Data As Object, ByRef Cancel As Boolean)
        Cancel = Data Is Nothing
    End Sub
    

    plus Standard Modules Constructors:

    Option Compare Database
    Option Explicit
    Option Private Module
    
    Private Const mModuleName   As String = "Constructors"
    
    Public Function NewViewAdapter(View As IViewCommands) As ViewAdapter
        With New ViewAdapter:   Set NewViewAdapter = .Initialize(View):         End With
    End Function
    
    Public Function NewController(ByVal ViewAdapter As ViewAdapter) As Controller
        With New Controller:    Set NewController = .Initialize(ViewAdapter):   End With
    End Function
    

    and MyApplication:

    Option Compare Database
    Option Explicit
    
    Private Const mModuleName   As String = "MyApplication"
    
    Private mController As Controller
    
    Public Function LaunchApp() As Long
        Dim frm As IViewCommands 
        ' Open and assign frm here as instance of a Form implementing 
        ' IViewCommands and raising events through the callback interface 
        ' IViewEvents. It requires an initialization method (or property 
        ' setter) that accepts an IViewEvents argument.
        Set mController = NewController(NewViewAdapter(frm))
    End Function
    

    Note how use of the Adapter Pattern combined with programming to interfaces results in a very flexible structure, where different Controller or View implementations can be substituted in at run time. Each Controller definition (in the case of different implementations being required) uses different instances of the same ViewAdapter implementation, as Dependency Injection is being used to delegate the event-source and command-sink for each instance at run time.

    The same pattern can be repeated to define the relationship between the Controller/Presenter/ViewModel and the Model, though implementing MVVM in COM can get rather tedious. I have found MVP or MVC is usually better suited for COM-based applications.

    A production implementation would also add proper error handling (at a minimum) to the extent supported by VBA, which I have only hinted at with the definition of the mModuleName constant in each module.

提交回复
热议问题