[i]
Session
Using Software Design Patterns
Bill Anderson
Overview
One of the hallmarks of a successful application is the adaptability to change to new and updated requirements. This flexibility isn’t inherent – it should be planned in the application’s design phase. Using Software Design Patterns in your development will provide the foundation for handling these unexpected requirements. This session presents many useful design patterns that can lead to more flexible applications.
Software Patterns – A Definition
In reference to construction patterns, Christopher Alexander states, “Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice.” Mr. Alexander was referring to physical spaces and objects – windows, doors, walls and the like. While software design patterns are expressed in terms of objects, methods and interfaces, the generality is very similar. Patterns are a solution to a problem in a context.
Software design patterns solve specific design problems and make object oriented designs more flexible, elegant and adaptable to changing requirements.
Quoting from the book Design Patterns – Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides (if you ever hear about the “Gang of Four” or see the initials GoF, this is the “gang”.) A pattern has four essential elements:
1. The pattern name is a handle we can use to describe a design problem; its solutions and consequences in a word or two.
2. The problem describes when to apply the pattern. It explains the problem and its context.
3. The solution describes the elements that make up the design, their relationships, responsibilities, and collaborations. The solution doesn’t describe a particular concrete design or implementation, because a pattern is like a template that can be applied in many different situations.
4. The consequences are the results and trade-offs of applying the pattern.
In essence a software design pattern 1) has a name 2) has a reason to exist 3) is defined and 4) has a (beneficial) result.
Familiarity with Software Patterns
You’re already very familiar with software patterns. Let’s demonstrate your familiarity with some examples…
What object(s) would you use to handle unlimited amounts of text? An edit box would be a good choice.
What object(s) would you use to handle a sequential range of numbers? A spinner comes to mind…
What object(s) would you use to handle a static list of items? Well, we have several objects available to us to handle a static list of items. Which object is truly appropriate will depend on our requirements (can the list be updated, available room on the form, etc.), but there are several appropriate selections. Obvious and clear choices are list boxes and combo boxes. In VFP 8, grids can be made to act like list boxes. In VFP 9, the textbox class has been enhanced via the AutoComplete property to automatically store and suggest values. But we can certainly rule out certain controls such as the check box, command button, timers, separators and others.
See what I mean? You’re already very familiar in applying generic solutions to your specific application development issues.
Using the edit box control as an example, you implicitly know the context by which the control is used and when it isn’t. Whether or not the edit box control is read only or not, or the specific name of the control source used to bind the edit box to a field or the height of the edit box – those issues are implementation details. You’re familiar with the control, how to use it and when it’s appropriate to use such a control.
An edit box (like other base controls in VFP and other languages) can be considered a control used to handle an application design problem. It’s not a software design pattern, but it’s a pattern nonetheless.
For contrast, let’s apply the edit box control to the GoF software design pattern definition:
1) Name – Edit Box.
2) Problem to solve – Providing an easy to use control for handling unlimited amounts of text.
3) Design, relationships, responsibilities, collaborations – this isn’t applicable to controls. Pattern is like a template – an edit box certainly is a template. We supply the size, control source and other behaviors to apply it to a specific situation.
4) Consequences, results – We can handle large amounts of text but the control takes up a lot of room compared to the size of other controls.
To repeat, you’re familiar with patterns. Let’s see how software design patterns are used to solve specific design problems.
Bridge Pattern
Definition: Decouple an abstraction from the implementation so the two can vary independently.
Heuristic: The definition says it all. Separate a class’s abstraction from its implementation by making each a separate class. Defer implementation details to these classes. Avoid “God” objects.
The Bridge Pattern is the most fundamental of all design patterns.
We’ll illustrate the Bridge Pattern with a simple example. Here’s a typical line of VFP code:
lnResult = MESSAGEBOX([Are you sure you want to delete this record?], ;
4 + 32, [11th European Visual FoxPro DevCon])
What could be wrong with this line of code? We’ll look this line with a little more detail…
Delivery Mechanism Message
(Implementation) (Abstraction)
Ü Ü
lnResult = MESSAGEBOX([Are you sure you want to delete this record?], ;
4 + 32, [11th European Visual FoxPro DevCon])
We’re combining the method of delivery with the message being delivered. In essence, we are coupling the “envelope” (the delivery mechanism) with the “letter” (the actual message) and “mailing” (displaying the message) it all in one command. Later on, you may determine that some of your users prefer the old style WAIT WINDOW or dialogs with different styles (a different envelope). Or perhaps your application will be used as an in-process DLL automation server where no user interface is allowed (Don’t deliver the mail).
The solution is to build a message handler object to deal with the implementation issues. Assuming for the moment that we’ve instantiated an object property in our class called oMessageHandler (or the act of referencing oMessageHandler triggers the assign method of this object property, instantiating the object for us) we can write the example line of code in this fashion:
Delivery Mechanism Message
(Implementation) (Abstraction)
Ü Ü
lnResult = THIS.oMessageHandler.Display([Are you sure you want to delete this record?])
The object oMessageHandler acts as a bridge between the implementation and the abstraction.
Bridge Types
Reference Bridge – The above example is defined as a reference bridge. The interface object has a property which contains a reference to the implementation object.
Multiple Bridge – A multiple bridge is a reference bridge that uses an array property to hold implementation object references.
An example of a multiple bridge within Visual FoxPro is the SetAll method of a container. Given a container object named oContainer and the following command is issued
oContainer.SetAll([Tag], [Test])
Internally Visual FoxPro does something like this:
FOR EACH loObject in THIS.Objects
loObject.SetProperty([Tag], [Test])
ENDFOR
Aggregation Bridge – An aggregation bridge is an implementation object that is placed on a container object at design time or at run time via the AddObject method.
To repeat, the bridge pattern is the most basic of all design patterns. In fact, the core of many design patterns to follow incorporates this basic pattern.
Decorator (Wrapper) Pattern
Definition: Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
Heuristic: Subclass all “black box” objects such as the Visual FoxPro base classes (or if you’re using a VFP based framework, subclass the framework classes) and ActiveX controls.
If you’ve subclassed a Visual FoxPro control, then you’re familiar with the Decorator pattern.
The goal of the Decorator pattern is rather straightforward to understand – make a class that provides the same methods and properties as the object being subclassed. This gives you the ability to augment, override or suppress the behavior of the object being subclassed. Visual FoxPro does this automatically for all VFP base classes and ActiveX controls so decorating is easy.
Using the IMPLEMENTS clause of the DEFINE CLASS statement is in effect a decorator around a COM component.
Decorators can also be used for “white box” objects as well to modify behavior as needed in a controlled fashion.
Decorators can easily be extended beyond software management. You can use a function to decorate a VFP function or command.
Example: Placing an unmodified Microsoft Web Browser control (Internet Explorer) onto a Visual FoxPro form simply will not run correctly. A NODEFAULT statement must be placed in the Refresh method of the Web Browser control. Simply subclass the Microsoft Web Browser control, decorate the Refresh method with a NODEFAULT statement and use this decorated subclass in your forms and classes.
Adapter Pattern
Definition: Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.
Heuristic: Use an adapter pattern to translate a new or updated interface to an established interface. In other words, make a class look like another class.
Whereas a Decorator pattern is used modify the behavior of a particular class, the Adapter pattern does not modify object behavior. The Adapter pattern is used to only modify an interface to the object.
Let’s say that we’re using a class named A. We make calls to the methods in class A in our application. Later on, we decide to replace the class named A with a class named B. However, some of the method names are different. What do we do?
One method is to go through the application and rip out/replace all of the calls to methods within class A with similar calls to class B. This plan has several consequences: (1) There may not be an easy one to one correspondence with method names (2) If you later replace class B with class C, the same issues arise.
The solution is to use a subclass of B (let’s call it BB for now) and add every method in class A that isn’t in B (Note that even if class A has a same method name as B, that does not necessarily imply that these methods perform the exact same function). By definition, the class named BB contains all the method names of both A and B. Now map the “A” method names to one or more of the “B” method names. If there’s no matching method(s) in B for a method, then you’ll need to write your own method.
If this appears to you be an extension of the bridge pattern, it is. The subclass BB is a bridge between the methods of class A and class B.
Example: Let’s say you’re using a subclass of the Microsoft Web Browser control in your application. Later on, you decide to replace this ActiveX control with a Web Browser control from a competing company. Use the Adapter pattern to subclass this new control and make it look like the interface to Microsoft Web Browser control.
Template Method
Definition: Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.
Heuristic: Define a common sequence of events via methods of a template class, leaving the implementation of these methods to subclasses.
Template patterns are often used by third party frameworks as placeholders for you, the developer, to add additional behavior to address a specific scenario. Template patterns will contain abstract operation methods (which should be coded in the subclasses) and should contain hook operations (which could be coded in the subclass) which serve as empty placeholder methods.
Example: Let’s build a skeleton template class to export a file. The process would look something like this:
Function Export
WITH THIS
LOCAL llExported
llExported = .f.
.cExportFile = .GetFileToExport()
IF .OpenFileToExport(.cExportFile) & abstract operation method
IF .BeforeExportFile(.cExportFile) & hook operation
llExported = ExportFile(.cExportFile) & abstract operation method
.AfterExportFile(.cExportFile, llExported) & hook operation
ENDIF .BeforeExportFile(.cExportFile)
ENDIF .OpenFileToExport(.cExportFile)
.CloseFileToExport(.cExportFile)
IF NOT llExported
.WriteError()
ENDIF NOT llExported
RETURN llExported
ENDWITH
In this example the OpenFileToExport, BeforeExportFile, AfterExportFile and ExportFile are empty methods in this template class. OpenFileToExport and ExportFile must be coded in the subclass, or no file will be exported. The BeforeExportFile and AfterExportFile methods are empty methods that could be used by the subclass to provide additional capabilities.
Iterator Pattern
Definition: Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
Heuristic: The mechanism of traversing and accessing a common set of objects should be separated from the objects being traversed.
If you’ve ever traversed an array, then you’re familiar with the concepts behind the Iterator pattern.
The Collection base class was added to Visual FoxPro 8. This base class provides the basic functionality of the Iterator pattern. The collection base class is a container object whose contained members are referenced by number or by a passed index to the Item method. Members can be added via the Add method and removed via the Remove method. Members can be sorted as needed through the KeySort method property and traversed via a FOR or FOR EACH loop.
Note that the objects being referenced have no knowledge of being contained within a collection – even if the contained members are also collections!
Observer Pattern
Definition: Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
Heuristic: Use an observer pattern to separate state management from the objects being managed.
The Observer pattern has characteristics of both the Bridge and the Iterator patterns. State management is abstracted from the objects that react to changes in state management so the two can vary independently. Once such a state management change occurs, the Observer object iterates through the collected objects and passes the message to the managed objects.