Session E-PATT

Introduction to Object Oriented
Design Patterns in Visual FoxPro

Steven Black
SBC


Summary

Design patterns are a great way to categorize and convey design experience. This session, designed for object oriented novices, is a general overview of object-oriented design patterns. We’ll focus on a few compelling patterns, with an eye to recognizing and building them in VFP.

Introduction

A pattern is a recurring solution to a particular problem in a given context. A design pattern is a general abstraction of the solution across the range of problems in contexts where the pattern is appropriate.

Design patterns have names, and knowing (and agreeing) on their names and meanings leads to pattern language which is useful in conveying the abstractions among ourselves. The shared vocabulary makes patterns an excellent vehicle for conveying design experience.

Most catalogued software design patterns, when applied under appropriate conditions to solve the correct sort of problem, have known tendencies to be stable, scaleable, and coherent. These patterns are just now being identified, analyzed, and cataloged. When properly catalogued, design patterns define the parts, collaborations, and responsibilities of classes and instances used in a software subsystem. Thus they abstract the subsystem above the level of classes, instances, and code. Patterns are all about architectures, their component structures, and all their nuances.

Therefore patterns serve as a guide to the sensible design of software building blocks, and they help us better communicate and cope. Anyone who has used a napkin to sketch the workings of a software system knows that a picture is worth a thousand words.

A note on pattern form: There are many ways to communicate patterns, and there are even several conventions about how patterns should be conveyed on paper. In this document I am less concerned about following one or more particular conventions than I am about conveying how design patterns can be recognized and implemented. The task of presenting the full nature of a particular pattern is the job of pattern catalogs, a listing of which is found at the end of this document.

Notation

This document uses OMT notation. Here are a few important elements of this notation.

Figure 1. Abstract classes or abstract elements within classes have italic labels. Concrete classes use regular text.

Figure 2. Inheritance is symbolized with a triangle. In this case, the concrete classes inherit from the abstract class

Figure 3. The relationship type is determined by the arrow base, and cardinality is determined by the arrow head. Dashed lines usually imply creation.

Pattern: Bridge

Problem

To achieve reuse, some classes need to adapt either their implementations, their interfaces, or both. How to structure a class so that either (or both) can be easily modified?

Context

You are designing a new class, or adapting an existing class to make it more reusable. You anticipate future extensions to object's form or function and you want to avoid extending a class hierarchy for the sake of new implementations. Perhaps you'd like to choose a specific behavior at run-time.

Forces

Solution

Decouple an object's public interface ("form") from its implementation (its "function"), using two (or more) objects where otherwise one might less flexibly suffice.

Separate a class’s interface from its implementation by making each a separate but tightly coupled objects. This way each can be varied independently of the other.

The workings among the players in a Bridge should be direct and simple: the interface object forwards client requests to the appropriate implementation object. Tight coupling within the Bridge structure is both common and desirable. The tight coupling is much easier to manage when all the implementation objects come from the same class, as illustrated in the Codebook example illustrated below.

Resulting Context

A Bridge is a decoupling of an abstraction (interface) from its implementation so the two can vary independently. Bridges are scale-independent structures that apply in many object oriented situations. A Bridge also serves as an atomic element player in other design patterns.

Rationale

The usual way to manage the requirement for different interface or implementation is by extending the class hierarchy using inheritance. After all, inheritance is usually the first mechanism most folks use to reuse their classes. And why not? In the early going, inheritance is always a big success.

At the limit, however, inheritance leads to sclerosis - inheritance doesn't scale particularly well. Simple, single-object inheritance binds interface and implementation - you only have one package to transform into a subclass. This means that interface and implementation aspects cannot be independently varied without extending the class hierarchy.

But class hierarchies naturally lose their inherent adaptability with size and as the number and variety of clients increases. Why? - side effects, partly. The probability of side-effects in clients increases with the number and variety of those clients, and correspondingly with the current state of the hierarchy. In a giant class hierarchy, making a small change near the top can potentially be very expensive. This will occasionally limit what can be reasonably done at a given level in a class hierarchy.

The Bridge pattern mitigates class sclerosis by decoupling interfaces from implementations, and putting the abstraction and implementation in separate class hierarchies. This allows the interface and implementation classes to vary independently, and the inherent substitutability of subclasses makes the structure intrinsically adaptable (and hence reusable).

The Bridge in all this is the relationship between the interface and the implementation objects that together form a self-supporting system.

A.K.A.

"Handle And Body" and "Envelope And Letter". Those descriptive names are good - the dual-object nature of Bridge patterns is well conveyed, as is the tight coupling usually found between the programming interface and implementation objects.

Visual FoxPro Samples

Here we’ll look at three simple ways Bridge patterns can be built in FoxPro: First with member properties, second we’ll do more flexible implementations using member arrays, and third using object composition within containers.

Member Property Bridge

One way to build a Bridge is as diagrammed below. Here an interface member property contains a reference to an implementation object.

Figure 4. Bridge pattern where an interface class member property points to an implementation class. The Bridge in this diagram is .oImp, the member property that stores a reference to an implementation object – that's physically what the arrow represents. The benefit is this: each side can be subclassed independently of the other.

In general terms, here’s how such systems are constructed in VFP. What follows is a simple illustrative class that provides WAIT WINDOW services.

XX= CREATEOBJECT( "WaitMsgServer")
XX.Execute("This, for now, is in a WAIT WINDOW")

DEFINE CLASS WaitMsgServer AS MsgInterface
FUNCTION Init( txPassed)
*-- Load an interface object
THIS.aImp= CREATEOBJECT("WaitMsg")
ENDFUNC

FUNCTION Execute( txPassed)
*-- Pass the request along…
THIS.aImp.Execute( txPassed)
ENDFUNC
ENDDEFINE

DEFINE CLASS WaitMsg AS MsgImplementation
FUNCTION Show( txPara1)
WAIT WINDOW THIS.cMessage
ENDFUNC
ENDDEFINE

DEFINE CLASS MsgInterface AS CUSTOM
*-- Abstract message interface class
aImp= .NULL.
FUNCTION Execute( txPassed)
*-- Abstract
ENDFUNC
ENDDEFINE

DEFINE Class MsgImplementation AS Custom
*-- Abstract message implementation class
cMessage= ‘’
FUNCTION Execute( tcPassed)
THIS.cMessage=tcPassed
THIS.Show()
ENDFUNC

FUNCTION Show
*-- Abstract
ENDFUNC
ENDDEFINE

Sample 1. A simple Bridge using a member property to reference the implementation object.

Another example of this sort of Bridge is found in Codebook where the cBizobj (business object) class uses an object of the cDataBehavior class to implement the usual table navigation and record-processing functions. The following diagram illustrates this.

Figure 5. This Bridge pattern in Codebook 3.0 hangs on an aptly named member property. Any of the cDataBehavior subclasses may be substituted.

Member Array Bridge

Starting from the Member Property Bridge, it’s a short stretch to provide multiple simultaneous implementations by using multiple member properties or, as described below, member arrays.

Figure 6. A flexible BRIDGE: a selection of implementations stored in a member array.

Code sample 1 is modified here to support four different types of dialogs to extend the legacy WAIT WINDOW capability.

DEFINE CLASS WaitMsgServer AS MsgInterface
FUNCTION Init
*-- Load interface objects
THIS.aImp[1]= CREATEOBJECT("WaitMsg")
THIS.aImp[2]= CREATEOBJECT("RegularMsg")
THIS.aImp[3]= CREATEOBJECT("InfoMsg")
THIS.aImp[4]= CREATEOBJECT("WarningMsg")
THIS.aImp[5]= CREATEOBJECT("ErrorMsg")
*-- Supporting the legacy singular WaitMsg capability
THIS.oImp= aImp[1]
ENDFUNC

FUNCTION Execute( txPassed, tnMessageType)
THIS.aImp[tnMessageType].Execute( txPassed)
ENDFUNC
ENDDEFINE

DEFINE CLASS WaitMsg AS MsgImplementation
FUNCTION SHOW( txPara1)
WAIT WINDOW THIS.cMessage
ENDFUNC
ENDDEFINE

DEFINE CLASS RegularMsg AS MsgBoxImplementation
cTitle= "My Application"
ENDDEFINE

DEFINE CLASS InfoMsg AS RegularMsg
nIcon= 64
ENDDEFINE

DEFINE CLASS WarningMsg AS InfoMsg
nIcon= 48
ENDDEFINE

DEFINE CLASS ErrorMsg AS WarningMsg
nIcon= 16
nButtons= 5
ENDDEFINE

DEFINE CLASS MsgInterface AS CUSTOM
*-- Abstract message interface class
DIMENSION aImp[4]
aImp[1]= .NULL.
aImp[2]= .NULL.
aImp[3]= .NULL.
aImp[4]= .NULL.

*-- Virtual
FUNCTION Execute( txPassed)
ENDDEFINE

DEFINE CLASS MsgBoxImplementation AS MsgImplementation
nIcon= 0
nButtons= 0
FUNCTION Show
=MessageBox( THIS.ctext, THIS.nIcon+THIS.nButtons, THIS.cTitle)
ENDFUNC
ENDDEFINE

DEFINE Class MsgImplementation AS Custom
*-- Abstract message implementation class
cMessage= ‘’
FUNCTION Execute( tcPassed)
THIS.cMessage=tcPassed
THIS.show()
ENDFUNC
*-- Virtual
FUNCTION Show
ENDFUNC
ENDDEFINE

Sample 2. Many implementations can be connected to an interface array member property.

Containership Bridge

Containership implementations are similar to array member implementations. In Visual FoxPro, containership is superbly implemented, so containership Bridges are easy to create and manage. Useful are the Controls() array, the PARENT keyword, SetAll(), and AMEMBERS(,,2), which make it possible to manage containership nesting. The containership relationship is illustrated below, followed by an illustrative code example.

Figure 7. An interface container contains one or many implementations.

DEFINE CLASS MsgServer AS MsgInterface
FUNCTION Init
*-- Load an interface object with implementations.
*-- Exercise for the reader: Imagine delaying
*-- the AddObject calls until actually needed.
THIS.AddObject( "msgWaitWindow","WaitMsg")
THIS.AddObject( "msgRegular", "RegularMsg")
THIS.AddObject( "msgInfo", "InfoMsg")
THIS.AddObject( "msgWarning", "WarningMsg")
THIS.AddObject( "msgError", "ErrorMsg")
ENDFUNC

FUNCTION Execute( tnPassed, tcMessage)
*? I don't recommend doing it quite like this --
*? This simple example assumes you know the number of the
*? implementation object. In a perfect but (less concise)
*? example, .Execute(x,y) could accept an x of type "C", as in
*? <whatever_message_server>.Execute("Warning", <Message>)
*?
THIS.Controls(tnPassed).Execute(tcMessage)
ENDFUNC
ENDDEFINE

DEFINE CLASS WaitMsg AS MsgImplementation
FUNCTION SHOW( txPara1)
WAIT WINDOW THIS.cMessage
ENDFUNC
ENDDEFINE

DEFINE CLASS RegularMsg AS MsgBoxImplementation
cTitle= "My Application"
ENDDEFINE

DEFINE CLASS InfoMsg AS RegularMsg
nIcon= 64
ENDDEFINE

DEFINE CLASS WarningMsg AS InfoMsg
nIcon= 48
ENDDEFINE

DEFINE CLASS ErrorMsg AS WarningMsg
nIcon= 16
nButtons= 5
ENDDEFINE

DEFINE CLASS MsgInterface AS Container
*-- Abstract message interface class
*? Hardcoded dimension
DIMENSION aImp[4]
aImp[1]= .NULL.
aImp[2]= .NULL.
aImp[3]= .NULL.
aImp[4]= .NULL.

FUNCTION Execute( txPassed)
*-- Abstract method
RETURN 0
ENDDEFINE

DEFINE Class MsgImplementation AS Custom
*-- Abstract message implementation class
cMessage= ''
FUNCTION Execute( tcPassed)
THIS.cMessage=tcPassed
THIS.Show()
FUNCTION Show
*-- Abstract method
RETURN 0
ENDDEFINE

DEFINE CLASS MsgBoxImplementation AS MsgImplementation
nIcon= 0
nButtons= 0
FUNCTION Show
=MessageBox( THIS.ctext, THIS.nIcon+THIS.nButtons, THIS.cTitle)
ENDFUNC
ENDDEFINE

Sample 3. Multiple implementations instanciated in an interface container.

Pattern: Chain of Responsibility

Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.

Chain Of Responsibility patterns decouple a message sender from its receiver by giving many objects a chance to handle the message. The identity of the eventual service-providing object(s) may not be known to the sender, so the Chain Of Responsibility routes the request until it is adequately handled.

A good place to find Chain Of Responsibility patterns is within easily traversed hierarchies, like containership. With containership the logic is usually simple: "If an object can’t handle a request, then delegate it to its Parent". Help systems, among others, frequently use this pattern to invoke context-sensitive help. Chain of responsibility can be implemented without containership by using object pointer registration schemes.

Implementing a Chain Of Responsibility Pattern in FoxPro

Here’s a way to structure a Chain Of Responsibility: When in doubt, delegate to THIS.PARENT. In the example below, nested boxes delegate their Click() event upwards until it is handled. Don’t be fooled by all this code, for most of it just sets up the example. The only code you really need is in the Click() methods of the container classes. The code follows, starting on the next page.

xx=CREATE("Form1")
xx.Show()
READ EVENTS
DEFINE CLASS form1 AS form
Height = 300
Width = 448
Caption = "Form1"

FUNCTION INIT()
*-- Add six nested containers
THIS.AddObject( "FirstContainer", "ctnr")
*-- Style note: nested WITH...ENDWITH statements.
WITH THIS.FirstContainer
.AddObject( "SecondContainer", "ActiveCtnr") TAKE NOTE!

WITH .SecondContainer
.AddObject( "ThirdContainer", "ctnr")
WITH .ThirdContainer
.AddObject( "FourthContainer", "ctnr")

WITH .FourthContainer
.AddObject( "FifthContainer", "ctnr")
WITH .FifthContaine
.AddObject( "SixthContainer", "ctnr")
ENDWITH
ENDWITH
ENDWITH
ENDWITH
ENDWITH
ENDDEFINE

Sample 4. The code above generates nested containers of class Cntr as in the illustration below.

Figure 8. Nested containers. Here each bold-labeled container participates in the Click() events of all its inner members.

xx=CREATE("ChainOfResponsibility")
xx.Show()
READ EVENTS

DEFINE CLASS ChainOfResponsibility AS form
BackColor= RGB( 255,255,255)
Autocenter=.t.
Height = 300
Width = 448
Caption = "Containership Chain of Responsibility"

FUNCTION INIT()
*-- Add six nested containers
THIS.AddObject( "FirstContainer", "ctnr")
WITH THIS.FirstContainer
.AddObject( "SecondContainer", "Hotctnr")
WITH .SecondContainer
.AddObject( "ThirdContainer", "ctnr")
WITH .ThirdContainer
.AddObject( "FourthContainer", "HotCtnr")
WITH .FourthContainer
.AddObject( "FifthContainer", "ctnr")
WITH .FifthContainer
.AddObject( "SixthContainer", "HotCtnr")
WITH .SixthContainer
.AddObject( "SeventhContainer", "Ctnr")
ENDWITH
ENDWITH
ENDWITH
ENDWITH
ENDWITH
ENDWITH
ENDFUNC

FUNCTION CLICK( toTrash)
WAIT WIND "Form Click" TIME 1
ENDFUNC
FUNCTION DESTROY
CLEAR EVENTS
ENDFUNC
ENDDEFINE
*---------------------------------------

DEFINE CLASS ctnr AS container
Name = "ctnr"
FUNCTION INIT
*-- Container placement
THIS.BackColor=RGB(255,255,255)
THIS.Top = 20
THIS.Left = 20
THIS.Width = THIS.PARENT.Width-40
THIS.Height = THIS.PARENT.Height-40
THIS.AddObject("Ctnrlabel", "Ctnrlabel", THIS.Name)
THIS.CtnrLabel.Visible=.T.
THIS.BackStyle= 1
THIS.Visible= .T.
ENDFUNC

FUNCTION Click( )
*-- Raise the click()
IF TYPE( "THIS.Parent")= "O"
THIS.Parent.Click( )
ENDIF
ENDFUNC
ENDDEFINE

DEFINE CLASS HotCtnr AS Ctnr
BackColor = RGB(192,192,192) For your viewing pleasure
FUNCTION Init()
DODefault()
THIS.CtnrLabel.FontBold= .T.

FUNCTION Click( )
WAIT WIND PROPER(THIS.Name) + " is handing the request" TIME 1
*-- If the search must stop here, omit the following
IF TYPE( "THIS.Parent")= "O"
THIS.Parent.Click( )
ENDIF
ENDFUNC
ENDDEFINE

DEFINE CLASS CtnrLabel AS Label
Top=1
Left=2
FontSize=8
FontBold=.F.
Caption= "Ctnr"
BackStyle= 0
FUNCTION INIT( tclabel)
THIS.Caption= Proper(tcLabel)
ENDFUNC

FUNCTION CLICK( )
THIS.Parent.Click()
ENDFUNC
ENDDEFINE

Sample 5. Class definition for Ctnr and ActiveCtnr. Note that THIS.PARENT.DoHelp() is invoked upon no joy in THIS.DoHelp().

Other ways of implementing Chain Of Responsibility patterns

In our example we use PARENT referencing to algorithmically reckon the next successor in the chain. There are other ways to do it, including direct referencing, where each object keeps pointers to one or more successors. Assigning objects to these pointers must be managed, of course, and care must be taken to avoid both circularities and unhandled requests.

Chain Of Responsibility can also be built with a handler class that broadcasts the message to a sequence of objects until the message is satisfactorily handled. A variant of this uses object registration mechanisms to maintain the list and sequence of objects to be called.

You may need to pass parameters along the chain. This can be problematic if a variety of classes participate in the successor chain, especially if a handler is involved and the identity of the receiver classes isn’t known for certain. In this case, it’s most helpful to standardize message passing using objects instead of parameters. Like always, a mature and predictable programming interface helps.

Finally, it’s useful to note that a Chain Of Responsibility doesn’t need to end when a message is handled by a receiver in the chain. It may well be desirable for the message to be broadcast to many objects regardless.

Pattern: Observer

Observers define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Problem

When one object changes, you need to update dependents. But you feel uneasy about introducing dependencies among objects in code…

Context

Objects are not islands; they need to cooperate together.

Forces

Solution

Create new relationships wherein one object (the Observer) defines and manages the dependencies between other objects (its subjects) and triggers appropriate action based on the state of those subjects.

Resulting Context

Instead of knowing all the other subjects, a subject needs only know the Observer. This drastically reduces the overall coupling found within a system.

Rationale

Observer patterns work by providing a common interface and centralized dispatching for cooperative behavior. Observer objects typically query or respond to one or more of its subjects, and provide synchronization services (or initiate external synchronization services) according to the state of its subjects.Without an Observer object, the number of possible inter-object relationships grows dramatically as the number of interdependent objects in a system increases.

Variants

The Observer pattern, useful in and of itself, is the basis for a significant number of variants. One way to segment these variants is whether the observer is implemented using a "push" or "pull" protocol.

An active observer will pull the state of its subjects to them. This is usually done within an event loop or with a timer. The Voyeur pattern (page 13) is an example of this variant.

A passive (push) observer needs its subjects to send notification messages in order for the observer to perform its role. The Mediator pattern (page 12) is an example of this.

Pattern: Mediator

A Mediator object is a form of Observer that abstracts inter-workings of two or more objects. They are communication hubs that serve to decouple objects - objects don’t need to know about each other, they just need to know their Mediator. Mediator promotes loose coupling by keeping objects from referencing to each other explicitly, and lets you vary their interaction independently.

Look for Mediator patterns where objects are decoupled and in situations when inter-object behavior variation is present or expected. Mediators well serve situations where complex protocols must be managed, and when centralized access points are desirable.

Mediator is a behavioral pattern. When we speak of Mediator, it’s a system of at least three objects messaging in a star topology.

Figure 9. The Mediator provides a common connection point, centralized (and subclassable) behavior and behavior management, all with a common interface.

Sample Mediator implementations in Visual FoxPro

Following diagram illustrates a Mediator found in Y. Alan Griver's Visual FoxPro Codebook. In Codebook, toolbar navigation buttons call a business object class (cBizObj) which mediates in a one-way direction between the form (of class cBizObjForm) and the data behavior object (of class cDataBehavior). This simplifies the programming interface - only the cBizObj class protocols need be known - and allows for the easy substitution of the data behavior implementations from the cDatabehavior class. Note that several BRIDGE patterns are evident here separating programming interfaces from implementations.

Figure 10. Hitting the Next button in Codebook. The cBizObj class mediates between the form and the desired behavior.

Pattern: Voyeur

Voyeur patterns are observers that react to exposed members without the knowledge or consent of the event participants. Voyeurs decouple the viewed objects by encapsulating part, or all, of the system’s behavior into a controlling object.

A good place to find a Voyeur pattern is at the boundary between two incompatible, incomplete, or low-cohesive systems. Voyeur activity is usually triggered by timers, or alternatively from within regularly repeating event loops.

Voyeur, like Observer, is a behavioral pattern. It is similar to Observer except that subjects are passive - they don’t actively notify their Observer - and they don’t need to know any details about the Voyeur’s existence.

Implementing Voyeur patterns in FoxPro

A good example of a Voyeur is Ken Levy’s SuperCls utility. SuperCls is a timer-activated toolbar whose function is to provide class information services while editing class methods. It works as follows: The timer polls WONTOP() and fires the toolbar.Show() and toolbar.Hide() methods as appropriate.

Figure 11. Ken Levy’s SuperCls.

Here’s another example of a voyeur, wherein a timer regularly polls attributes and sets properties accordingly. Note that the subjects themselves have no code related to the function of the form. In this case, the form reports the position and distance of the mouse relative to its origin.

As it so happens, using a Voyeur implementation is cleaner than many other ways of doing it. Alternatives, which typically involve using the various MouseOver() methods to refresh the display, is less practical because:1) we cannot easily control the refresh rate, 2) we need to code many MouseOver() events because visible objects eclipse form's view of the mouse, 3) thus most objects need to "know" about the context of this implementation or, alternatively, know a mediator to accomplish the chore, and 4) rigged as it is, all the code is in a single place - the observer itself.

XX= CREATEOBJECT( "FORM1")
XX.SHOW()
READ EVENTS

DEFINE CLASS form1 AS form
Height = 223
Width = 372
DoCreate = .T.
BackColor= RGB(192,192,192)
Caption = "Voyeur"

ADD OBJECT timer1 AS timer WITH ;
Top = 13, Left = 9, ;
Interval = 100, Name = "Timer1"

ADD OBJECT command1 AS commandbutton WITH ;
Top = 161, Left = 140, ;
Height = 29, Width = 94, Caption = "Cancel"

ADD OBJECT RowDisplay AS VoyeurTextBox WITH Top = 40
ADD OBJECT ColDisplay AS VoyeurTextBox WITH Top = 68
ADD OBJECT DistDisplay AS VoyeurTextBox WITH Top = 96
ADD OBJECT label1 AS VoyeurLabel WITH ;
Caption="Mouse row",Top= 43
ADD OBJECT label2 AS VoyeurLabel WITH ;
Caption="Mouse column",Top=71
ADD OBJECT label3 AS VoyeurLabel WITH ;
Caption="Distance to origin",Top=99

*-- Here is the voyeur part…
PROCEDURE timer1.Timer
thisform.RowDisplay.Value = PADL(MROW(),6)
thisform.ColDisplay.Value = PADL(MCOL(),6)
thisform.DistDisplay.Value= IIF(MROW()>0 AND MCOL()>0,;
PADL((MCOL()^2+MROW()^2)^(1/2),6),"Unknown")
ENDPROC

PROCEDURE Destroy
Clear EVENTS

PROCEDURE command1.Click
thisform.Release()

ENDDEFINE

DEFINE CLASS VoyeurTextBox AS textbox
Alignment = 1
Height = 24
MousePointer = 1
Left = 160
ReadOnly = .T.
Width = 79
FontBold = .T.
ENDDEFINE
DEFINE CLASS VoyeurLabel AS LABEL
Alignment = 1
BackColor = RGB(192,192,192)
Height = 18
Left = 30
Width = 100
ENDDEFINE

Sample 6. A VOYEUR - an unknown and unacknowledged observer.

Figure 12. The Voyeur form in action. As the mouse moves, the display values are automatically updated.

What to expect from Voyeur patterns

Voyeurs are great for dynamically linking two uncooperating objects. For timer-based VOYEURS, expect overhead loading proportionate to their number, firing frequency, code executed and, as usual, the Timer() event will occasionally get in the way when trace is running.

Pattern: Allocator

In a good program, every acquired resource must be released at the appropriate point. Temporary environment state changes also should be restored when appropriate. Things that need to be released or restored include tables, locks on records and tables, work areas, index orders, object references (or instance counters), SET settings, streaming output files, print jobs, collation sequences, etc.

The Allocator pattern is often found in C++, and is easily implemented in VFP. It is a very low-level coding pattern, one that involves just one object, and is easy to understand and apply. The idea behind Allocator is to use an object's Destroy() method to assure resource deallocation and environment resetting, even in the presence of errors. As soon as the object goes out of scope, it cleans up after itself before ceasing to exist.

For example, how many times have you done something like this?

*-- Remember the old workarea
LOCAL lnOldSelect
lnOldSelect= SELECT()
SELECT Customer
* << Customer stuff goes here >>
SELECT (lnOldSelect) BOOKMARK "A"

The same thing can be accomplished very cleanly with a class!

*-- Remember the old workarea
LOCAL loOldSelect
loOldSelect= CREATE( "SaveSelect", "Customer")
* << Customer stuff goes here >>
lnOldSelect= .NULL. This line often not necessary.
*-------------------------------

DEFINE CLASS SaveSelect AS Line Line is lightweight!!
LnOldArea=0
FUNCTION INIT( tcAlias)
THIS.lnOldArea= SELECT()
IF ! EMPTY( tcAlias)
SELECT (tcAlias)
ENDIF
FUNCTION DESTROY
SELECT (THIS.lnOldArea) BOOKMARK "B"
ENDDEFINE

Well, at this point it doesn't look any easier to do it with a class, but consider that allocating and releasing resources can be abstracted to cover all sorts of resource situations, and the need arises constantly, often with multiple resources for each instance, and we have the potential for real savings. Two points in particular to keep in mind:

  1. The code marked BOOKMARK "A" above is not guaranteed to execute. Your routine may contain a mid-structure RETURN statement, or an error might occur that prematurely branches you out of the routine. In the case of a a muffed selected work area, it's probably not a federal case, but consider that the allocated resource may be a file lock, or a file open with FOPEN(), or a communications task, or an on-line connection to an Oracle back-end... The code marked BOOKMARK "B", however, will execute (guaranteed) as soon as the object is explicitly released or as soon as it goes out of scope, which is often exactly what we want!
  2. Often resources are allocated in a program, and it's extremely convenient to have the resource deallocated when the program goes out of scope. For example, using the examples above, usage could be reduced to:

    *-- Remember the old workarea
    LOCAL loOldSelect
    loOldSelect= CREATE( "SaveSelect", "Customer")
    << Customer stuff goes here >>
    *---- End of routine or end of file: loOldSelect goes out of scope.

Implementing Allocators

You could define a class for each sort of resource you'll eventually allocate or, more pragmatically, you could let someone do the work for you. Included on the conference CD-ROM is a program called ENVLIB.PRG, written by our great friend Tom Rettig. Here's a more typical application for an Allocator pattern, where several sorts of things need to be saved at once. First, here is a conventional way of remembering the state of a work area:

*-- Remember the state of the old workarea
LOCAL lnOldSelect, lcOldOrder, lnOldRecNo, lcOldFilter
lnOldSelect= SELECT()
SELECT CUSTOMER
lnOldRecNo= IIF(EOF(), .NULL., RECNO())
lcOldOrder= ORDER()
lcOldFilter= FILTER()
<< Your stuff goes here >>
IF FILTER()<> lcOldFilter AND ! EMPTY( lcOldFilter)
SET FILTER TO lcOldFilter
ENDIF
IF ORDER()<> lcOldOrder AND ! EMPTY( lcOldOrder)
SET FILTER TO (lcOldOrder)
ENDIF
IF !ISNULL( lnOldRecNo) AND RECNO() <> lnOldRecno
GO lnOldRecno
ENDIF
SELECT( lnOldSelect)

Sample 7. Enough overhead? For bonus points: What are some potential problems with the code above?
Answer: We don't save relation and buffering information, and whether the index is ascending or descending.
To tighten this up, we need more code.

Here is what that code reduces to using a class called SaveTable found in Rettig's EnvLib class library:

LOCAL loSaveTable
loSaveTable= CREATE("SaveTable")
<< Your stuff goes here >>
*-- The next statement may not be required if
*-- loSaveTable goes out of scope at the correct time
loSaveTable= .NULL.

Sample 8. Maintainable enough?

Tom's EnvLib library has many interesting hierarchies. I leave it to you to explore the source code to see how Allocators for FoxPro resources can be elegantly abstracted and subclassed.

Summary of Selected Reading and Resources

Books and Articles

Alexander,C., et al. A Pattern Language. New York: Oxford University Press, 1977. ISBN 0-19-501919-9

Alexander,C., et al. A Timeless Way of Building. New York: Oxford University Press, 1978.

Black, S. (1995) Pattern Implementation in VFP, European Visual FoxPro Developers Conference '95 session notes, E-PATT, Frankfurt, Germany. And also: GLGDW '95 session notes, #30, Milwaukee, WI.

Brooks, F, (1975 and 1995) The Mythical Man Month.. Reading, MA. Addison Wesley. ISBN 0-201-83595-9.

Buschman, F, and Meunier, R (1995) A System of Patterns. Chapter 17 (p.325) in a compilation work by Coplien, J, and Schmidt, D (1995) Pattern Languages of Program Design. Reading, MA. Addison Wesley.
ISBN 0-201-60734-4.

Buschman, F, Meunier, R, Rohnert, H, Sommerlad, P, and Stal, M (1996) A System of Patterns. West Sussex, England. John Wiley Sons. ISBN 0-471-95869-7.

Coad P, North, D, and Mayfield, M, (1995) Object Models: Strategies, Patterns, and Applications. Prentice Hall. ISBN 0-13-108614-6. Also a helpfile: STPTHLP.ZIP in CIS.CaseForum, Lib 3

Coplien, J. Advanced C++ Programming Styles and Idioms. Reading, MA: Addison Wesley, 1992.

Coplien, J, and Schmidt, D (1995) Pattern Languages of Program Design. Reading, MA. Addison Wesley.
ISBN 0-201-60734-4.

Gamma, E., Helm, Observations on Observer, Dr. Dobb’s Sourcebook, September/October 1995.

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

Hay, D (1995) Data Model Patterns: Conventions of Thought. Dorset House.

Helm,R, and Gamma, E. The Courrier Pattern. Dr .Dobb's Sourcebook Jan/Feb 1996. Miller Friedman Inc,
San Francisco.

Juancarlo Anez. (1995) A Perfect Fit. Windows Tech Journal, September 1995, Oakley Publishing Company, Springfield OR.

Pree, W (1995) Design Patterns for Object Oriented Development. Reading, MA. Addison Wesley and ACM press. ISBN 0-201-42294-8.

Rohnert, Hans, PLOP 96 Conference Proceedings, Addison Wesley (1996)

Vlissides, John, Seven Habits of Successful Pattern Writers, C++ Report, Vol 7 no 9, November-December 1995, SIGS publications, New York NY.

Some interesting internet sites:

http://www/state.sd.us/people/colin/fox_page.htm

The Cunningham and Cunningham inc homepage, http://c2.com has a description of pattern history, a description of the Portland pattern form, guidelines for writing patterns in Alexander's style, and several patterns and pattern languages and links to other sites.

The patterns homepage: http://st-www.cs.uiuc.edu/users/patterns/patterns.html Maintained by Richard Helm.

Conferences:

PLOP ’96, "Pattern Languages of Programs", Sept 4-6 1996 in Allerton Park Il. http://www.cpl.uiuc.edu/~plop/

Author’s note: Portions of this document have appeared elsewhere, notably in FoxTalk Magazine, and in the abstracts of regional Visual FoxPro conferences in Canada, the United States, and Europe.