Chapter 9. The Simple Factory Pattern

One type of pattern that we see again and again in OO programs is the Simple Factory pattern. A Simple Factory pattern is one that returns an instance of one of several possible classes, depending on the data provided to it. Usually all of the classes it returns have a common parent class and common methods, but each of them performs a task differently and is optimized for different kinds of data. This Simple Factory is not, in fact, one of the 23 GoF patterns, but it serves here as an introduction to the somewhat more subtle Factory Method GoF pattern we'll discuss shortly.

How a Simple Factory Works

To understand the Simple Factory pattern, let's look at the diagram in Figure 9-1.

A Simple Factory pattern

Figure 9-1. A Simple Factory pattern

In Figure 9-1, X is a base class, and classes XY and XZ are derived from it. The XFactory class decides which of these subclasses to return, depending on the arguments you give it. On the right, we define a getClass method to be one that passes in some value abc and that returns some instance of the class x. Which one it returns doesn't matter to the programmer, since they all have the same methods but different implementations. How it decides which one to return is entirely up to the factory. It could be some very complex function, but it is often quite simple.

Sample Code

Let's consider a simple VB6 case where we could use a Factory class. Suppose we have an entry form and we want to allow the user to enter his name either as “firstname lastname” or as “lastname, firstname.” We'll make the further simplifying assumption that we will always be able to decide the name order by whether there is a comma between the last and first name.

This is a pretty simple sort of decision to make, and you could make it with a simple if statement in a single class, but let's use it here to illustrate how a factory works and what it can produce. We'll start by defining a simple interface that takes the name string in and allows you to fetch the names back.

Public Sub init(ByVal s As String)
End Sub
'-----
Public Function getFrName() As String
End Function
'-----
Public Function getLname() As String
End Function

The Two Derived Classes

Now we can write two very simple classes that implement that interface and split the name into two parts in the constructor. In the FNamer class, we make the simplifying assumption that everything before the last space is part of the first name.

'Class FNamer
Implements Namer
Private nm As String, lname As String, frname As String
'-----
Private Function Namer_getFrname() As String
Namer_getFrname = frname
End Function
'-----
Private Function Namer_getLname() As String
Namer_getLname = lname
End Function
'-----
Private Sub Namer_init(ByVal s As String)
Dim i As Integer
nm = s
i = InStr(nm, " ")                'look for space
If i > 0 Then
  frname = Left$(nm, i - 1)     'separate names
  lname = Trim$(Right$(nm, Len(nm) - i))
Else
  lname = nm            'or put all in last name
  frname = ""
End If
End Sub

And in the LNamer class, we assume that a comma delimits the last name. In both classes, we also provide error recovery in case the space or comma does not exist.

'Class LNamer
Implements Namer
Private nm As String, lname As String, frname As String
'-----
Private Function Namer_getFrname() As String
Namer_getFrname = frname
End Function
'-----
Private Function Namer_getLname() As String
Namer_getLname = lname
End Function
'-----
Private Sub Namer_init(ByVal s As String)
Dim i As Integer
nm = s                  'save whole name
i = InStr(nm, ",")      'if comma, last is to left
If i > 0 Then
  lname = Left$(nm, i - 1)
  frname = Trim$(Right$(nm, Len(nm) - i))
Else
  lname = nm            'or put all in last name
  frname = ""
End If
End Sub

Building the Simple Factory

Now our Simple Factory class is easy to write and is part of the user interface. We just test for the existence of a comma and then return an instance of one class or the other.

Private nmer As Namer   'will be one kind or the other
'-----
Private Sub getName_Click()
Dim st As String, i As Integer
st = txNames.Text        'get the name from the entry field
i = InStr(st, ",")       'look for a comma
If i > 0 Then
  Set nmer = New lNamer  'create last name class
Else
  Set nmer = New Frnamer 'or fist name class
End If
nmer.init st
'put results in display fields
txFrName.Text = nmer.getFrName
txlName.Text = nmer.getLname
End Sub

Using the Factory

Let's see how we put this together. The complete class diagram is shown in Figure 9-2.

The Namer factory program

Figure 9-2. The Namer factory program

We have constructed a simple user interface that allows you to enter the names in either order and see the two names separately displayed. You can see this program in Figure 9-3.

The Namer program executing

Figure 9-3. The Namer program executing

You type in a name and then click on the Compute button, and the divided name appears in the text fields below. The crux of this program is the compute method that fetches the text, obtains an instance of a Namer class, and displays the results.

And that's the fundamental principle of the Simple Factory pattern. You create an abstraction that decides which of several possible classes to return, and it returns one. Then you call the methods of that class instance without ever knowing which subclass you are actually using. This approach keeps the issues of data dependence separated from the classes' useful methods.

Writing the Factory Pattern in VB.NET

In VB7, we can get a fair amount of mileage out of using inheritance here. We can define a base class called NameClass that holds the first and last name in protected variables and define the two accessor functions to get the first and last name out of the variables.

Public Class NameClass
    Protected Lname, Frname As String
    Public Function getFirst() As String
        Return Frname
    End Function

    Public Function getLast() As String
        Return Lname
    End Function
End Class

Then we can derive the FirstFirst and LastFirst classes from this class and make use of the underlying get methods. The complete FirstFirst class is just this.

Public Class FirstFirst
    Inherits NameClass
    Public Sub New(ByVal nm As String)
        Dim i As Integer
        i = nm.indexOf(" ")
        If i > 0 Then
            Frname = nm.substring(0, i).trim()
            Lname = nm.substring(i + 1).trim()
        Else
            Frname = ""
            LName = nm
        End If
    End Sub
End Class

And the LastFirst class is entirely analogous. The factory class is quite similar but makes use of the constructors.

Public Class NameFactory

    Public Function getNamer( _
        ByVal nm As String) As NameClass
        Dim i As Integer

        i = nm.indexOf(",")
        If i > 0 Then
            Return New LastFirst(nm)
        Else
            Return New FirstFirst(nm)
        End If
    End Function
End Class

You can see the difference in how these classes relate in Figure 9-4.

The VB7 Namer program, showing inheritance

Figure 9-4. The VB7 Namer program, showing inheritance

Factory Patterns in Math Computation

Most people who use Factory patterns tend to think of them as tools for simplifying tangled programming classes. But it is perfectly possible to use them in programs that simply perform mathematical computations. For example, in the Fast Fourier Transform (FFT), you evaluate the following four equations repeatedly for a large number of point pairs over many passes through the array you are transforming. Because of the way the graphs of these computations are drawn, the following four equations constitute one instance of the FFT “butterfly.” These are shown as Equations 1–4.

Factory Patterns in Math Computation
Factory Patterns in Math Computation
Factory Patterns in Math Computation
Factory Patterns in Math Computation

However, there are a number of times during each pass through the data where the angle y is zero. In this case, your complex math evaluation reduces to Equations (5–8).

Factory Patterns in Math Computation
Factory Patterns in Math Computation
Factory Patterns in Math Computation
Factory Patterns in Math Computation

Then we can make a simple factory class that decides which class instance to return. Since we are making Butterflies, we'll call our Factory a Cocoon.

'Class Cocoon
'get back right kind of Butterfly
Public Function getButterfly(y As Single) As Butterfly
 If y = 0 Then
    Set getButterfly = New addButterfly
 Else
    Set getButterfly = New trigButterfly
 End If
End Function

In this example, we create a new instance each time. Since there are only two kinds, we might create them both in advance and return them as needed.

'Class Cocoon1
Private addB As Butterfly, trigB As Butterfly
'-----
'create instances in advance
Private Sub Class_Initialize()
 Set addB = New addButterfly
 Set trigB = New trigButterfly
End Sub
'-----
'get back right kind of Butterfly
Public Function getButterfly(y As Single) As Butterfly
 If y = 0 Then
    Set getButterfly = addB
 Else
    Set getButterfly = trigB
 End If
End Function

Thought Questions

Thought Questions
  1. Consider a personal checkbook management program like Quicken. It manages several bank accounts and investments and can handle your bill paying. Where could you use a Factory pattern in designing a program like that?

  2. Suppose you are writing a program to assist homeowners in designing additions to their houses. What objects might a Factory be used to produce?

Programs on the CD-ROM

FactoryNamer The VB6 name factory
FactoryNamervbNetNamer The VB7 name factory
FactoryFFT A VB6 FFT example
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset