Advanced Design Patterns
Provides services to user. Examples include telnet, TCP, HTTP.
Structures information and attaches semantics.
Provides dialog control and synchronization facilities.
Responsible for segmenting long messages into packets. Recovers lost packets with acknowledgments and retransmissions. Flow control. Congestion control. Breaks messages into packets and guarantees
Responsible for routing packets from source to destination host. Selects a route from sender to receiver.
Responsible for moving packet from one node (host or packet switch) to next node. Error detection and correction. Medium access.
The physical layer, the lowest layer in the OSI stack, is responsible for moving information between two systems connected by a single physical link. The physical layer provides the abstraction of
bit transport, independent of the link technology. Specifies voltage levels, bit spacings.
The OSI 7-layer model is a cool example because it neatly shows the general types of services required for computers to talk to each other. The Internet protocol stack is a refinement of the OSI 7-layer model, minus the Presentation and Session layers, whose services are either not needed or abstracted into the neighboring layers.
Note that each of the layers in the OSI stack don't necessarily function on distinct hardware or memory space. For example, it's common to find the Data Link and Physical layers tightly coupled and interleaved (for performance reasons) within the same Ethernet network interface card.
A large system requires decomposition. One way to decompose a system is to segment it into collaborating objects. In large systems a first-cut rough model might produce hundreds or thousands of potential objects. Additional refactoring typically leads to object groupings that provide related types of services. When these groups are properly segmented, and their interfaces consolidated, the result is a layered architecture.
The trouble with layers of computer software is that sooner or later you loose touch with reality. Layers are abstraction boundaries, and the more they encapsulate their works the more one is unaware of the application's inner works.
Layering is a form of information hiding. A "layering violation" occurs in situations where a layer uses knowledge of the implementation details of another layer in its own operations. At the limit this leads to changes to one layer resulting in changes to every other layer, which is an expensive and error prone proposition.
Layering can lead to poor performance. To avoid this penalty, in situations where an upper layer can optimize its actions by knowing what a lower layer is doing, we can reveal information that would normally be hidden behind a layer boundary.
The layers must be engineered at the outset, before the system is built.
The following is a partial list of forces that bring about layered architectures. Note that some of these forces are present to varying degrees in all software systems.
A Layered model does not imply that each layer should be in a separate address space. Efficient implementations demand that layer-crossings be fast and cheap. Examples: User Interfaces may need efficient access to field validations.
Class: Layer J
Responsibility: Provides services used by Layer J+1 and delegates subtasks to Layer J-1
Collaborator: Layer J-1
Here are some typical interactions in layered architectures.
Messages that percolate downwards between layers are called Requests. For example, a client issues a request to Layer J. What Layer J cannot fulfill, it delegates to Layer J-1. Note that Layer J often translates requests from Layer J+1 into several requests to Layer J-1.
Messages that percolate upward between layers are called Notifications. A notification could start at layer J where, for example, an observer object detects an observable event. Layer J then formulates and sends a message (notification) to Layer J+1.
Layers are logical places to keep information caches. Requests that normally travel down through several layers can be cached to improve performance.
A system's programming interface is often implemented as a layer. Thus if two applications (or inter-application elements) need to communicate, placing the interface responsibilities into dedicated layers can greatly simplify the other application's layers and, as a bonus, make them more easily reusable.
The principle of separating the user interface from the application proper is old. It is rarely practiced, for all the talk we devote to it. The principle of separating the user interface from the application has the hardest consequences and is hardest to follow consistently. For the most part most applications barely separate the GUI from the application code, though they claim otherwise.
Imagine the application is totally program driven, with the user interface just one driving program. I'm not talking simply of separating the interface code from the application code, I mean separate GUI and application components.
In other words: The GUI is not a part of the application. It is the first client of the application.
A typical VFP application has too much special-purpose code in the GUI. This is mostly a fundamental problem with the IDE: The easiest thing to do, by far, is to put code in those GUI control methods.
Why so much GUI code? One cause of ballooned of GUI code is that it does things that should be done by the "model" - which we'll define, for now, as simply "another layer somewhere". Another cause of GUI code bloat is many people tend to embed code to maintain various kinds of integrity among objects. The result of these things is "know it all" controls.
Another mistake many developers fall into is GUI elements that pull the data to display directly in from the domain model and then having the GUI elements update the domain objects on any changes being made. Again, nothing could be easier in VFP!
Possibly better is a system of "naïve controls" that rely on separate Renderer objects to fill them with data from the domain model objects, and to update the domain objects with the changes made by the user.
It is easier on the user if input errors are brought up directly upon entry. Having the UI outside the application separates input from error detection. First the UI will change, so isolate and make an interface to it. Then someone will remove the human from the picture entirely, with electronic interchange or another application driving the program. Therefore, just making an interface to the UI component is not sufficient, it has to be an interface that does not care about the UI.
Therefore, put the UI totally outside the application. The application proper is bounded by a program-driven interface, and the UI is just one user of that interface, perhaps not even the first. Other users of the application could be another controlling application, or a testing application. Once the GUI is separate, almost anything is possible.
If the UI is really outside the application, what about when the user starts and then cancels a modification - where are the editing and rollback copies of the object kept? In the UI, or outside the application? What about typing errors - are they detected in the GUI or inside the application?
Answer: Keep edits in the GUI, of course! The user fumbling around in a GUI is a reality of a GUI. You want idiomatic GUI behavior and effects encapsulated in the GUI. Imagine writing a second program-driven interface, and having to deal with all these messages reminding the automated program in real-time that, say, a field is required. Yech!
Building systems requires us to bring many varied and unrelated concepts together. A typical medium or large sized system might involve diverse concepts like domain functionality, transactions, meta-data, database technology, network communication protocols, OS API calls, a GUI, etc.
Given pressure to quickly produce a reasonably fast system, it's tempting to tie these concepts closely together and so embed transaction-control code in the GUI code, or OS API code in the business code. This leads systems that are:
Hard to change: if you want to change the transaction-control system you need to scour all the GUI code to find all transaction-control related stuff
Hard to understand: business code and OS API code are, in their own right, complex and hard to understand. Mix them together and the complexity multiplies before your very eyes.
Hard to write: if you're writing business code the last thing you want to be worrying about is catching OS exceptions.
Write a layer of software to isolate each disparate concept or technology. These layers should isolate at the conceptual level (perhaps business code really needs to know nothing about the OS API - this is all handled transparently by some object management code) and/or at the technical level (handling unhandled exceptions raised by the object management code so they don't find their way into the business code). Isolation should be two-way (the business code 'knows' nothing of the OS API code and vice-versa).
This leads systems that are:
Easier to change: by isolating the database from the communication code we can change one or the other with minimum impact
Easier to understand: each 'bit' of the system deals with only one concept: business, networks, database
Easier to write: business people can write business code that isn't polluted with code to display dialogue boxes or handle network exceptions.
Of course the Isolation Layer itself may be complex and, possibly, represents a single point of failure. Over-application of the pattern leads to a system where everything is strongly decoupled and so the effects of system events may be unpredicatable and design or change is always 'selfish': distribution is hidden from the business designer and so they design without any thought for distribution - something which could bring the system to its knees.
Discussion: The choice a layered architecture can have many beneficial effects on your application if it is applied in the proper way. First, since the architecture is so simple, it is easy to explain to team members and so demonstrate where each object's role fits into the "big picture". If a designer is very strict about clearly defining where objects fit within the layers, and the interfaces between the layers, then the potential for reuse of many objects in the system can be greatly increased.
A common problem with many object designs is that they are too tightly constrained to the limits of the particular application being built. Many designers tend to put too much of the logic of an application in the GUI layer. In this case, there are few, if any, domain objects that are potentially available for reuse in other applications.
Another benefit of this layering is that it makes it easy to divide work along layer boundaries. It is easy to assign different teams or individuals to the work of coding the layers in a four-layer architectures, since the interfaces are identified and understood well in advance of coding. Finally, a four-layer architecture makes it possible to code the bulk of your system (in the domain model and application model layers) to be independent of the choice of persistence mechanism and windowing system.
Layers can make for great abstractions. But remember: abstractions are illusions! Beautiful, clean, elegant abstractions are still illusions. Don't be afraid to "cheat" - for example when you need better performance - and poke through a layer's formal boundaries. This is the essence of programming. Once you have a layered architecture don't be afraid to hack, just be elegant and rigorous about how you surface your hacks to your consumers.
The Strategy design pattern permits attaching an appropriate behavior or algorithm at run-time. It helps keep behavior flexible by decoupling a process from the one or more potential behaviors (or algorithms) that implement it.
Some processes require variety in behavior. To achieve this variety with a minimum of fuss, it helps to package the behaviors in a separate behavior class -- thus making them interchangeable -- and thereafter tinker only with the behavior classes, independently of the process that invokes them.
Sometimes the only difference between two classes is their behavior. It's not what the objects are that's different, but rather what they do. Subclassing a process for each new behavior is an obvious and workable mechanism. Another way, possibly better, is to identify and separate the variant parts of a process from its invariant parts, and then tinker only with the variants as required.
For example, imagine your accounting software produces invoices for companies operating in many different tax jurisdictions. The way taxes are calculated varies greatly depending on where sellers and customers are located. Additionally, taxes can vary according to what is being invoiced. Children's clothes, books, liquor, tobacco, and heating oil are examples of products that get special (and different) tax treatment almost everywhere.
Similarly, in some industries, discounts are popular sales incentives. Sales managers are constantly inventing creative new discount schemes. Sometimes discounts are constrained by the accounting software's ability to handle them.
Do you really want to deal with the potential for a separate industry or client-specific invoicing subclass for each country, state, or sales territory where your application is used? Of course not. You need a rational way to build your invoicing module so new features can be applied without polluting the underlying foundation which is otherwise largely invariant.
A Strategy pattern decouples a process from the algorithms that implement it. The key to setting up a successful Strategy pattern is interface consistency: give all your strategies a common interface, and the invocation process won't care which class provides the services. The best way to give strategies a common interface is, of course, to make them all belong to the same class.
In the diagram in , the Process "owns" a separate and independent Strategy instance that has an interface defined in the AbstractStrategy class. This ownership means, in practice, that the strategy objects are destroyed when the Process is destroyed. A common variant is for the Process to own more than one Strategy instance, where the Strategy objects are instanced as needed and thereafter retained in case they are needed again. This "pay-as-you-go" behavior means you only pay the overhead for the objects you actually need.
Here are some generally applicable reasons to consider a Strategy pattern:
Everywhere I go, customers have creative ideas about how discounts should work, or how they should be presented. (Henceforth we'll only talk about discounts, but everything here applies also to processing taxes).
Say your invoice class currently has the following interface:
Now suppose a client requires a new sort of discount, a significant variant. What to do? Here are a few options:
Here are three simple discount strategy classes. The first calculates a discount for each line item. The second calculates a discount based on a threshold invoice total, and the third calculates discounts for certain product types. All three are subclasses of an abstract discount type. These are just code skeletons, but from this it's easy to see that the AbstractDiscount class defines the whole class interface. It doesn't have to - you can modify the interface of subclasses to suit your mood - this is simply the way I prefer to do things.
DEFINE CLASS LineItemDiscount AS AbstractDiscount Rate= 0.1 FUNCTION Execute( toInvoice) *-- Apply a discount to each line item LOCAL lni FOR lni= 1 TO ALEN(toInvoice.LineItems,1) toInvoice.LineItem( lni).Discount = THIS.Rate * ; toInvoice.LineItem( lni).Price ENDFOR ENDDEFINE
DEFINE CLASS LineItemDiscount AS AbstractDiscount nThreshold= 1000 FUNCTION Execute( toInvoice) *-- Apply a discount if the total exceeds a threshold LOCAL lni, lnTotal lnTotal = 0 FOR lni= 1 TO ALEN(toInvoice.LineItems,1) lnTotal=lnTotal + toInvoice.LineItem( lni).Price ENDFOR IF lnTotal >= THIS.lnThreshold toInvoice.Discount= lnTotal* THIS.rate ENDIF ENDDEFINE
DEFINE CLASS ProductTypeDiscount AS AbstractDiscount nRate= 0.1 nThreshold= 1000 cType= "Dog Food"
FUNCTION Execute( toInvoice) *-- Apply a discount to each qualifying line item LOCAL lni FOR lni= 1 TO ALEN(toInvoice.LineItems,1) IF toInvoice.LineItem( lni).cType= THIS.cType toInvoice.LineItem( lni).Discount = THIS.Rate * ; toInvoice.LineItem( lni).Price ENDIF ENDFOR ENDDEFINE
Classes that cooperate as in a Strategy pattern are certainly flexible, but separating algorithms into independent classes can incur significant disadvantages. The most onerous I've found occurs when the strategy class needs non-trivial access to data structures in the calling process. For example, the strategy classes may need to know alias names, or to share arrays and pointers. In this situation, I've usually found it best to use a separate object to package everything about the calling process that the strategy may require. In other words, instead of passing 36 parameters to strategy objects, pass a single object having 36 properties. This keeps the interface clean - we pass at most one parameter, an object - and makes it easier to reuse processes and strategies as generalized abstractions.
A decorator wrapper (I'll just call it Decorator) is an elegant OOP technique that can be handy in a wide variety of situations. In particular, the Decorator pattern is useful for extending the functionality of a class without polluting (or even needing) the original source.
But before we get to the decorator pattern, lets examine what happens when we allow a class to grow organically with inheritance. To illustrate, consider the following class interface for a frog:
Suppose we later need a singing Frog. Well, within our current simple hierarchy, we already have several obvious options.
So, buffeted by the needs of this current implementation, you make your choice and you take your chances. What if you later needed a burping frog, one that belches after meals. Do we subclass again? If so, where? Do we add a Burp() method, or do we simply augment the Eat() method because problem analysis indicates that burping naturally follows from eating. Again, which hierarchical level is best for modification? And later, when you need a frog that can beg, roll-over, shake-a-leg, and stay, a new question eventually arises: "How did this Frog class become such a mess?".
At the outset, a pragmatic reuse artists would be correct in asking when, if ever, will the need for a singing, dancing, and belching frog ever arise again? Why not just augment the frog for this instance, without polluting our general, simple, and reusable Frog class?
The answer may be to use a decorator. A decorator is, in essence, a class with mostly "pass-through" behavior. It "wraps" a class by reference, forwarding all messages to the reference except for the messages the wrapper is designed to intercept. Setting up a decorator takes a bit of work, but thereafter it's a snap to use. Consider the following DecoFrog class:
Sample 4. Notice the key element of decorators: They are "transparent", passing through all messages except for those that need to be intercepted. Creating an abstract decorator class (like the one above) that passes through everything is the first step. Now to augment the frog for a particular instance, we can use a simple subclass of the decorator without polluting the Frog class.
To the outside world, the DecoFrog class has the same programming interface as a Frog. But it's not a Frog, it's a lens through which we can "see" a Frog. If we need a specialized one-off Frog, like one that sings, we could do this:
Similarly, we can define a dancing frog by simply subclassing the class DecoFrog as follows:
Retrofitting a decorator is easy. Where before your code looked like this:
A singing frog can now be substituted at run-time like this:
or more succinctly:
(Note that nesting CREATEOBJECT statements work just fine in Visual FoxPro).
Additionally, decorators can be chained, with functionality added or modified as needed! This gives you considerable pay-as-you-go flexibility. For example, to build a singing and dancing frog, do as follows:
or, if you prefer one-line of code:
Well, Tada! We now have an ordinary Frog named Kermit that appears, in this instance, with the ability to sing and dance, and we didn't need to pollute the Frog class to get it. In fact, we didn't need the source to class Frog.
Here are some benefits that come from using decorators.
As with all things, the "no free lunch" pattern applies: You cannot get the added flexibility afforded by using a decorator without some tradeoffs. Here are some of them:
Provide a surrogate or placeholder for another object to control access to it. Proxies are usually decorators in everything but name.
How to provide correct and/or efficient access to an object that may be resource intensive or otherwise require protection?
A client needs the services of an object, but direct access is technically possible but questionable.
Make a component's clients communicate with a representative rather than with the component itself.
Three actors: The client (as usual), the original object (as usual), and an intervening object (the Proxy) which serves as a placeholder for the original object and forwards requests as appropriate.
There are many uses for the Proxy pattern.
A template method is an abstract algorithm that references other methods. Template methods define the specific steps in an algorithm, often without much regard for implementation details. For example, the following method, used in military software to land jets on aircraft carriers on foggy nights, is an algorithm that directly calls four other methods:
This is exciting stuff. Forgive me, please, for not testing this.
The methods invoked by template methods are called hooks. Hook methods are places where adaptation can occur by subclassing. It's the job of hooks (and especially hook subclasses) to define, augment, or override each step in the algorithm. Hook methods are not to be confused with hook operations, which are defined in the next pattern. Hook operations are places, back-doors if you will, where external programs may be explicitly called to augment or override an existing method without subclassing.
The Template Method pattern, which is the interaction between template methods and hooks, is found in, among other places, all patterns that deal with abstract classes and abstract methods.
Codify the structure of an algorithm in a method, but purposefully leave the implementation steps to other methods, thereby making the algorithm partially abstract (or as abstract as it should be).
As a general rule, the steps in most software processes are known, as is their sequencing. For example, we authenticate users before giving them access to sensitive data; our programs clean-up before termination; we locate the runway before we begin our final approach; and so on.
In higher level abstractions, however, that's often all we know. We know that our programs should clean-up before termination, but exactly what this cleanup means - and exactly how it will be done - isn't of immediate high-level concern. More importantly, how the program cleans-up probably should be flexible so it can be done correctly under different circumstances.
A class's ability to adequately adapt is a virtue. Template methods are designed with exactly this in mind.
A good way to think of the Template Method pattern is template methods correspond to a "frozen" (invariable) part of a framework. The template methods call "hot spots" (hooks), which are domains that are purposefully adaptable.
Template Method patterns are among the easiest to understand and use, and they can be found everywhere. Anyone with top-down design experience will quickly recognize an instance of the Template Method design pattern.
Consider the following task: You must write a Visual FoxPro behavior class to direct a robot to search for contraband and explosives. It's execute method might look like this.
FUNCTION Ingress() *-- Subclass and define your own behavior. *-- Note that it isn’t unusual for template *-- methods to call other template methods. *-- Example, THIS.Ingress() could be another *-- template method, like this: * * THIS.ApproachRamp() * THIS.NavigateRamp() * THIS.EnterAirPlane() * ENDFUNC
FUNCTION Search() ENDFUNC
FUNCTION Egress() ENDFUNC
FUNCTION GoSomewhereSafe() ENDFUNC
FUNCTION SetWaitForInstructions() ENDFUNC
Listing 1 , illustrating a template method called ::Execute(), which calls five hooks. You would subclass this class to provide different implementation behavior.
I'm constantly amazed at how easy it is to write complex real-time robot control systems in Visual FoxPro.
In the example above, the template method is the Execute() method. In and of itself, the method (and hence the class) is completely useless - in spite of its ambitious intent, it does nothing. What it provides, however, is an abstract basis for the development of specialized subclasses which can make robots to search cars, buses, airplanes, embassies, even your own home.
On balance, classes built with template methods are easily adapted by subclassing because the major steps of an algorithm and their interfaces are pre-defined. All we need to do is subclass, and provide our own behavior where appropriate.
If you use VFP version 5.0, you have a fair degree of control over what downstream users can and cannot do. For example, by using the new Hidden methods and properties features, you can limit what the user's classes can see and accomplish with subclasses, or force them to use access methods to access class services to make the class more robust.
The only real downside is you must which methods have default behavior that may be overridden, and which ones have no default behavior, meaning they must be overridden.
Author's note: I would like to emphasize the contribution of Wolfgang Pree's work in the foundation of many ideas discussed in this pattern (see References below).
Hook operations are places within procedural or method code where adaptation is specifically designed to take place. The Hook Operations design pattern provides a consistent interface or methodology for adapting software using hooks. Hook operations usually do nothing by default; their invocation is the exception, not the rule. Correctly implemented, hook operations allow almost limitless extensibility.
Explicitly provide places where your software can be extended using a consistent extension mechanism, document the interface, and permit downstream developers to customize to suit their needs.
Object oriented toolkits and frameworks are composed of related and reusable classes that give general-purpose functionality. Toolkits provide code reuse, while frameworks provide architectural reuse. In either case, the goal is to be as flexible and extensible as possible.
There are several ways that toolkits and frameworks can be adapted, the most obvious of which is to subclass and then augment, or override, the original behavior. Subclassing pre-supposes several things that usually cannot be guaranteed: Firstly, to subclass, source code is required. Secondly, subclassing requires specialized knowledge of the classes and understanding of their functional interactions; certainly some subclassing tasks are trivial, but many are not. Third, it's extremely difficult to know every process that creates the parent class of this new subclass, and you may need to doctor these processes appropriately or some instances won't behave as you expect.
Hook operations are an alternative to all this. Hook operations are well defined back-doors where folks can attach programs to modify behavior, or even replace the process that is being hooked.
As a quick aside, we need look no further than Visual FoxPro's own architecture for a fine example of hook behavior. All of FoxPro's programmable events, for example, fire before the actual native VFP behavior. The code you put in, say, a control's Click() event, it executes before the actual Windows click message, and using NODEFAULT you can instruct VFP not to fire its native behavior. So when you write event code, you are in fact hooking an instance of VFP's base classes and augmenting or overriding the native behavior.
Another example is the Visual FoxPro Class Browser, which is a real hookfest. By using its AddIn() method, and by specifying a method to hook, you can permanently register custom behavior for a variety of events. Registering the add-in is persistent because the AddIn() method creates a record in BROWSER.DBF, which lives in your HOME() directory.
For example, with the registration instructions and the code below, you can hook the Browser's ExportClass() method to change the content of the exported class code to make it hog less space. The resulting code contains spaces instead of tabs (which I personally prefer), and also eliminates all the blank lines that are otherwise generated. This is very handy if you need to cut and paste code for an electronic message
This particular add-in places the modified class code in the Windows clipboard. As far as I know, there is no way to hook the ExportClass() method just before it fills the class code display window. We'll call this add-in on the CmdExport.Click() hook, fill the clipboard, modify the clipboard contents, and then return to the regular export process. Our modified code in in the clipboard, not in the Class Browser's code window.
*-- Browser Add-Ins pass a reference to Browser LPARAMETER oBrowser
*-- Put the class definition on the clipboard *-- Note: The .F. parameter on ExportClass() supresses *-- the class code display window. Here we just want *-- to place the code on the clipboard. The code display *-- window will appear naturally as part of the *-- downstream behavior which executes after this add-in. _CLIPTEXT= oBrowser.ExportClass(.F.)
*-- Tabs, begone! I prefer two spaces instead. _CLIPTEXT=STRTRAN(_CLIPTEXT, chr(9), SPACE(2))
*-- Excess blank lines, begone! DO AT( CHR(13)+CHR(13), _CLIPTEXT) > 0 _CLIPTEXT=STRTRAN(_CLIPTEXT, CHR(13)+CHR(13), CHR(13)) ENDDO
*-- Returning .F. tells BROWSER to execute its regular *-- cmdExport.Click() method as usual (which displays *-- the standard results in a class code display *-- window, wasteful of white space as always. *-- Remember that your modified stuff is in the *-- clipboard. RETURN .F.
Listing 2: A Class Browser hook to send reformatted source code to the clipbpard.
Here is a neat way to implement hook operations: Define a custom property to hold reference to a hook object, and thereafter always call this hook object before you execute your standard behavior.
To implement a Hook Operation pattern you need 1) to code the hook operation in your base class methods, and 2) design and maintain hook classes.
Here's the stripped-out core of a hook operation, without much regard for anything but the essentials. In real life, you might use PEMSTATUS() to check the existence of the hook method, as well as check the TYPE() of the hook itself. I've done this elsewhere, but not here, the interest is clarity.
FUNCTION SomeMethod( txPassed) *-- Here is a raw hook call IF ! ISNULL( THIS.oHook) THIS.oHook.SomeMethod( txPassed) ENDIF * << YOUR CODE HERE >>
Listing 3 , a simple hook operation, called early in ::SomeMethod()
Here are some facts about the example above:
FUNCTION SomeMethod( txPassed) *-- Here is a hook call that can override our code IF ! ISNULL( THIS.oHook) LOCAL lxTest lxTest= THIS.oHook.SomeMethod( txPassed) IF ! lxTest RETURN ENDIF ENDIF * << YOUR CODE HERE >>
Listing 4 , a simple hook operation, called early in ::SomeMethod(), that overrides the remainder of ::SomeMethod() with a return value.
Now that we can override our default behavior by agreeing that an "override" hook returns logical false. This is not the best solution, obviously, because the convention that "override hooks return false" really isn't sustainable. All sorts of reasons exist for a method to return logical false; using this protocol to manage the behavior of all your hooks, forever, is odious.
A solution I prefer is a sneaky use of a persistent and unused legacy FoxPro system memory variable to set a flag, as follows:
FUNCTION SomeMethod( txPassed) *-- Here is a hook call that can override our code IF ! ISNULL( THIS.oHook) THIS.oHook.SomeMethod( txPassed) *-- In this example, hooks use the unused but *-- available _BOX system memory variable to *-- communicate an intended NODEFAULT. *-- So in hook code, _BOX=.F. means NODEFAULT
IF ! _BOX *-- Reset _BOX for the benefit of the next hook *-- that may need it. _BOX= .T. *-- "NODEFAULT", so we’re done. RETURN ENDIF ENDIF * << YOUR CODE HERE >>
Listing 5, a simple hook operation, called early in ::SomeMethod(), that overrides the remainder of ::SomeMethod() by setting a handy, public, and otherwise unused flag.
The help file says that _BOX system memory variable is "Included for backward compatibility. Use the Report Designer instead". Well, I say: use it for hooks to communicate to their calling methods instead. Voila. Now it doesn't matter what hook methods return; if they set _BOX to .F., that means, in effect, NODEFAULT.
Here is a more illustrative example of the benefits of hooks. Here we'll hook the RightClick() method of a textbox to provide a context sensitive popup menu. In practice you could hook all the common methods of all your base classes to do a variety of things.
The class definition, complete with its seeded RightClick() method, looks like this:
*-- Reference to a hook object ohook = .NULL.
PROCEDURE RightClick *-- Delegate to any hook IF ! ISNULL( THIS.oHook) AND ; TYPE( "THIS.oHook") = "O" AND ; PEMSTATUS( THIS.oHook, "RightClick", 5)
_BOX= .T. THIS.oHook.Rightclick()
IF ! _BOX RETURN ENDIF
ENDIF ENDPROC FUNCTION Release *-- It’s always wise to clean up your pointers THIS.oHook= .NULL. RELEASE THIS ENDDEFINE
Listing 6, a textbox with a RightClick hook.
Here's a RightClick()-seeded hook object that works extremely well with textboxes and edit boxes to bring up a handy edit menu:
PROCEDURE RightClick *-- Bring up a handy edit tools menu DEFINE POPUP shortcut SHORTCUT RELATIVE FROM MROW(),MCOL() *-- Cut DEFINE BAR _med_cut OF shortcut PROMPT "Cu\<t" ; KEY CTRL+X, "Ctrl+X" ; MESSAGE "Removes the selection and places it onto the Clipboard" *-- Copy DEFINE BAR _med_copy OF shortcut PROMPT "\<Copy" ; KEY CTRL+C, "Ctrl+C" ; MESSAGE "Copies the selection onto the Clipboard" *-- Paste DEFINE BAR _med_paste OF shortcut PROMPT "\<Paste" ; KEY CTRL+V, "Ctrl+V" ; MESSAGE "Pastes the contents of the Clipboard" *-- Select All DEFINE BAR _med_slcta OF shortcut PROMPT "Se\<lect All" ; KEY CTRL+A, "Ctrl+A" ; MESSAGE "Selects all text or items in the current window" *-- Clear DEFINE BAR _med_clear OF shortcut PROMPT "Cle\<ar" ; MESSAGE "Removes the selection and does not place it onto the Clipboard" ACTIVATE POPUP Shortcut ENDDEFINE
Listing 7, a usable RightClick() hook usable with code in Listing 6.
To set up this basic hook do the following:
oForm.AddObject( "oTxtbox", "appTextBox") oForm.AddObject( "oMouseHook","lblMouseHook") oForm.otxtbox.Visible= .T.
oForm.Caption= "RightClick this textbox" oForm.Show() READ EVENTS
When you rightclick over the textbox, control is passed to the hook, which invokes a context sensitive shortcut menu. Two things to draw from this example:
Two conflicting forces determine where and when hook operations should be used. On one hand, hook operations are most valuable at critical junctures in a process. On the other hand, if outsiders are those applying hooks, usability dictates that they should be positioned consistently at predictable junctures.
Unfortunately, it's difficult to identify and document exactly where a process' critical junctures lie - even assuming we could predict the needs of those who will apply hooks. For this reason, hook operations are usually found, predictably, either at the beginning or at the end of methods.
It's not possible to have hook operations at both the beginning and the end of methods because of inheritance. When one executes a ParentClass::Method() call, executing both "pre" and "post" hooks at that single point isn't likely desirable.
How, then, to provide hooks both at the beginning and at the end of a particular process? It's easy if you use a multi-part process. For example, consider a commandbutton whose a Click() calls a particular method, as follows
DEFINE CLASS MyForm AS FORM ADD OBJECT oCmd AS HooklessProcess
FUNCTION Somemethod WAIT WINDOW "No hooks here" ENDDEFINE
Listing 8, a button without hooks.
Compare the above to this, where the "pre" hook is launched by the commandbutton, and the "post" hook is launched at the end of the method invoked.
DEFINE CLASS MyForm AS FORM ADD OBJECT oCmd AS StartHookLessProcess
* << Your code here >>
IF ! ISNULL( THIS.oHook) THIS.oHook.SomeMethod() && The "Post" hook ENDIF ENDFUNC ENDDEFINE
Listing 9, a button with a hook before calling a method, which contains a hook at its conclusion.
You can easily abstract this with the following rule of thumb: Events (like click and dblclick) always fire "pre" hooks, and Methods (things invoked by events) always fire "post" hooks. So you have, in effect, an invokable back-door on the way in, and another invokable back-door on the way out.
Classes created with embedded hook operations are generally much more flexible than those that aren't. If you define hook operations consistently and predictably throughout your application, then it's usually possible to attach new behavior to satisfy the needs of a particular instance without subclassing. Moreover, hook operations can be reckoned and bound at run time.
Now let's take these two patterns, combine them in a variety of ways, and see what we can come up with.
First, a quick summary:
Here are two new definitions that we'll use from here onwards
Let's look at some ways that these classes can be deployed.
This configuration comes up all the time. It happens when hook methods are implemented by the template class itself. Obviously, in this case, none of the classes called by the template method can be abstract. This is simply the common case when the template class is fully satisfied within its object.
A self-sufficient template class can be subclassed for new hook behavior, which leads to the following common configuration. Everyone who's programmed in an object oriented way has done this:
In other words, a self-sufficient class is subclassed and one or more hook methods is augmented or overridden to achieve a new behavior. This is what everyone immediately thinks of when they hear a phrase containing the words "benefits of OOP".
In this configuration, there is no inheritance relationship between the template classes and the hooks that implement them. Template T and hook H are coupled in the sense that T must know the interface of H. A change in H, or a requirement change in T, implies a change in the other in order to preserve correctness.
Managing the interfaces between the two classes is not trivial in the case where T is a template due to the presence of one or perhaps many hook operations. A logical way to manage hook operation interfaces is to use the same method name in the template method and the hook operation, as follows:
THIS.oHook.RightClick() && Same name as the && method of origin
Listing 10, naming hook methods after its invoker makes abundant sense.
Which begs the question: If template classes and hook classes share the same interface, as in the example above, why would they come from separate classes?
In Visual FoxPro, all the base classes have some methods in common. For example, you can click, right click, and double click virtually all the visual classes, so it appears the separate-classes question has little relevance. It's when you start propagating custom methods, however, that the question becomes increasingly appropriate as the dual-interface maintenance burden increases.
In summary, there may be merit to the idea that template and hook classes should belong to the same class hierarchy in order to make maintaining the interfaces a little easier, if not automatic.
On the other hand, if your template methods and hook operations don't share the same interface nomenclature, then you have the makings of a perpetual maintenance nightmare, and there is little you can do at an architectural level to alleviate that. The best you can do is use a naming convention and hope you don't forget to correctly map the interfaces together.
At first glance, having hook classes inherit from template classes seems to make sense. If we define a new non-HIDDEN method in template T, then hook class H will possess it automatically. We'll still need to add hook implementation code, of course, but in the interim at least the class interfaces are automatically in sync.
Unfortunately, from a purist OOP design perspective, this is completely bogus. Here's why:
This configuration is not intuitive and, at first glance, seems outlandish. Why would a template class inherit from a hook class? After all, T defines the basic algorithms, and usually you need to subclass T to implement its abstract hook methods, or to augment or override its concrete hook methods. This hardly seems worth talking about.
If we look at this configuration more closely, we can come to the following surprising (I think potentially explosive) conclusions.
Defining the hook methods in H, and making T a subclass of H, means
Which achieves for us a major goal, namely the separation of an interface (the button) from its implementation (the behaviors). The code below creates a form with three textboxes and assigns a different behavior pattern to the RightClick() method of each checkbox.
We'll talk more about hook operations during the session.
DEFINE CLASS AbstractHook AS Relation && Relations are Ultra Light! ********************************************** * A chainable hook class **********************************************
*-- This property, along with the SetHook method, *-- allows us to chain hooks! oHook= .NULL. *-- Set the oHook reference PROCEDURE setohook LPARAMETERS txPassed LOCAL llRetVal DO CASE CASE ISNULL( txPassed) THIS.oHook= .NULL. llRetVal= .T. CASE TYPE( "txPassed")= "O" IF TYPE( "THIS.oHook")= "O" AND ; PEMSTATUS(THIS.oHook, "SetoHook", 5) THIS.oHook.SetoHook( txPassed) ELSE THIS.oHook= txPassed ENDIF
ENDCASE RETURN llRetVal ENDPROC
********************************************** *-- Here we define just a two methods to hook. *-- In practixe you'll want to hook many more ********************************************** PROCEDURE GotFocus *-- Delegate to the implementation chain IF ! ISNULL( THIS.oHook) AND ; TYPE( "THIS.oHook") = "O" AND ; PEMSTATUS( THIS.oHook, "GotFocus", 5) RETURN THIS.oHook.GotFocus() ENDIF PROCEDURE RightClick *-- Delegate to the implementation chain IF ! ISNULL( THIS.oHook) AND ; TYPE( "THIS.oHook") = "O" AND ; PEMSTATUS( THIS.oHook, "RightClick", 5) RETURN THIS.oHook.Rightclick() ENDIF
DEFINE Class MyForm AS Form AutoCenter= .T. Caption= "Chained Hooks Example - 3 different behaviors" ShowTips= .T. ADD OBJECT txt1 AS MyTextBox WITH Left=9, Top=11 ADD OBJECT txt2 AS MyTextBox WITH Left=9, Top=41 ADD OBJECT txt3 AS MyTextBox WITH Left=9, Top=71 PROCEDURE Init Local loHandle ********************************* * First textbox gets help and edit features ********************************* loHandle= CREATE("HelpRightClick") THIS.txt1.SetoHook( loHandle) loHandle= CREATE("EditRightClick") THIS.txt1.SetoHook( loHandle)
********************************* * Second textbox gets just edit features ********************************* loHandle= CREATE("EditRightClick") THIS.txt2.SetoHook( loHandle)
********************************* * Third textbox gets random color and help features ********************************* loHandle= CREATE("ColorRightClick") THIS.txt3.SetoHook( loHandle) loHandle= CREATE("HelpRightClick") THIS.txt3.SetoHook( loHandle) PROCEDURE Destroy CLEAR EVENTS ENDDEFINE
DEFINE CLASS ColorRightClick AS AbstractHook *-- This class causes the screen backcolor *-- to go random when the right click method fires. nOldColor= 0 PROCEDURE INIT THIS.nOldColor= _SCREEN.BackColor
PROCEDURE GotFocus DODEFAULT() IF POPUP("shortCut") DEACTIVATE POPUP ShortCut RELEASE POPUPS ShortCut ENDIF PROCEDURE RightClick DODEFAULT() _SCREEN.BackColor= RAND()* 255^3
PROCEDURE Destroy _SCREEN.BackColor= THIS.nOldColor ENDDEFINE
DEFINE CLASS HelpRightClick AS AbstractHook PROCEDURE GotFocus DODEFAULT() IF POPUP("shortCut") DEACTIVATE POPUP ShortCut RELEASE POPUPS ShortCut ENDIF
*-- This class adds Help to the shortcut menu *-- when its rightclick method fires. PROCEDURE RightClick DODEFAULT() IF ! POPUP("ShortCut") DEFINE POPUP shortcut SHORTCUT RELATIVE FROM MROW(),MCOL() ENDIF
*-- Add a separating line IF CntBar("ShortCut")> 0 DEFINE BAR 1010 OF Shortcut PROMPT "\-" AFTER _MLAST ENDIF DEFINE BAR 1011 OF Shortcut PROMPT "Help" AFTER _MLAST ON SELECTION BAR 1011 OF ShortCut Help ACTIVATE POPUP Shortcut NOWAIT ENDDEFINE
DEFINE CLASS EditRightClick AS AbstractHook *-- This class adds edit features to the shortcut *-- menu when the rightclick method fires.
PROCEDURE GotFocus DODEFAULT() IF POPUP("shortCut") DEACTIVATE POPUP ShortCut RELEASE POPUPS ShortCut ENDIF PROCEDURE RightClick DODEFAULT() IF ! POPUP("ShortCut") DEFINE POPUP shortcut SHORTCUT RELATIVE FROM MROW(),MCOL() ENDIF IF CntBar("ShortCut")> 0 DEFINE BAR _med_sp300 OF shortcut PROMPT "\-" ENDIF DEFINE BAR _med_cut OF shortcut PROMPT "Cu\<t" ; KEY CTRL+X, "Ctrl+X" ; MESSAGE "Removes the selection and places it onto the Clipboard" DEFINE BAR _med_copy OF shortcut PROMPT "\<Copy" ; KEY CTRL+C, "Ctrl+C" ; MESSAGE "Copies the selection onto the Clipboard" DEFINE BAR _med_paste OF shortcut PROMPT "\<Paste" ; KEY CTRL+V, "Ctrl+V" ; MESSAGE "Pastes the contents of the Clipboard" DEFINE BAR _med_slcta OF shortcut PROMPT "Se\<lect All" ; KEY CTRL+A, "Ctrl+A" ; MESSAGE "Selects all text or items in the current window" DEFINE BAR _med_clear OF shortcut PROMPT "Cle\<ar" ; MESSAGE "Removes the selection and does not place it onto the Clipboard" ACTIVATE POPUP Shortcut NOWAIT ENDDEFINE
DEFINE CLASS AbstractTextBox AS TextBox *-- Set the oHook reference PROCEDURE setohook( txPassed) LOCAL llRetVal DO CASE CASE ISNULL( txPassed) THIS.oHook= .NULL. llRetVal= .T. CASE TYPE( "txPassed")= "O" IF TYPE( "THIS.oHook")= "O" AND ; PEMSTATUS(THIS.oHook, "SetoHook", 5) THIS.oHook.SetoHook( txPassed) ELSE THIS.oHook= txPassed ENDIF
ENDCASE RETURN llRetVal ENDPROC PROCEDURE release *-- Release related hook objects IF TYPE("THIS.oHook") <> "U" THIS.oHook= .NULL. ENDIF PROCEDURE GotFocus *-- We use _BOX as a flag _BOX= .T. *-- Delegate to the implementation chain IF ! ISNULL( THIS.oHook) AND ; TYPE( "THIS.oHook") = "O" AND ; PEMSTATUS( THIS.oHook, "GotFocus", 5) RETURN THIS.oHook.GotFocus() ENDIF
PROCEDURE RightClick *-- We use _BOX as a flag _BOX= .T. *-- Delegate to the implementation chain IF ! ISNULL( THIS.oHook) AND ; TYPE( "THIS.oHook") = "O" AND ; PEMSTATUS( THIS.oHook, "RightClick", 5) RETURN THIS.oHook.Rightclick() ENDIF ENDPROC
DEFINE CLASS MyTextBox AS AbstractTextBox *-- Reference to a hook object ohook = .NULL. Width=150 ToolTipText = "Right Click Me" StatusBarText= "Right Click Me" Value = "Right Click Me" ENDDEFINE
Template methods call hook methods, and hook methods are adaptable by standard subclassing mechanisms. Hook operations are places where optional adaptation is meant to happen. Templates and hooks are related structures that can be combined in a variety of ways. One of these combinations, the case where templates classes inherit from hook classes permits a surprising degree of configurability and maintainability.
Because if a template class inherits from a hook class, then the template class is a hook. Moreover, since template classes are (by definition) themselves hooked, then there is really no limit to the behavior that can be attached and chained to a particular template method in a template class.
Relax, this took a long time to sink into me too.
Steven has been a Fox developer since 1986. He markets Steven Black's INTL Toolkit , a multi-lingual framework for FoxPro and Visual FoxPro, which he created in 1993 and continues to refine. He has been a featured speaker at FoxPro DevCons and regional conferences, and his contributions occasionally darken the pages of VFP books and magazines. His company, SBC / UP!, is based in Kingston, Ontario, and operates worldwide. He specializes in multi-lingual, multi-site, and other challenging FoxPro projects, including out-of-control project turn-arounds and cleanups. He consults with small developers as well as Fortune 500 companies, national and international government agencies, and software development companies to elevate their development teams. He is also the creator and webmeister of the FoxForum Wiki, a popular collaborative topic-based Visual FoxPro website at http://www.stevenblack.com/wiki