Home   Portfolio   Resources   Publications   Research   News   Contact   About   

Article

Decorating for the Busy Developer

Lauren Clarke

Decorating or "wrapping" an object represents a flexible alternative to subclassing. This article explains what a decorator is, demonstrates a novel way to implement the pattern in Visual FoxPro, and provides some application ideas.

If you are like me, your eyes tend to glaze over a bit when object oriented design pattern terms are thrown about. The word "decorator" is one of those terms, and due in part to the image of Martha Stewart it immediately conjures up, and the complete lack of tasteful decorations in most programmer's lairs, it leaves one wondering what decorations and programming have to do with each other. Decorator (a noun) is a design pattern that uses aggregation (another OOP term, which means one object holds a reference to another) to "wrap" an object and perhaps provide some additional features not present in the wrapped object. Simply put, a decorator is a class that provides a "skin" around another object to which one wishes to add new responsibilities at runtime. A simple example follows:

Consider the object that SCATTER NAME creates in VFP.

SCATTER NAME loData

This line of code creates an object with properties that correspond to the fields of the table in the current workarea, and property values corresponding to the values of those fields. This process represents a nice lightweight alternative to assigning the properties of a custom data object from the fields of a table. However, wouldn't it be nice if loData could do something other than store field values? It'd be nice if it had methods you could use in your day-to-day life as a developer working with this object. For example, what if it could validate itself, or render itself as an XML string, or save itself? The trouble is we don't have access to the internal Visual FoxPro class used to create loData. We can't create subclasses of it, and even if we could, we couldn't force SCATTER NAME to use our subclass instead of the base class. So we have an object that we would like to endow, that is, decorate with new abilities and we can't (or don't want to) create a subclass to deal with these new responsibilities. So,we are going to implement a method of extending the abilities of the SCATTER NAME object at runtime. Enter the decorator pattern.

DEFINE CLASS DecoCust1 AS Relation




*-- data properties

cFName = 
'
'

cLName=
'
'

cPhone= 
'
'

nCredLine = 
'
'




*--- other properties

oData = NULL

cFullName = 
'
'



FUNCTION cFName_Assign( tcValue)

THIS.oData.cFName=tcValue



FUNCTION cFName_Access()

RETURN This.oData.cFName



FUNCTION cPhone_Assign( tcValue)

THIS.oData.cPhone=tcValue



FUNCTION cPhone_Access()

RETURN This.oData.cPhone



FUNCTION cLName_Assign( tcValue)

This.oData.cLName=tcValue



FUNCTION clName_Access()

RETURN This.oData.cLName



FUNCTION nCredLine_Assign( tcValue)

This.oData.nCredLine=tcValue



FUNCTION nCredLine_Access()

RETURN This.oData.nCredLine



FUNCTION cFullName_Assign( tcValue)

LOCAL lnPos

lnPos = AT( 
" 
",tcValue)

This.oData.cFname = LEFT(tcValue,lnPos)

This.oData.cLname = Substr(This.oData.cFname,lnpos,50)



FUNCTION cFullName_Access()

RETURN This.oData.cFname+
" 
"+This.odata.cLname



FUNCTION INIT( toData)

This.oData = toData



FUNCTION isValid()


*-- a validation code goes here


*-- see downloads file for code

FUNCTION toXML()


*-- a xml string building code goes here


*-- see downloads file for code

FUNCTION Save()

GATHER This.oData

FUNCTION DESTROY()

This.oData = NULL

ENDDEFINE

Well, that was a bunch of work (see below for a way to avoid most of it). Let's go over the highlights of this class. First, note that we have a property for each field in our customer table. Also, we have a property oData which will hold a reference to our SCATTER NAME generated data object. Finally, we have a property "cFullName" which will provide the customer's full name based on the cFname and cLname fields. Then, we have a bevy of access and assign methods that serve to link the oData properties to the decorator's properties. With this construct in place, the following expressions will both return the same value:
oDeco.odata.cLName

oDeco.cLName

The access and assign approach ties us to version 6.0 or later of VFP. The alternative is to use GetProp() and SetProp() methods to provide access to the wrapped object. For example, we could have a GetFname() function that would return oData.cFname, but this really makes the wrapper less than transparent which is not desirable. Finally, note that the INIT() method takes as a parameter a reference to the data object to be wrapped by the class. Ok, let's put our decorator to work.
We'll dissect at the following code snippet line by line.
SCATTER NAME loData BLANK

loData.cFName = 
"Mellow
"

loData.cLname = 
"Yellow
"

loData = CREATE(
"DecoCust1
",loData)

If loData.IsValid()

  APPEND BLANK

  loData.Save()

ELSE

...

First, we create a standard SCATTER NAME object. The BLANK keyword indicates the object will be created with empty properties.
SCATTER NAME loData BLANK

Next, we assign our new object a first and last name.
loData.cFName = 
"Mellow
"

loData.cLname = 
"Yellow
"

Nothing spectacular so far, but this next line is the point at which the rabbit goes into the hat.
loData = CREATE(
"DecoCust1
",loData)

Here we instantiate a class DecoCust1, the init function of this class takes an object as a parameter, and we send our SCATTER NAME generated loData object for this parameter. DecoCust1 wraps itself around loData and augments its abilities. Note that we re-use the loData variable name to store a pointer to our new instance of DecoCust1. This is not necessary, but since DecoCust1 will seamlessly wrap our data object, presenting all of its original properties as if it were the old loData, it is natural to do so. In our case, DecoCust1 has an IsValid() method and we can use this to validate Mr Yellow.
If loData.IsValid()
...

If we pass this test, a record is added to the current table and we fire the Save() method which will save the record to the table.

APPEND BLANK

  loData.Save()

ELSE

...

Now that the object has been created, and is decorated with additional properties and methods, we can check our record with IsValid(), save it with Save(), render it to XML with ToXML(), and all the while we can still reference the objects' original properties
? loData.cFName 
&& prints 
"Mellow
"

? loData.cLName 
&& prints 
"Yellow
"

And we also have an additional property calculated from underlying fields.
? loData.cFullName 
&& prints 
"Mellow Yellow
"

To summarize, we have a class DecoCust1, which adds a cFullName property, IsValid() , Save(), and toXML() methods (and potentially many other useful methods) to our SCATTER NAME generated data object. From the developer's standpoint, there is very little practical difference between the wrapper and a bone-fide subclass of the Visual FoxPro data object class. The key point of course is that you can't subclass Visual FoxPro's data object class. Also note that these abilities were added at run-time, meaning we have the option of adding these abilities if and when they are needed without instantiating a large feature-ridden class each time a record object is needed.
Another real-world sample of a decorator, csZIP/csUnZip is provided in available in this month's Subscriber Downloads at http://www.pinpub.com/foxtalk . It decorates the popular DynaZip utility to provide some simplifications and extensions that are helpful when using the utility from within VFP.

Subclasses vs Decorators

Decorators offer an alternative to subclassing, but they must be used judiciously. The advantages come from the fact that you can gain subclass-like behavior for classes you cannot actually subclass. Also, you are given more flexibility at runtime to endow your objects with abilities only if they need it to fulfill the current task. In our example, there may be many places in an application where the base FoxPro data object will suffice. This being the case, it would be a shame to have to instantiate and use a complicated and expensive class where the lightweight class would do. Decorators offer "pay as you go" options where one can add functionality as needed. It is possible to nest decorators inside decorators. If we decided that mixing the validation code and the XML rendering code in one class made it too large and inflexible, we could create a separate decorator for each task. For example, we could start with the basic data object:
SCATTER NAME loData BLANK

Then, if necessary endow it with the ability to validate its properties
loData = CREATE(
"ValidateDataDeco
",loData)

loData.ValidateProperty(
"cFname
")

Then, if needed, we could decorate again and gain some rendering functionality
loData = CREATE(
"RenderDataDeco
",loData)

Which would allow us to do things like:
RESPONSE.Write(loData.toHTML())

Or
RESPONSE.Write(loData.toXML()).

And, unless you need to validate and render your data object every time you use it, you can save a lot of overhead by avoiding the giant do-everything data class.

The following lists summarize the pros and cons.

Advantages of decorators:

  1. Allows extension of classes we can't directly subclass
  2. Avoids deep class hierarchies
  3. Provides a pay-as-you-go option which avoids instantiating large feature-ridden classes when few of the features are needed

Disadvantages of decorators:

  1. Pass-though code must be maintained
  2. Passing requests through to the decorated component requires a performance hit
  3. Can complicate debugging

From the developer's perspective, the single most important issue here is the maintenance of pass-through code. The access and assign code must be maintained in concert with the wrapped object. In our example this means each time the structure of the table changes, we've got some work to do in our decorator class definition. This issue is exacerbated when we are wrapping classes that have methods as well as properties as we have to write pass-through code for each method. In short, if the interface of the wrapped object changes, so must the wrapper. Until recently, this fact is enough to really cool one's feet to the idea of using the decorator in anything but the most dire situations. However, version 6.0 of Visual FoxPro gives us an opportunity to generalize decorator classes and completely eliminate this fragile use-case specific pass-through code. Our rescue comes in the form of the THIS_ACCESS method.

THIS_ACCESS Overview

THIS_ACCESS is a method that can be added to any subclass in VFP. This method will fire every time the class is accessed. This means that every time a property is set or accessed or a method is called, the THIS_ACCESS method will fire prior to that action taking place. THIS_ACCESS takes as a parameter the name of the member being accessed. (Side note: It is too bad THIS_ACCESS only takes the called member as a parameter, if one could also access the value being sent (in the case of a property assignment) or the parameters being sent (in the case of a method call) inside the THIS_ACCESS method, it would open a world of possibilities, but that is off topic for this article) THIS_ACCESS must also return a reference to the object being accessed. It is this last requirement that we leverage to implement an almost codeless delegation scheme for a generic decorator class. Let's re-do our example above using this new approach.

Redecorating with THIS_ACCESS

Here's what the class definition for DecoCust might look like when we utilize THIS_ACCESS
DEFINE CLASS DecoCust2 as RELATION

oData = NULL



FUNCTION INIT( toData)


*-- add parameter checking here

THIS.oData = toData

ENDFUNC



FUNCTION THIS_ACCESS( tcMember)

IF PEMSTATUS(THIS,UPPER(tcMember),5)

  RETURN THIS

ELSE

  RETURN THIS.oData

ENDIF

ENDFUNC



FUNCTION DESTROY()

  THIS.oData = NULL

ENDDEFINE



FUNCTION isValid()


*-- validation code goes here



FUNCTION toXML()


*-- xml string building code goes here



ENDDEFINE

That's it. Notice the substantial reduction in lines of code from our previous DecoCust1 example. The "big idea" here is the THIS_ACCESS method which first checks to see if the requested member belongs to the decorator class, and if not, a reference to the wrapped data object is returned. This way, the decorator can decorate by adding functionality, like this IsValid() while forwarding requests for the oData properties directly to the oData object.

Also, notice the DecoCust2 class above is very generic. The IsValid() and toXML() methods could be removed and we'd have a nice BaseDeco class to wrap any component which we could subclass to add things like IsValid() for specific implementations.

Paying the piper

Wrapping a class has some costs both in development and run-time. The development costs come from the need to keep the interface of the wrapper synchronized with the interface of the wrapped component. If you choose to manually maintain the interface, this can be a costly proposition -especially if the wrapped class is changing often. Using the THIS_ACCESS trick above can vastly reduce your development load, as the interface will be updated automatically. However, since THIS_ACCESS fires each time the object is used there is a run-time cost to be paid for this approach. The following table will give you an idea of the runtime costs for these different approaches.

TaskSubclassDeco1Deco2
Instantiation11.812.66
Access Decorator Property10.959.90
Assign Decorator Property12.2011.34
Call Decorator Method10.964.48
Access Decorated Property10.966.81
Assign Decorated Property11.145.37
Call Decorated Method12.154.88
KEY:
Subclass = no decorator, subclass only.
Deco1 = a hardwired decorator with explicit pass-through code
Deco2 = a decorator implemented with THIS_ACCESS.
This table has been normalized to be machine independent and more readable. For each task, the "Subclass" option has been given a weight of 1 and the others scaled accordingly. So, for example, Deco2 takes 9.90 times longer to access a property than a traditional subclass. To get actual time values for your system, just run the perfcheck.prg provided in the free source code (See the Download Files section below).

Some of these factors look pretty alarming, but keep in mind the times we are talking about here. My system, a PIII limping along at 500mhz, takes 0.000006 seconds to access a property from Deco1, and a staggering 0.00003 to access the same property through a decorator using THIS_ACCESS (Deco2). In a real use-case, say a middle-tier data object, an application might access a data object 100 times to serve a user's request. In this situation, the THIS_ACCESS method represents a cost of no more than 0.003 seconds in our benchmark classes. Considering the THIS_ACCESS method may eliminate hundreds of lines of high-maintenance pass-through code this might represent a good tradeoff, however these results do make one pause to consider carefully where to implement these techniques.

WITH/ENDWITH and GATHER gotchas

If you plan on using a THIS_ACCESS decorated class in a WITH/ENDWITH loop, you'll be in for a surprise. VFP exhibits some peculiar behavior in this area. In a recent issue of FoxTalk, Randy Pearson wrote an article on the advantages of the WITH/ENDWITH command. It is likely you will want to use this construct with a decorated class at some point. The trouble is, VFP won't let you. The following code will not fire the THIS_ACCESS method of loData and will result in an error.

SCATTER NAME loData BLANK

loData = CREATE(
"DecoCust1
",loData)



WITH loData

  .cFname = 
"Billy
"

  .cLName=
"Bob
"

  .cPhone=
"123.456.7890
"

ENDWITH

A workaround is to reference the decorated component directly in the WITH construct:
WITH loData.oData

  .cFname = 
"Billy
"

  .cLName=
"Bob
"

  .cPhone=
"123.456.7890
"

ENDWITH

There are some differences in the behavior here between VFP6 and VFP7 Beta 1. Both are odd and not really consistent with the documentation on access and assign methods. There is a program in the download file you can use to explore the differences.

In addition to this, the GATHER NAME command works fine with the property-level access methods, it seems to ignore the THIS_ACCESS method at this time.

Conclusion

The decorator patter offers a nice alternative to subclassing. The THIS_ACCESS method of building decorators allows us to avoid writing reams of pass-through code when building decorators in VFP. This convenience comes with a performance price but in many situations I think the price is more than justified. I'll leave you with one possibly interesting diversion. Look up "multiple inheritance" in a good general OOP reference. Then, take a look at our DecoCust2 class above, and consider the possibility of aggregating more than one object at a time and replacing the IF/THEN in the INIT() clause with a CASE statement. Bon voyage!

Lauren thanks the http://fox.wikis.com community for their help in refining and testing some of the ideas presented in this article.

References:

Gamma, E., Helm, R., Johnson, R, and Vlissides, J. (1994), Design Patterns, Elements of Object Oriented Software, Addison Wesley, Reading, MA, ISBN 0201633612.

Malak, Michael "Simulating Multiple Inheritance", Journal of Object-Oriented
Programming, April 2001 Vol 13 No 12 p 3-5

Download Files

Click here to download all the related files (code samples etc.) for this article free of charge. If you found this article useful and wish to make a donation, please visit our donation policy page.

Lauren Clarke [lauren@cornerstonenw.com] is president of CornerStone Systems Northwest, a firm that builds Internet and Intranet applications with an emphasis on the public and private scientific research community. He has worked remotely for over five years.


Article Details  
Title: Decorating for the Busy Developer
Author: Lauren Clarke
Published: December 01, 2001
Last modified: December 01, 2001
Published in: FoxTalk
Other Pages of Interest
SVG and VFP Introduction Whitepaper
Parsing Power Tools, Part 1
Parsing Power Tools, Part 2
Parsing Power Tools, Part 3
Parsing Power Tools, Part 4
Collab. Part I: Source Control
Collab. Part II: Wiki Tech. for Teams
Decorating for the Busy Developer
Static Content in a Dynamic world
Benford's Law: A filter for fraud

Fast Facts

Decorating or "wrapping" an object represents a flexible alternative to subclassing. This article explains what a decorator is, demonstrates a novel way to implement the pattern in Visual FoxPro, and provides some application ideas.

Download Files

Click here to download all the related files (code samples etc.) for this article free of charge. If you found this article useful and wish to make a donation, please visit our donation policy page.
 Home   Portfolio   Resources   Publications   Research   News   Contact   About   
Copyright © Cornerstone Systems Northwest Inc. 1999 - 2008