Flylib.com

Books Software

 
 
 

Microsoft Visual Basic Design Patterns (Microsoft Professional Series) - page 51

[Previous] [Next]

COMments

As in Visual Basic, passing a COM object by value in C++ is not a built-in language feature but is a feature the majority of the COM programming community would like to see added to the Microsoft COM specification. I hope it will happen soon and, since Visual Basic object-oriented programming is deeply rooted in COM, the feature will eventually find its way into Visual Basic. In the interim, the Object By Value design pattern can allow you to implement this feature today.

[Previous] [Next]

Related Patterns

The Object By Value design pattern, similar to the Prototypical Object Factory (Chapter 9), permits an object to be cloned. However, the Object By Value design pattern can also pass the clone to a recipient across process boundaries. Similar to the Smart Proxy (Chapter 7), the recipient is then referencing an object in its own process address space, thus avoiding the overhead of cross-process invocation. Unlike the Smart Proxy, however, all object invocations are performed within the local copy, whereas a Smart Proxy can intelligently decide when to delegate requests back to the original object across process boundaries.

[Previous] [Next]

Chapter 7

Smart Proxy

What can you surmise from this scenario?

Ms. Smart Proxy says, "I accept this award on behalf of Bill Stamatakis, who could not be here tonight because he is on a business trip in Tokyo. Bill wanted me to relay his sincerest gratitude for this gesture and to thank the following people for their contributions that lead to this award…"

Based on this excerpt, you can guess that Ms. Smart Proxy represented Bill Stamatakis at an awards ceremony by accepting a prize intended for him, and communicated his appreciation to the audience. Hence, the expected outcome was satisfied when Bill's name was called to receive his award at the podium. If Bill knew he would be unavailable to attend the ceremony but chose not to ask someone to fill in for him or did not notify the members of the ceremony committee, the presenter would have been in an awkward predicament.

Ms. Smart Proxy's role was therefore important in keeping the award presentations flowing smoothly. One can easily imagine other scenarios in which a system would break down if the actions associated with a specific individual were not carried out by the individual, or by a representative of that individual, also known as a proxy. According to Dictionary.com, the definition of a proxy is "The person who is substituted or deputed to act or vote for another."

Replacing the word "person" with "object" allows us to view the concept of a proxy from the perspective of object-oriented software development.

[Previous] [Next]

Purpose

The Smart Proxy design pattern provides a reference to a surrogate object that represents a real object in a given context.

[Previous] [Next]

Utilization

The Smart Proxy design pattern is useful in the following circumstances:

  • If you want to provide transparent access with the most optimal performance to the real object, which does not reside in the same address space as the client thread that initiated the request
  • If you want to regulate access to the real object by requiring the client to have specific rights to invoke the properties and methods of the real object
  • If you want to defer creation of the real object until it's needed
[Previous] [Next]

Scenario

To understand when to employ the Smart Proxy design pattern, consider an example of the first situation described in the "Utilization" section. An out-of-process COM server (ActiveX EXE) is running on a server connected to your company network. Microsoft Visual Basic clients running on workstations connected to the network bind to objects on this server through the COM layer. Because the client and server processes do not reside in the same process address space, a proxy to the real object on the server is automatically returned to the client. The proxy delegates the client's request across the network to the real object. The source code for the client remains the same whether the object being referenced is in-process (ActiveX DLL) or out-of-process (ActiveX EXE), but performance degradation will be evident in an out-of-process scenario. Therefore, the least amount of interaction with the server, the better.

Typically an object's state and behavior are represented as properties and methods in an interface. For example, this code extract illustrates the steps to construct and initialize a Person object.

Dimperson1AsHRComponent.Person

'ConstructaPersonobjectandretainareferencetotheperson1
'objectvariable.
Setperson1=NewPerson

'InitializethestateofthePersonobject.
Withperson1
.Name="Smith"
.Age=32
.Height="5-10"
EndWith

If HRComponent is an ActiveX DLL, the cost of setting each property is minimal because the Person object resides in the client process's address space. The interface of the Person object clearly is self-documenting because the properties distinctly define what the Person object supports. This is good programming style from the perspective of object-oriented design.

If HRComponent is an ActiveX EXE, however, the cost of setting each property value is considerably higher. The Person object does not reside in the client process's address space, so each property assignment is individually delegated across the network to the real object running on the ActiveX EXE server. The object variable person1 has a reference to a proxy provided internally by Visual Basic. This proxy transparently packages and submits each request to the real object. Furthermore, if the code were to request a property value (such as strName = person1.Name ), the proxy would forward this request across the network to the real object because the proxy is stateless by default. The proxy's sole purpose is to forward requests to the object that it represents.

You can solve this out-of-process performance problem in two ways: you can modify the interface to support setting a series of properties in one call, or you can employ the Smart Proxy design pattern. Let's examine both approaches.

A common and effective solution is to create an array of name-value pairs, which you can do by creating a two-dimensional array of type Variant, as shown in the following code extract. The first row of the array is the header containing the names . All the following rows contain the values for each name. Performance is superior to the individual property-setting approach, but this method of implementation doesn't provide a true object-oriented solution. The interface supported by the Person object is no longer self-documenting. Debugging and understanding the code becomes significantly more difficult.

Dimperson1AsHRComponent.Person
Dimattribs(0To1,0To2)AsVariant

'ConstructaPersonobjectandreturnareferencetotheperson1
'objectvariable.
Setperson1=NewPerson

'SetupattributesforsubmissiontoPersonobject:
'
'Initializeheaderofnames.
attribs(0,0)="Name"
attribs(0,1)="Age"
attribs(0,2)="Height"

'Initializevaluespername.
attribs(1,0)="Smith"
attribs(1,1)=32
attribs(1,2)="5-10"

'SendUpdateStaterequesttoPersonobject.
Callperson1.UpdateState(attribs)
{% if main.adsdop %}{% include 'adsenceinline.tpl' %}{% endif %}

It might be worth sacrificing the principles of object orientation in order to increase performance. Implementing the Smart Proxy design pattern, however, allows you to stick to your principles and at the same time gain the much-needed performance.

A smart proxy is "smart" because, unlike a "dumb" proxy that just forwards all requests to the real object, the smart proxy can decide when it is necessary to contact the real object. For instance, the smart proxy can accumulate and package a series of requests into a compound structure that is submitted to the real object in one call across the wire. Also, a smart proxy can cache the real object's state locally in the client process. Requesting the property value of a smart proxy returns the results as quickly as if the client were accessing the real object in-process. A Smart Proxy design pattern is really a class that wraps the dumb proxy provided within Visual Basic. (See Chapter 4.) The Smart Proxy design pattern in essence implements the same Person interface that both the dumb proxy and real object implement. (See Figure 7-1.) The Smart Proxy design pattern therefore remains transparent to the client without compromising performance or object-oriented principles.

In the Smart Proxy design pattern implementation, you can choose to use a two-dimensional array of type Variant similar to the non-object-oriented solution described previously. Since it is an internal implementation, it would remain private to the class. Setting a property value would update the two-dimensional array defined internally. When you've updated the properties in the smart proxy, follow through with a call to a method that submits all property settings as a single request with an accompanying array. The SmartProxyPerson class submits requests to the dumb proxy that is automatically generated by Visual Basic. For simplicity, let's assume the SmartProxyPerson class is creating an instance and holding onto a reference to the dumb proxy in its Class_Initialize event handler, relieving the client of that task. (The "Implementation" section of this chapter covers the possible implementation options in more detail.) Let's also assume the state is cached locally. When the client requests a property value, it is returned from the SmartProxyPerson object that is in-process. The code extract below is a feasible scenario for updating multiple property values of the real Person object as a single request.

click to view at full size.

Figure 7-1. Smart Proxy design pattern implementation of the Person class.

Dimperson1AsHRComponent.SmartProxyPerson

'ConstructaSmartProxyPersonobjectandreturnareferencetothe
'person1objectvariable.
'
'Duringinitialization,SmartProxyPersonconstructsadumbproxy
'Personobjecttowhichitultimatelydelegatesallrequests
'intendedfortherealPersonobjectthatresidesoutsidethe
'addressspaceoftheclientinitiatingtherequest.
'
Setperson1=NewSmartProxyPerson

'Setthepropertyvalues.
person1.Name="Smith"
person1.Age=32
person1.Height="5-10"

'Updatethestateoftherealpersonobject.
person1.SetComplete