[ 1 ] [ 2 ] [ 3 ] [ 4

Structure

A number of hook operation types are possible, depending on the desired effect.

Internally fulfilled hooks: When hooked objects handle fulfill their hooks by instance programming or by subclassing, the structure illustrated in Figure 3 is typical. This classic structure is well known and is the basis for the Template Method design pattern as described by Gamma, Helm et al in Design Patterns.

Externally fulfilled hooks:  If hook operations delegate to external hook implementations, this leads to many possible variants. Two examples: Figure 4 shows a case where the hook objects come from separate hook classes, and Figure 5 shows a case where the hook class is a superclass of the hooked class.

Figure 3: What I call "self-sufficient hooks" have a structure similar to that of the Template Method design pattern. Here you use inheritance or instance programming to modify the default behavior.

Figure 4: Supporting hooks are separate hook objects, here shown as separate classes from those they adorn.  

Figure 5: Hooked objects and their hooks can share the same interface, which implies that they can share the same class hierarchy. This is very handy from a maintenance perspective since the interfaces remain automatically in synch.

Figure 5 deserves explanation because it is counterintuitive and admittedly convoluted. One would think that a hookable class couldn’t possibly be from the hook parentclass.  But it works great! Here's why: hookable objects use the hook’s public interface. If hookable and hook share the same interface, then the situation is nicely polymorphic and easily maintained since all native hook behavior can be ensconced in the hook parentclass, abstracted nicely away and above the concrete implementation classes.

As a mental exercise, assume that a hooked class is-a hook. This means instances could freely use each other as hooks in hook operations. So hooked objects call hooks, and hook and hooked objects are hooks, meaning they can be chained together, all on the same interface.

Structure: Hook sequencing

Assuming we are free to sprinkle hook operations throughout our events and methods, how to best place them? To help answer this, here are our placement options relative to the native stream of program control.

Pre-process hooks are invoked before the usual procedure code. Pre-process hooks are especially powerful if they can override or control subsequent built-in behavior. 

Post-process hooks are invoked after the usual procedure code. Unlike pre-process hooks, post-process hooks cannot control the execution of the invoking procedure.

In-process hooks are invoked in mid-procedure. These can be very useful, especially when used within monolithic procedures.

Participants

There are two participants in the hook operation pattern. Note that the pattern can be extended, and one participant may play both roles.

Hooked Object
– Contains template method(s) each containing one or more hook operations.

Hook Object
– Not necessarily distinct from the hooked object
– Implements the hook

Collaborations

The hooked object invokes hooks through hook operations.

Hook objects may themselves be hooks.

Consequences

The advantages of hook operations include:

Some disadvantages are

Implementation

This section briefly shows some ways hook operations can be implemented in Visual FoxPro.

1. A simple pre-process hook

Listing 3 shows a simple call to a pre-process hook. This example uses a common convention of naming the pre-process with the prefix „pre“, and similar hook operations are found in a number of VFP frameworks.

FUNCTION Click
  *-- Pre-process hook
  THIS.PreClick()
  WAIT WINDOW „Click“
  <100 lines of code>
ENDFUNC

Listing 3. A pre-hook followed by standard method code.

2. A simple post-process hook

Listing 4 shows a simple post-processing hook. This example uses a common convention of naming the pre-process with the prefix „post“

FUNCTION Click
  WAIT WINDOW „Click“
  <100 lines of code>
  *-- Post-process hook
  THIS.PostClick()
ENDFUNC

Listing 4. A post-process Click hook.

3. Combined simple pre and post-process hooks

As you may have guessed, pre and post-hooks can be combined, as shown in listing 5, in an attempt to achieve flexibility on both sides of the standard call.  This is not recommended!

FUNCTION Click
  THIS.oHook.PreClick()  && Pre-hook
  WAIT WINDOW „Click“
  <100 lines of code>
  THIS.oHook.PostClick() && Post-hook
ENDFUNC

Listing 5. Pre and post-hooks surround a standard method. If you intend this method to be subclassed, then avoid this!

However you do it, combining pre and post-hooks, as in listing 5, is not recommended if the method is to be subclassed. If you subclass the click method of listing 5, and use DODEFAULT() or the scope resolution operator (::) to invoke this parent method, then you’ll fire both the pre and post-hook at that point. Depending on the hooks, this can cause unexpected results because both hooks will fire as part of the scope resolution invocation.

4. A delegated pre-process hook

In a generalization of Listing 3, we could substitute THIS.oHook to reference a specialized hook object, as illustrated in Listing 6.

FUNCTION Click
  *-- Pre-process hook
  THIS.oHook.PreClick()
  WAIT WINDOW „Click“
  <100 lines of code>
ENDFUNC

Listing 6. A pre-hook method belonging to a hook object. This gives us flexibility to substitute behavior objects, as well as accomplish the same thing as Listing 3 by setting THIS.oHook=THIS.

5. A simple polymorphic pre-hook

Listing 7 improves on Listings 3 and 6 because it uses a clean implementation heuristic: Hooks are called polymorphically.  In other words, the Click() method of an object calls the Click() method of its hook.

FUNCTION Click
  *-- Pre-process hook
  THIS.oHook.Click()
  WAIT WINDOW „Click“
  <100 lines of code>
ENDFUNC

Listing 7. A cleaner version of Listings 3 and 6. Hook methods are called polymorphically.

[ 1 ] [ 2 ] [ 3 ] [ 4 ]