Welcome To The Home Of The Visual FoxPro Experts  
home. signup. forum. archives. search. google. articles. downloads. faq. members. weblogs. file info. rss. print.
HOOKED ON OBJECTS, PART 1

There are many techniques in the world of OOP. Most of them are all based on the principles of code re-use and separation of user interface and business rules. It is my, maybe not so humble opinion, that a good framework show use these techniques in some extend. In this article I will show you how to use the technique of the chain-of-responsibility, and tell you the advantages of this technique.

The philosophy
The idea behind the chain-of-responsibility is that the responsibility for triggering code is done by the main object and that the actual code is stored in objects that are in a attached to each other. In this sample I will take a project hook class that has two subclasses, the “master-hook” and the “child-hook”. The master-hook is created once and is sub-classed only once in the sub-master class. That is the project-hook that you will attach to the project.


It’s sole responsibility is to instantiate classes of project-hooks that will do things for you, nothing more, nothing less. The master-hook class has 9 non-default methods, one is a custom method, and two custom properties. The methods and properties are listed in the following table:

PEM – NameDescriptionAfterBuild (M) Method, fired after the building of a project BeforeBuild (M) Method, fired before the building of a project Init (M) Assumed to be well known QueryAddFile (M) Fires just BEFORE a file is added to the project QueryModifyFile (M) Fires BEFORE a file is modified QueryNewFile (M) Triggers by pressing the NEW button in the projectmanager QueryRemoveFile (M) Triggers before a file is removed from the project. QueryRunFile (M) Triggers before a file is executed, printed or previewed. LoadHooks (M) Custom method, loads the hooks that are in the chooks property oHook (P) A placeholder for the first instantiated hook of the child-hook classcHooks (P) A comma delimited list, looking like: Classlib.class1,classlib.class2, classlib.class3,…

The child-hook has 7 non-default methods, 1 custom method and 1 custom property. They are listed below:

PEM – NameDescriptionAfterBuild (M) Method, fired after the building of a project BeforeBuild (M) Method, fired before the building of a project pjHookInit (M) Called from the master-hook. Code can be used for doing additional things in the instance of the sub-classed hook. QueryAddFile (M) Fires just BEFORE a file is added to the project QueryModifyFile (M) Fires BEFORE a file is modified QueryNewFile (M) Triggers by pressing the NEW button in the projectmanager QueryRemoveFile (M) Triggers before a file is removed from the project. QueryRunFile (M) Triggers before a file is executed, printed or previewed. AddHook (M) Custom method, it receives as a parameter a reference to an object and places that in the chain of objects. oHook (P) A placeholder for the instantiated hook passed in the AddHook method.

A Closer Look
The masterhook has in the init the following code:

this.loadhooks()
this.ohook.pjHookInit(_vfp.ActiveProject, this)

The last line in this code is not explained any further here. It is the place where you could create other objects like toolbars specific for that object et cetera.

The call to the loadhooks is used to instantiate all the subclasses of child-hook.
This is what the code looks like:

* I made it a good habit to use different lines for different types of local variables.
* A tip from Eric den Doop learned me not to use the AS clause yet (introduced in VFP7), this makes code 
* available for developers working in VFP 5 and 6 as well.
LOCAL lcHooks, lcInfo, lcClassLib, lcClass
LOCAL lnI, lnRuns
LOCAL loProjHook

* place the chooks property in a variable
lcHooks = this.cHooks

* only fire when not empty
IF NOT EMPTY( lcHooks )
    * make sure the chooks string end on a comma.
    IF RIGHT( lcHooks,1) <> ","
        lcHooks = lcHooks + ","
    ENDIF
    * The last comma is needed to get the number of runs in the for loop
    lnRuns = OCCURS(",",lcHooks)
    * read the string
    FOR lnI = 1 TO lnRuns
        lnEnd = AT(",",lcHooks)
        * get the first “classlib.class” string
        lcInfo = SUBSTR( lcHook,1 ,lnEnd )
        lcInfo = STRTRAN(lcInfo,",","")
        * get the classlib and the class.
        lcClassLib = JUSTSTEM( lcInfo)
        lcClass = JUSTEXT(lcInfo)
        * create an object of it
        loProjHook = NEWOBJECT(lcClass, lcClassLib)
        * only subclasses of child-hook are allowed
        IF UPPER(loProjHook.baseclass) = = "PROJECTHOOK" AND ; 
                UPPER(loProjHook.parentclass) = ="CHILDHOOK"
            * add the object to the chain.
            IF ISNULL( this.ohook )
                * if oHook of this class is still NULL add (a reference of) the object to this class
                this.ohook = loProjHook
            ELSE
                * otherwise add (a reference of) the created object to the existing object.
                this.oHook.AddHook( loProjHook)
            ENDIF
        ENDIF
        * break down the lcHooks string
        lcHooks = SUBSTR( lcHooks, lnEnd+1)
    ENDFOR
ENDIF


What happens in the above code is as follows


The cHooks property is read and placed in a variable. The first “classlib.class” string is read from the total string and broken down into the classlib and class string with the juststem() and justext() functions. Those are the items needed to create a NEWOBJECT(). When the object is created the class itself checks whether the oHook property is still NULL. If such is the case the object is placed there. Actually a reference to the object is then placed in that property. If there is a reference to an object already in that property we make a call to the addhook() method of that object, the parameter is the object just created. (more correctly, a reference to the object named loProjHook)

AddHook
The child-hook has an AddHook method. The parameter is the loProjHook object-reference. This is the code:

LPARAMETERS toChildHook

IF ISNULL( this.oHook )
    this.oHook = toChildHook
ELSE
    this.oHook.addhook( toChildHook )
ENDIF

Simple and straightforward. When the oHook property is null the object-reference is placed there, if it filled already the call to the addhook method of the attached object is called with the loProjHook as parameter. In this way a chain of objects, referencing each other is created.

An example
As an example I will show, in this article, the QueryAddFile() method from the master-hook class. It will not fire any other code than that in the child-hook. Therefore we have to call that method explicitly. The parameter is passed by default. Create one such method and you will get this from VFP by default.

Master-hook code

LPARAMETERS tcFileName

Next we call the oHook.QueryAddFile method with the parameter received by default.

this.ohook.QueryAddFile(tcFileName)

Child-hook code
In the first line the parameter is received.

LPARAMETERS tcFileName

We look whether there is an objects attached to the oHook property. If such is the case we will fire that code.

IF NOT ISNULL( this.ohook )
    this.ohook.QueryAddFile( tcFileName )
ENDIF
RETURN

Implementation
The other methods (QueryNewFile, AfterBuild, BeforeBuild et cetera) are created similarly. The whole point now is how to implement this technique. Here are the steps to follow. I will stay with the QueryAddFile from our example.

Subclass the child-hook class. In the QueryAddFile of the subclass place the following code:

* Fire the default code first with parameter.
DODEFAULT( tcFilename)
* your code to execute.

OR, and this works the other way around:

* write your code to execute first.
* fire the other code next.
return dodefault( tcFileName)


Example
Seeing what happens makes things clearer. I subclass the child-hook into two classes, chld2 and chld3. In the QueryAddFile from Chld2 I place the following code:

LPARAMETERS tcFileName
NODEFAULT
WAIT WINDOW "You just added "+tcFileName
RETURN DODEFAULT( tcFileName )

Notice that the “LPARAMETER tcFileName” line is added to the code automagically. First I issue a NODEFAULT, giving me better control over my own code. The wait window statement needs no further explanation. The last line returns a DODEFAULT() with the parameter.

In the chld3 class the code in the QueryAddFile looks like this.

LPARAMETERS tcFileName
NODEFAULT
DODEFAULT( tcFileName)
WAIT WINDOW "Are you sure you want this?"
RETURN

The LPARAMETER is passed automatically again. The NODEFAULT gives better control. Then I FIRST call a dodefault(), if I had any code following the chld3.queryaddfile then that code would have fired first. Then I issue the wait window and returns the control to the project.

Firing this particular event will give the following result when I add a file to the project. First the chld2 code is fired:



Then the chld3 code is fired:



Let’s modify the code.
But only slightly! In the QueryAddFile from Chld2 I modify the code as follows:

LPARAMETERS tcFileName
NODEFAULT
DODEFAULT( tcFileName )
WAIT WINDOW "You just added "+tcFileName
RETURN 

And in the chld3 I modify the code like:

LPARAMETERS tcFileName
NODEFAULT
WAIT WINDOW "Are you sure you want this?"
RETURN DODEFAULT( tcFileName)

Let’s see what happens now. I click the Add button on the projectmanager interface. I first get this:



and then I get:



Just the other way around!

A Simple Test
As said I placed the controls in a “chain”. So let’s make this chain visible. Knowing, in this particular case, that there are only two classes added to the projecthook we can try the following:

?_vfp.ActiveProject.name

This returns “d:\testdir\projtest.pjx” being the drive, folder and the full name of the project.

?_vfp.ActiveProject.ProjectHook.name

This returns “submaster” being the name of the project-hook I attached to the project.

?_vfp.ActiveProject.ProjectHook.ohook.name

This returns “chld2” being the class name of the first project-hook I added to submaster.

?_vfp.ActiveProject.ProjectHook.ohook.ohook.name

This returns “chld3” being the class name of the second project-hook I added to submaster. It is, as you can see in the above code, attached to the chld2 projecthook.

?VARTYPE(_vfp.ActiveProject.ProjectHook.ohook.ohook.ohook) && "X"
?TYPE("_vfp.ActiveProject.ProjectHook.ohook.ohook.ohook") && "U"
?ISNULL(_vfp.ActiveProject.ProjectHook.ohook.ohook.ohook) && .T.

The last three lines show that the oHook from chld3 is still .NULL. and the type is unknown. Testing these same lines on _vfp.activeproject.projecthook.ohook.ohook return “O”, “O”, and .F. subsequently. Showing once more that a true chain of objects is created.

Epilogue
The example given here about the chain-of-responsibility is fairly simple. I used a projecthook here, as I have created several of those nifty hooks over time and all of them are just as useful to me, I wouldn’t do without any of them. Switching between them would be too time-consuming so I did a rewrite on them using the above sample making them ALL available at once. The same idea behind the above example can be used in other controls as well of course. I have such code for validation of textbox-values and everything else you can think of. I divided the code also for behavior of forms and controls. The behavior is divided in databehavior, formbehavior, mousebehavior et cetera. All I have to do is place the names of the classes, controlling the behavior and businessrules, in one property. Being a lazy programmer I created several builders for this. I can fill the cBehavior property with a few clicks of the mouse.

In the given example I show that with the chain-of-responsibility I can not only divide between user interface and business rules and thus re-use the User-Interface controls over and over again, but I also have a good control over the moment the code is fired. A second advantage is that I can make maximum use of the fieldmapping possibility in VFP.

All this really speeds up development. You design the looks of the forms first and then you start thinking about the implementation of the business rules as outlined in the functional analysis (You do use that of course……. SAY YES, SAY YES!!)

Think of the possibilities
Create a class that instatiates toolbars and add it to a form, you can simply create that toolbar once and re-use it over and over again. Create a class that opens a menu when the class is instantiated and removes it when the class is destroyed. Create a class that makes your forms only showing data (preventing editing first) and making editing possible when the user explicitly clicks an edit-button or through the menu sets the form mode to edit mode. Check the contents of an editbox with word spelling checker. Just to mention only a few possibilities. And all this with the click of a mouse. Creating your own behavior classes is simple following the rules given above thus giving you the possibility to extend a framework to unimaginable heights.

Have fun, Happy Foxiting!

Download code
Click here to download the code that is discussed in this article. The download is a zipfile. Its size is 4.588 bytes.

ABOUT THE AUTHOR: BOUDEWIJN LUTGERINK

Boudewijn Lutgerink Programming is one of the many hobbies of Boudewijn. He has worked with computers since 1985 and is the author of two books from Sybex. He has a weblog at http://weblogs.foxite.com/boudewijnlutgerink.

FEEDBACK


Your Name: 
Your Feedback: 

Spam Protection:
Enter the code shown: