Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


'Making Reuse Fun' instead of 'Making Fun of Reuse' : Page 3

OK, so the COM team enabled us to develop great binary reusable components in DLLs and OCXs. Using all this functionality you develop this amazing component and now you're waiting for the developers to snap it up and start raving. You discover, however, that having a component that can be reused does not guarantee that it will in fact be reused. Amazing capabilities are overshadowed by ease of use and other trivial issues.


Easy Access to the System Class
When using Client from within another project in Visual Basic, Intellisense helps to create and set the properties in your system class.  It is also easy to maintain the state for the duration of the application as explained above.  When using Client from an ASP application, this becomes slightly more problematic.  Firstly, depending on the amount of hits you expect, it may or may not be a good idea to maintain an instance of ClientCO.clsSystem for the duration of the session.  Let's assume you want to keep your connection information in an Application Variable on IIS.  This works quite well, as you only need to fetch the connection information once at application startup.  Using the functionality provided by clsSystem, it is relatively easy as well, as you only need to instantiate an instance of clsSystem and copy the content of the ConnectionString property into an application variable.  (Note that there are some limitations on the kind of repository you can use, but more about that later)

When writing your ASP page, you need to instantiate an instance of ClientCO.clsSystem in order to set the connection string property, then you need to instantiate ClientCO.clsClient to load and work with your Client object.  While this may sound pretty straightforward, using "CreateObject" and having to create two classes in order to work with only one does get silly after a while, especially if you want to use four different components and now have to instantiate four system classes as well.  This prompted a change to classes such as clsClient that proved to be very useful for much more than just ASP development.  Every class in my CO project (except for clsSystem) gets a modular instance of clsSystem which is exposed as the property System, and loads itself on first reference.  This means that I add the following code to ClientCO.clsClient

Private mobjSystem as ClientCO.clsSystem

Public Property Get System() as ClientCO.clsSystem
  If mobjSystem Is Nothing Then Set mobjSystem = New ClientCO.clsSystem
  Set System = mobjSystem
End Property

In my ASP application I can now instantiate only an instance of clsClient, set the connectionstring and start using the functionality of clsClient.  VBScript code for this would typically look as follows:

  Set lobjClient = Server.CreateObject("ClientCO.clsSystem")
  lobjClient.System.ConnectionString = Application("ConnectionString")
  lobjClient.Load (....

  set lobjClient = Nothing

CO classes that needs access to information variables now also has easy OO access to these variables.

The System Class on BO Level
True to the nature of our 3 Tier design, clsSystem has it's BO counterpart called clsSystemSVR.  On BO level private modular variables is used to store configuration information.  As is the case on CO level, every object on BO level has a System property, which returns an instance of clsSystemSVR.  So, in a typical call from ClientCO to ClientBO, the state of clsSystem is packed into XML and unpacked into clsSystemSVR.  ClientBO uses the configuration information to connect to the database etc. A typical call from CO to BO uses the system class as follows:

/// CO level
Function Load (plngID as Long)
  lstrSystemXML = Me.System.Pack
  lobjBO.f_Load(pstrSystemXML as String, ....)
End Function

///BO level
Function f_Load (ByVal pstrSystemXML as String, ....)
  Me.System.Unpack pstrSystemXML
  f_Load = Load (...)
End Function

Function Load(..)
  lstrWhatever = Me.System.ConnectionString 

Note that the state of the system class is passed ByVal, as the BO level does not change information in the system class.  In some instances it is necessary to update information in the system class from BO to CO level.  Limit this as far as possible, as ByRef parameters adds an additional network roundtrip every time it is used. 

On BO level no global variables are used, plus no functionality exists to automatically fetch values as they are needed.  When you use (or reuse) a component on BO level, you must supply it with the necessary configuration information.  You have seen how the CO level supplies the BO level with this info.  An example of the BO supplying another BO level component is when you  expose a class B as a property of class A.  In the following code snippet, clsContractClientSVR exposes clsClientSVR as a property that will be loaded on first reference:

Public Property Get Client () as  ClientBO.clsClientSVR

  if mobjClient is Nothing Then
    set mobjClient = CreateObject("ClientBO.clsClientSVR")
    Call mobjClient.System.Unpack (Me.System.Pack)
    mobjClient.Load Me.ClientID
  End If
  Set Client = mobjClient

End Function

After creating an instance of mobjClient, the configuration information is passed on to it using the XML pack and unpack functions.  When the two objects are in the same project however, it is a much better option to simply execute

  Set mobjClient.System = Me.System

A note on why I choose to pass all the configuration information between BO and CO level with every call (and taking up bandwidth), as opposed to maintaining it centrally on the BO side:

The aim of the components is to be reusable, and I have no guarantee as to which system is using it, or to which database they it will be connecting.  Most of our clients run these components from multiple apps to multiple databases while using one COM+ box, making a central store difficult to maintain. Fetching this information on a call by call basis on BO level also imposes an overhead on the BO classes, while the CO classes can maintain state and thus only has to fetch this information once.  Passing this information into the SPM and managing it using some kind of key per CO instance can be used as an alternative to the way I use it. 

Objects developed in VB lives inside a Single Threaded Apartment (STA).  Global variables are scoped at STA level rather than process level, so they are available to all objects sharing an STA.  In a normal VB client application using DLLs, only one STA normally exists, so synchronization of global etc. is easy to manage and debug.  On a COM+ box each server package process maintains a pool of STA threads.  COM+ matches objects with STA threads in an arbitrary way, so you have no guarantee as to which clients will be sharing threads and  thus sharing globals.  This means that you should approach global variables on BO level with caution.  As far as Read/Write properties go, you cannot rely on a global variable to share data.  For Read/Write data that needs to be shared, use the Shared Property Manager (SPM). 

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date