Chapter 8. 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 8-1. In this figure, 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.

Figure 8-1. A Simple Factory pattern

image

Sample Code

Let’s consider a simple C# 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 names.

This is a pretty simple 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 class that takes the name string in using the constructor and allows you to fetch the names back.


//Base class for getting split names
      public class Namer {
             //parts stored here
             protected string frName, lName;

             //return first name
             public string getFrname(){
                    return frName;
             }
             //return last name
             public string getLname() {
                    return lName;
             }
      }

Note that our base class has no constructor for setting the name.

The Two Derived Classes

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


public class FirstFirst : Namer {
      public FirstFirst(string name)    {
      int i = name.Trim().IndexOf (" ");
      if(i > 0) {
             frName = name.Substring (0, i).Trim ();
             lName = name.Substring (i + 1).Trim ();
      }
      else {
             lName = name;
             frName = "";
      }
   }
}

And in the LastFirst 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.


public class LastFirst : Namer {
      public LastFirst(string name)     {
             int i = name.IndexOf (",");
             if(i > 0) {
                    lName = name.Substring (0, i);
                    frName = name.Substring (i + 1).Trim ();
             }
             else {
                    lName = name;
                    frName = "";
             }
      }
}

In both cases, we store the split name in the protected lName and frName variables in the base Namer class. Note that we don’t even need any getFrname or getLname methods, since we have already written them in the base class.

Building the Simple Factory

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


public class NameFactory  {
      public NameFactory() {}

      public Namer getName(string name) {
             int i = name.IndexOf (",");
             if(i > 0)
                    return new LastFirst (name);
             else
                    return new FirstFirst (name);
      }
}

Using the Factory

Let’s see how we put this together. In response to the Compute button click, we use an instance of the NameFactory to return the correct derived class.


private void btCompute_Click(
             object sender, System.EventArgs e) {
      Namer nm = nameFact.getName (txName.Text );
      txFirst.Text = nm.getFrname ();
      txLast.Text = nm.getLname ();
}

Then we call the getFrname and getLname methods to get the correct splitting of the name. We don’t need to know which derived class this is. The Factory has provided it for us, and all we need to know is that it has the two get methods. The complete class diagram is shown in Figure 8-2.

Figure 8-2. The Name Factory program

image

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 8-3.

Figure 8-3. The Namer program executing

image

You type in a name and then click on the “Get name” 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.

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 14.

image

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 58.

image

We first define a class to hold complex numbers.


public class Complex       {
      float real;
      float imag;
//---------------------------------
public Complex(float r, float i) {
      real = r; imag = i;
}
//---------------------------------
public void setReal(float r) { real = r;}
//---------------------------------
public void setImag(float i) {imag= i;}
//---------------------------------
public float getReal() {return real;}
//---------------------------------
public float getImag() {return imag;}
}

Our basic Buttefly class is an abstract class that can be filled in by one of the implementations of the Execute command.


public abstract class Butterfly {
      float y;
      public Butterfly() {
      }
      public Butterfly(float angle) {
             y = angle;
      }
      abstract public void Execute(Complex x, Complex y);
}

We can then make a simple addition Butterfly class that implements the add and subtract methods of Equations 58.


class AddButterfly : Butterfly  {
   float oldr1, oldi1;
      public AddButterfly(float angle) {
             }
      public override void Execute(Complex xi, Complex xj) {
             oldr1 = xi.getReal();
             oldi1 = xi.getImag();
             xi.setReal(oldr1 + xj.getReal());
             xj.setReal(oldr1 - xj.getReal());
             xi.setImag(oldi1 + xj.getImag());
             xj.setImag(oldi1 - xj.getImag());
      }
}

The TrigButterfly class is analogous except that the Execute method contains the actual trig functions of Equations 14.


public class TrigButterfly:Butterfly  {
      float y, oldr1, oldi1;
      float cosy, siny;
      float r2cosy, r2siny, i2cosy, i2siny;

      public TrigButterfly(float angle) {
             y = angle;
             cosy = (float) Math.Cos(y);
             siny = (float)Math.Sin(y);
      }
      public override void Execute(Complex xi, Complex xj) {
             oldr1 = xi.getReal();
             oldi1 = xi.getImag();
             r2cosy = xj.getReal() * cosy;
             r2siny = xj.getReal() * siny;
             i2cosy = xj.getImag()*cosy;
             i2siny = xj.getImag()*siny;
             xi.setReal(oldr1 + r2cosy +i2siny);
             xi.setImag(oldi1 - r2siny +i2cosy);
             xj.setReal(oldr1 - r2cosy - i2siny);
             xj.setImag(oldi1 + r2siny - i2cosy);
      }
}

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. We never really need to instantiate Cocoon, so we will make its one method static.


public class Cocoon      {
      static public Butterfly getButterfly(float y) {
             if (y != 0)
                    return new TrigButterfly(y);
             else
                    return new addButterfly(y);
      }
}

Summary

We have seen that the simple Factory returns instances of classes that have the same methods. They may be instances of different derived subclasses, or they may in fact be unrelated classes that just share the same interface. Either way, the methods in these class instances are the same and can be used interchangeably.

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

image

..................Content has been hidden....................

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