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.
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.
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
The hooked object invokes hooks through hook operations.
Hook objects may themselves be hooks.
The advantages of hook operations include:
Some disadvantages are
This section briefly shows some ways hook operations can be implemented in Visual FoxPro.
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.
Listing 3. A pre-hook followed by standard method code.
Listing 4 shows a simple post-processing hook. This example uses a common convention of naming the pre-process with the prefix „post“
Listing 4. A post-process Click hook.
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!
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.
In a generalization of Listing 3, we could substitute THIS.oHook to reference a specialized hook object, as illustrated in Listing 6.
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.
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.
Listing 7. A cleaner version of Listings 3 and 6. Hook methods are called polymorphically.