VHP Computing Group. Home Page.  
TechnologiesSpecialistsDownloadLinksSign GuestbookView Guestbook

December, 2002
Vladimir Trukhin
Senior Software Engineer
JSC "Votkinsk Hydroelectric Power Plant"
Fax: +7 (34241) 63297
E-mail:
vlt@votges.ru

This article was first published in the March, 2002 issue of FoxTalk

Manager of the Objects Linked by Common Data

The objects know all about themselves, but they know little about their neighbors in an application. However, as Vladimir Trukhin shows us here, each of them can affect other objects. The application needs something to notify the objects about a change of environment—the Manager of the Objects.

You can create a form and connect it with data. What could be easier? It's very easy, especially in Visual FoxPro. Furthermore, you can create many forms, good and different. Each of them, as a rule, displays the data of some table or, more often, several tables.

Is it possible that some forms display the same data? Certainly—this frequently the case. Besides, the forms can have private data sessions. If you load some such forms and make changes in one of them, you won't see the change in other forms until you've made them via some mouse or keyboard manipulations. The situation becomes even worse if you create and start classes distinct from forms. They can be invisible to you, and you can't get to them by mouse. What's the solution?

Someone should take command
Yes, "someone" should do it. In this case, there's some ambiguity, which can misinform you and lead to incorrect results. I've named this "someone" the Manager of Objects. It can exist during the entire operating time of the application, while there will be objects linked by common data. And so, I create an instance of the Manager, in this case as a public variable to make the example easier to follow:

PUBLIC goDLOM && where DLOM is Data Linked Objects Manager
 goDLOM=CREATEOBJECT('DataLinkedObjectManager')
 

Now it's ready to command, but does the Manager know what objects work with the same data set? No, it doesn't. The Manager won't know anything about these objects if they don't declare anything about themselves. Each object, when it's created, should be registered in the Manager. It's most convenient to have this happen in the Init method of the created object:

PROCEDURE Init
 goDLOM.AddClient(THIS)
 ENDPROC
 

When the Manager knows all the participants of the game, it can serve as the referee. How can it know what's taken place with one object or another? The Manager knows nothing about objects, and it can't determine what happens with them. An object can know only about itself.

In changing the data, the object should inform the Manager about it, and the Manager, in turn, must inform all other objects of this event, excepting the hero of the occasion. Usually, for this purpose I use the object's SomeControlLostFocus method and the object's DataSetIsChanged property, which confirms the real change of a data set.

PROCEDURE SomeControlLostFocus
 IF THIS.DataSetIsChanged
   * … updating code …
   goDLOM.UpdateAllClients(THIS)
 ENDIF
 

Who will refresh the data?
It's high time to ask, "Can the Manager update the data of objects?" Each participant of the game should have its own method, which is named RefreshData, to update its data. In this method of an object, the logic of reception of the fresh data will be latent. Upon receiving the notification from an active object, the Manager will just need to call this method for all objects.

What happens if someone is too tired to play?
The objects don't exist eternally, and they're sometimes unloaded from memory. The Manager should be notified about such a sad fact. The object must do so itself, before it has died. The most suitable place for this to occur is in the Unload method of the object:

PROCEDURE Unload
 goDLOM.RemoveClient(THIS)
 ENDPROC
 

How many nuts in a pocket?
If someone wants to find out the number of participants in the game, it can ask the Manager, using the NumberOfClients property.

Definition of a Manager class
Now that all of the conditions have been determined, it's time to generate the list of properties and methods of a Manager class (see Table 1).

Table 1. Properties and methods of the Manager class.

Name of member

Type

Description

AddClient

Method

Adds a new object to client list.

NumberOfClients

Property

Gives the number of registered clients.

RemoveClient

Method

Removes the specified object from the client list.

UpdateAllClients

Method

Updates the data sets of all registered clients.

In my applications I use the following definition of the class:

*-- Class:             DataLinkedObjectManager
 *-- ParentClass:       Custom
 *-- BaseClass:         Custom
 
 DEFINE CLASS DataLinkedObjectManager AS custom
 Name='DataLinkedObjectManager'
 
 ** The list of clients to be driven by this manager
 DECLARE CLIENTS(1)
 
 ** Number of registered clients
 NumberOfClients=0
 
   PROCEDURE NumberOfClients_access
     LOCAL lnNumberOfClients
     IF ALEN(THIS.Clients)=1 AND ISNULL(THIS.Clients(1))
       lnNumberOfClients=0
     ELSE
       lnNumberOfClients=ALEN(THIS.Clients)
     ENDIF
   RETURN lnNumberOfClients
   ENDPROC
 
   PROCEDURE NumberOfClients_assign
     LPARAMETERS vNewVal
     RETURN
   ENDPROC
   
   PROCEDURE Init
     THIS.Clients(1)=NULL
   ENDPROC
   
   PROCEDURE Release
     RELEASE THIS
   ENDPROC
   
   ** Adds a new client into client list
   PROCEDURE AddClient
     LPARAMETERS loReference
     IF THIS.NumberOfClients=0
       THIS.Clients(1)=loReference
     ELSE
       DIMENSION THIS.Clients(THIS.NumberOfClients+1)
       THIS.Clients(THIS.NumberOfClients)=loReference
     ENDIF
   ENDPROC
   
   ** Removes a specified client from client list
   PROCEDURE RemoveClient
     LPARAMETERS loReference
     LOCAL lnElement, llSuccess, lnIndex
     lnElement=0
     FOR lnIndex=1 to THIS.NumberOfClients
       IF THIS.Clients(lnIndex)=loReference
         lnElement=lnIndex
         EXIT
       ENDIF
     ENDFOR
     IF lnElement!=0
       IF THIS.NumberOfClients=1
         THIS.Clients(1)=NULL
       ELSE
         ADEL(THIS.Clients,lnElement)
         DIMENSION THIS.Clients(THIS.NumberOfClients-1)
       ENDIF
       llSuccess=.T.
     ELSE
       llSuccess=.F.
     ENDIF
     RETURN llSuccess
   ENDPROC  
   
   ** Updates data sets of all registered clients  
   PROCEDURE UpdateAllClients
     LPARAMETERS loPushingObject
     LOCAL loCurrentObject
     IF THIS.NumberOfClients > 0
       FOR EACH loCurrentObject IN THIS.Clients
         IF loCurrentObject != loPushingObject
           loCurrentObject.RefreshData()
         ENDIF
       ENDFOR
     ENDIF
   ENDPROC
   
 ENDDEFINE
 

Conclusion
Most applications aren't limited to one data set. Therefore, for each such set it's possible to create an instance of the Manager class. Besides, some objects can be registered in several Managers, if their data crosses several data sets of the application.

You can download the class source code from file: dlom.zip. Size: 1KB.


Home Page | Technologies | Specialists | Download | Links | Sign Guestbook | View Guestbook