Implementation

[Previous] [Next]

Now that you know what a Bridge design pattern looks like, let's take a look at how to implement this design solution in Visual Basic. As I said in this chapter's "Scenario" section, a Bridge design pattern fits well in the framework paradigm by making the framework extensible through inheritance and through the composition of implementor objects. Before you start implementing anything, you should already know where the different participants will reside, which all depends on your perspective as a designer.

The interface (abstraction) expected by the client and the concrete bridge class (along with other concrete classes) reside in the same ActiveX component as each other. The implementor resides in the client process's address space. You should feel free to slice and dice this configuration any way you see fit. Your decisions will determine the extent to which you can use the different participants interchangeably. The source code implementation remains the same despite any physical partitioning.

On that note, to implement the abstraction, you create a class module that defines an abstract interface. This is the interface expected by clients. Because the interface is abstract, the implication is that the interface has no implementation of its own. So, all you do is define properties and methods with empty function bodies (no implementation), as shown in the code below. To prevent clients from creating instances of this interface, set the interface's Instancing property value to PublicNotCreatable using the Properties window in the Visual Basic integrated development environment (IDE).

 ' Class Name: Abstraction ' Note: This is an abstract interface expected by the client. ' The Instancing property must be set to PublicNotCreatable. Option Explicit ' Read-only property PropA returns a string value. Public Property Get PropA() As String End Property ' Method Func1 takes two Integer parameters and returns an Integer ' result. Public Function Func1(ByVal X As Integer, ByVal Y As Integer) _ As Integer End Function 

Next you want to define a concrete bridge class that inherits the abstract interface. The bridge class's implementation defines the events it associates with the properties and methods of the interface it inherited, which is the interface expected by the client. In response to requests made via this interface, the implementation fires a corresponding event, as shown here:

 ' Class Name: Bridge ' Note: This is a concrete class that inherits the Abstraction ' interface. Its purpose is to publish events that correspond ' to requests made via the Abstraction interface that it ' supports. Potential subscribers (implementors) can then ' execute implementation upon notification. ' Option Explicit ' Inherit the Abstraction interface. Implements Abstraction ' Define corresponding events for each property and method ' of the Abstraction interface. ' Public Event PropA(ByRef GetVal As String) Public Event Func1(ByVal X As Integer, _ ByVal Y As Integer, _ ByRef retVal As Integer) ' Bridge class concrete implementation for: ' ' Abstraction.PropA Private Property Get Abstraction_PropA() As String ' Publish corresponding event PropA. RaiseEvent PropA(Abstraction_PropA) End Property ' Abstraction.Func1 Private Function Abstraction_Func1(ByVal X As Integer, _ ByVal Y As Integer) As Integer ' Publish corresponding event Func1. RaiseEvent Func1(X, Y, Abstraction_Func1) End Function 

Visual Basic events are prototyped as subroutines; therefore, event handlers cannot return values. But an interface like Abstraction will typically contain properties and methods that return values. No need to worry, though; you can return values by passing parameters by reference. The content of parameters passed by reference can be changed from within the subroutine or function. This is an alternative means to the same end.

The Abstraction interface defines a read-only property, PropA, and a function, Func1, that return a String and an Integer, respectively. In the code extract above, for the event definitions in the Bridge class that correspond with PropA and Func1, I have labeled a specific parameter as ByRef followed by a parameter name that indicates the intended use.

To return a value from a function, you simply assign the value to the function name from within the function. The process is the same to return a value from a property procedure. In the Bridge class implementation, events corresponding to the function and the property procedure are published by passing along the names of the class's property and function. Event publication is synchronous; therefore, the RaiseEvent function will not return until all subscribing implementors have executed. You can then rely on the implementor to assign values to parameters passed by reference. These parameters correspond to the return values of the properties and functions of the Abstraction interface that is implemented by the Bridge class. The values are then returned to the client that made the request.

NOTE
The RaiseEvent function raises an event that has been declared within the same class module. Raising the event causes the event code to be executed.

After defining the Abstraction interface and the Bridge class, you need to define a concrete implementor class that provides an implementation for the Abstraction interface by subscribing to events published by a given bridge, as shown here:

 ' Class Name: Implementor ' Note: This is a concrete class that indirectly implements all ' properties and methods defined in the Abstraction interface. ' It connects to the Abstraction interface by subscribing to ' events published by a bridge that supports the interface for ' which this implementation is intended. ' ' Interface inheritance requires the class to implement all ' properties and methods of the interface it inherits. An ' implementor might provide partial implementation by ' subscribing only to events of interest. ' Option Explicit ' Declaring a Bridge object variable as follows will enable ' the Implementor object to subscribe to events published by the ' Bridge object to which it attaches. Private WithEvents theBridge As Bridge ' Attach Implementor to Bridge. Doing so will allow the ' Implementor object to subscribe to events published by the Bridge ' object. Public Sub Attach(Bridge_ As Bridge) Set theBridge = Bridge_ End Sub ' Detach Implementor from Bridge. This results in an implicit ' unsubscribe of all events published by the Bridge object. Public Sub Detach() Set theBridge = Nothing End Sub ' Implicitly subscribe to the events Func1 and PropA published by ' the Bridge class by defining the following event handlers ' respectively. ' ' Implementor.ImplicitSubscribe(Publisher:=theBridge, ' Event:=Func1, ' EventHandler:=theBridge_Func1) Private Sub theBridge_Func1(ByVal X As Integer, _ ByVal Y As Integer, _ retVal As Integer) retVal = X * Y End Sub ' Implementor.ImplicitSubscribe(Publisher:=theBridge, ' Event:=PropA, ' EventHandler:=theBridge_PropA) Private Sub theBridge_PropA(GetVal As String) GetVal = "Greetings from the Implementor object" End Sub 

The Implementor class must declare an object variable that will reference a Bridge object by using the keyword WithEvents. This will enable the Implementor object to subscribe to events published by a given Bridge object. If you were to type this code into a Visual Basic class module editor, you would notice an item named theBridge in the Object drop-down list in the editor. Selecting this item populates the Procedure drop-down list with the events published by the Bridge class.

When a client calls the Implementor.Attach method to obtain a reference to a Bridge object, an implicit subscription is registered with that instance of a Bridge. From a Visual Basic programmer's perspective, the comments above the Implementor class's event handlers in the preceding code loosely describe what Visual Basic does internally. Conceptually, this code is correct, but technically this is not legal Visual Basic code, nor is it completely accurate. Behind the scenes, Visual Basic applies COM's connectable-object technology. (See MSDN Library Visual Studio 6 online help for information on connectable objects.) As a Visual Basic programmer, you don't have to concern yourself with this level of detail. I mention it only to reiterate the fact that the magic of Visual Basic object-oriented programming depends heavily on COM.

Visual Basic automatically subscribes to events and will call the event handlers that are defined in the Implementor class module. If you don't want to subscribe to a specific event, don't define an event handler for it. Unsubscribing from events automatically occurs when the reference to the Bridge object publishing the events is released, as shown in the Implementor.Detach method in the preceding code. Also keep in mind that Visual Basic will automatically release the reference to the Bridge object when the Implementor object is destroyed. The Implementor.Detach method is useful when you want to unsubscribe from events, but keep a reference to the Implementor object around for future use in the client application. The important point is this: the client is responsible for maintaining references to both the Bridge and Implementor objects. Following is a code extract of a possible client implementation that clearly illustrates how to establish collaboration between all Bridge design pattern participants:

 ' This is a possible client (consumer) of the Bridge design pattern. ' Option Explicit Private Sub cmdClient_Click() Dim anAbstraction As Abstraction Dim aBridge As Bridge Dim anImplementor As Implementor Dim retVal As Integer Dim propVal As String ' Create an instance of a Bridge and ' assign a reference to the aBridge object variable. Set aBridge = New Bridge ' Using polymorphism, obtain an object reference ' to a Bridge object that supports the Abstraction interface. Set anAbstraction = aBridge ' Create an instance of an Implementor object and ' assign a reference to the anImplementor object variable. Set anImplementor = New Implementor ' Attach the Implementor object to the Bridge object. Doing so ' will allow the Implementor object to subscribe to events ' published by the Bridge object. anImplementor.Attach aBridge ' Because the anAbstraction object variable is currently ' referencing an instance of a Bridge that supports the ' Abstraction interface, all requests made via this interface ' result in the publication of a corresponding event. This ' event is handled by the currently attached subscriber, ' which, based on the previous source code, is the Implementor ' object referenced by the anImplementor object variable. ' ' Client->Abstraction.PropA->Bridge.RaiseEvent(PropA) ' ->Implementor.theBridge_PropA propVal = anAbstraction.PropA ' Client->Abstraction.Func1->Bridge.RaiseEvent(Func1) ' ->Implementor.theBridge_Func1 retVal = anAbstraction.Func1(5, 100) ' The following should appear in a message box: ' anAbstraction.PropA = Greetings from the Implementor ' anAbstraction.Func1(5,100) = 500 MsgBox "anAbstraction.PropA = " & propVal & vbCr & _ "anAbstraction.Func1(5,100) = " & retVal 



Microsoft Visual Basic Design Patterns
Microsoft Visual Basic Design Patterns (Microsoft Professional Series)
ISBN: B00006L567
EAN: N/A
Year: 2000
Pages: 148

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net