Chapter 5. The Abstract Factory Pattern

The Abstract Factory pattern is one level of abstraction higher than the Factory Method pattern. You can use this pattern to return one of several related classes of objects, each of which can return several different objects on request. In other words, the Abstract Factory is a factory object that returns one of several groups of classes. You might even decide which class to return from that group by using a Simple Factory.

One classic application of the Abstract Factory pattern is when your system needs to support multiple look-and-feel user interfaces, such as Windows 9x, Motif, and Macintosh. You tell the factory that you want your program to look like Windows, and it returns a GUI factory that returns Windows-like objects. Then when you request specific objects, such as buttons, check boxes, and windows, the GUI factory returns Windows instances of these visual interface components.

In Java 1.2, the pluggable look-and-feel classes accomplish this at the system level so that instances of the visual interface components are returned correctly once the program selects the type of look and feel. In the following code, we find the name of the current windowing system and then tell the pluggable look-and-feel (PLAF) Abstract Factory to generate the correct objects.

String laf = UIManager.getSystemLookAndFeelClassName();
try {
        UIManager.setLookAndFeel(laf);
    }
    catch(UnsupportedLookAndFeelException exc){
        System.err.println("Unsupported L&F: " + laf);
  }catch(Exception exc)
        System.err.println("Error loading " + laf);
  }

A GardenMaker Factory

Let's consider a simple example where you might want to use the Abstract factory in your application.

Suppose you are writing a program to plan the layout of gardens. These could be annual gardens, vegetable gardens, or perennial gardens. No matter the type of garden, we want to ask the same questions:

  1. What are good center plants?

  2. What are good border plants?

  3. What plants do well in partial shade?

...and probably many other plant questions that we'll omit in this simple example.

We want a base Garden class that can answer these questions.

public abstract class Garden {
   public abstract Plant getCenter();
   public abstract Plant getBorder();
   public abstract Plant getShade();
}

In this case, the Plant object just contains and returns the plant name.

public class Plant {
String name;
   public Plant(String pname) {
     name = pname;            //save name
   }
   public String getName() {
      return name;
   }
}

In Design Patterns terms, the abstract Garden class is the Abstract Factory. It defines the methods of a concrete class that can return one of several classes, in this case one each for center, border, and shade-loving plants. The Abstract Factory could also return more-specific garden information, such as soil pH and recommended moisture content.

In a real system, for each type of garden we would probably consult an elaborate database of plant information. In this example, we'll return one kind of plant from each category. So, for example, for the vegetable garden we write the following:

public class VeggieGarden extends Garden {
    public Plant getShade() {
        return new Plant("Broccoli");
    }
    public Plant getCenter() {
        return new Plant("Corn");
    }
    public Plant getBorder() {
        return new Plant("Peas");
    }
}

Similarly, we can create Garden classes for PerennialGarden and AnnualGarden. Each of these concrete classes is a Concrete Factory, since it implements the methods outlined in the parent abstract class. Now we have a series of Garden objects, each of which returns one of several Plant objects. This is illustrated in the class diagram in Figure 5.1.

The major objects in the Gardener program.

Figure 5.1. The major objects in the Gardener program.

We can easily construct Gardener, our abstract factory driver program, to return one of these Garden objects based on the radio button that a user selects, as shown in the user interface in Figure 5.2.

The user interface of the Gardener program.

Figure 5.2. The user interface of the Gardener program.

How the User Interface Works

This simple interface consists of two parts: the left side, which selects the garden type, and the right side, which selects the plant category. When you click on one of the garden types, this causes the program to return a type of garden that depends on which button you select. At first, you might think that we would need to perform some sort of test to decide which button was selected and then instantiate the right Concrete Factory class. However, a more elegant solution is to create a different ItemListener for each radio button as an inner class and have each one create a different garden type.

First, we create the instances of each Listener class.

     Veggie.addItemListener(new VeggieListener());
     Peren.addItemListener(new PerenListener());
     Annual.addItemListener(new AnnualListener());

Then we define the actual inner classes.

class VeggieListener implements ItemListener {
    public void itemStateChanged(ItemEvent e) {
        garden = new VeggieGarden();
        clearPlants();
    }
}
//--------------
class PerenListener implements ItemListener {
    public void itemStateChanged(ItemEvent e) {
        garden = new PerennialGarden();
        clearPlants();
    }
}
//--------------
class AnnualListener implements ItemListener {
    public void itemStateChanged(ItemEvent e) {
        garden = new AnnualGarden();
        clearPlants();
    }
}

Thus when a user clicks on one of the plant type buttons, the plant type is returned, and the name of that plant is displayed.

public void actionPerformed(ActionEvent e) {
    Object obj = e.getSource();
    if(obj == Center)
       setCenter();
    if(obj == Border)
       setBorder();
    if(obj == Shade)
       setShade();
    if(obj == Quit)
       System.exit(0);
}

You can avoid the if tests in the previous actionPerformed method by making three different ActionListener classes, but for a program this simple, this does not seem justified. You can examine the details of drawing the garden in the code on the CD-ROM.

Adding More Classes

One of the great strengths of the Abstract Factory pattern is that you can add new subclasses very easily. For example, if you need a GrassGarden or a WildflowerGarden, you can subclass Garden and produce these classes. The only real change that you'd need to make in any existing code is to add some way to choose these new kinds of gardens.

Consequences of the Abstract Factory Pattern

One of the main purposes of the Abstract Factory is that it isolates the concrete classes that are generated. The actual names of these classes are hidden in the factory and need not be known at the client level.

Because of the isolation of classes, you can change or interchange these product class families freely. Further, since you generate only one kind of concrete class, this system keeps you from inadvertently using classes from different families of products. However, adding new class families takes some effort because you must define new, unambiguous conditions that cause such a new family of classes to be returned.

While all of the classes that the Abstract Factory pattern generates have the same base class, nothing prevents some subclasses from having additional methods that differ from the methods of other classes. For example, a BonsaiGarden class might have a Height or WateringFrequency method that is not present in other classes. This creates the same problem as occurs in any subclasses. That is, you don't know whether you can call a class method unless you know whether the subclass is one that allows those methods. This problem has the same two solutions as does any similar case. Either you can define all of the methods in the base class, even if they don't always have an actual function. Or, if you can't change the base class, you can derive a new base class that contains all of the methods that you need and then subclass that for all of your garden types.

Thought Question

  1. Suppose that you are writing a program to track investments, such as stocks, bonds, metal futures, and derivatives. How could you use an Abstract Factory pattern?

Programs on the CD-ROM

Program Description

Abstract FactoryGardener.java

Launches the user interface given in this chapter and exercises the Abstract Factory pattern and the various Garden classes.
..................Content has been hidden....................

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