Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
613 views
in Technique[技术] by (71.8m points)

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

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 to make it as flexible and extendable as possible. To do this, I want to make use of MVC, Interfaces (2) (3) , Custom Collection Classes, Raising Events Using Custom Collection Classes, finding better ways to centralize and manage the events triggered by the controls on a form, and some additional VBA design patterns.

I anticipate that this project is going to get pretty hairy so I want to try to grok the limits and benefits of using interfaces and events together in VBA since they are the two main ways (I think) to really implement loose-coupling in VBA.

To start with, there is this question about an error raised when trying to use interfaces and events together in VBA. The answer states "Apparently Events are not allowed to be passed through an interface class into the concrete class like you want to using 'Implements'."

Then I found this statement in an answer on another forum: "In VBA6 we can only raise events declared in a class's default interface - we can't raise events declared in an Implemented interface."

Since I'm still groking interfaces and events (VBA is the first language I've really had a chance to try out OOP in a real-world setting, I know shudder), I can't quite work through in my mind what all this means for using events and interfaces together in VBA. It kinda sounds like you can use them both at the same time, and it kinda sounds like you can't. (For instance, I'm not sure what is meant above by "a class's default interface" vs "an Implemented interface.")

Can someone give me some basic examples of the real benefits and limitations of using Interfaces and Events together in VBA?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...