July 30, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Pattern Summaries: Abstract Factory Pattern

  • October 20, 2003
  • By Mark Grand
  • Send Email »
  • More Articles »

This is part of an ongoing series of articles in which I will summarize patterns from my "Patterns in Java" series of books.

The essence of a pattern is a reusable solution for a recurring problem. A complete pattern will also provide reasons to use or not use the solution, the consequences of using the solution, and suggestions on how to implement the solution. The summaries in these articles will just describe the essential problem and its solution.

Abstract Factory

Suppose you have the task of building a user interface framework that works on top of multiple windowing systems, like MS-Windows, Motif, or Mac OS. It must work on each platform with the platform's native look and feel. You organize it by creating an abstract class for each type of widget (text field, push button, list box, etc.) and then writing a concrete subclass of each of those classes for each supported platform. To make this robust, you need to ensure that the widget objects created are all for the desired platform. That is where the abstract factory comes into play.

An abstract factory class defines methods to create an instance of each abstract class that represents a user interface widget. Concrete factories are concrete subclasses of an abstract factory that implements its methods to create instances of concrete widget classes for the same platform.

In a more general context, an abstract factory class and its concrete subclasses organize sets of concrete classes that work with different but related products. For a broader perspective, consider another situation.

Suppose you are writing a program that performs remote diagnostics on computers made by a computer manufacturer called Stellar Microsystems. Over time, Stellar has produced computer models having substantially different architectures. Their oldest computers used CPU chips from Enginola that had a traditional complex instruction set. Since then, they have released three generations of computers based on their own reduced instruction set architectures called ember, super-ember, and ultra-ember. The core components used in these models perform similar functions but involve different sets of components.

For the program you are writing to know which tests to run and how to interpret the results, it will need to instantiate objects that correspond to each one of the core components in the computer being diagnosed. The class of each object will correspond to the type of component to be tested. That means you will have a set of classes for each computer architecture. There will be a class in each set corresponding to the same type of computer component. Because this situation fits the Abstract Factory pattern so well, you can use that pattern to organize the creation of objects that correspond to core computer components.

Here is a class diagram showing classes for only two types of components in only two architectures:


Figure 1. Abstract Factory example.

An instance of the Client class manages the remote diagnostic process. When it determines the architecture of the machine it has to diagnose, it calls the ArchitectureToolkit class's getFactory method. That static method returns an instance of a class such as EmberToolkit or EnginolaToolkit that corresponds to the architecture that the Client object passed to the getFactory method. The Client object can then use that toolkit object to create objects that model CPUs, MMUs, and other components of the required architecture.

Now we will consider the more general case. The following class diagram shows the roles that classes play in the Abstract Factory pattern.

Here is a class diagram that shows the roles that classes and interfaces play in the Abstract Factory pattern.


Figure 2. Abstract Factory, classes and interfaces example.

Client
Classes in the Client role use various widget classes to request or receive services from the product that the client is working with. Client classes only know about the abstract widget classes. They should have no knowledge of any concrete widget classes.
AbstractFactory
AbstractFactory classes define abstract methods for creating instances of concrete widget classes.
Abstract factory classes have a static method shown in the above diagram as getFactory. Another common name for that method is getToolkit. A Client object calls that method to get an instance of a concrete factory appropriate for working with a particular product.
ConcreteFactory1, ConcreteFactory2
Classes in this role implement the methods defined by their abstract factory superclasses to create instances of concrete widget classes. Client classes that call these methods should not have any direct knowledge of these concrete factory classes, but instead access singleton instances of these classes by calling a method of their abstract factory superclass.
WidgetA, WidgetB
Interfaces in this role correspond to a feature of a product. Classes that implement an interface in this role work with the product to which the interface corresponds.
Product1WidgetA, Product2WidgetA. . .
Classes in this role are concrete classes that correspond to a feature of a product that they work with. You can generically refer to classes in this role as concrete widgets.

An implementation issue for the Abstract Factory pattern is the mechanism the abstract factory class's getFactory method uses to select the class of the concrete factory it returns to client objects. The simplest situation is when client objects only need to work with one product during the run of a program. In that case, the abstract factory class will typically have a static variable set to the concrete factory class that is used for the duration of the program run. The abstract factory class's getFactory method can then just return an instance of that class.

If the abstract factory object will use information provided by the requesting client to select among multiple concrete factory objects, you can hard code the selection logic and choice of concrete factory objects in the abstract factory class. That strategy has the advantage of simplicity. It has the drawback of requiring a source code modification to add a new concrete factory class.

A different strategy is to use a Hashed Adapter pattern. It separates the selection logic for concrete factories from the data it uses to make the selection. It works by putting references to concrete factory classes, along with the information used to select them, into a data structure, which is typically a HashMap or a Hashtable. The data structure allows an abstract factory to select a concrete factory object by performing a lookup on the data structure. The advantage of using the data structure is that it is possible to devise schemes for building the data structure that allow an abstract factory to work with new concrete factory classes at runtime without any source code modification.

Here is some of the Java code that implements the design for remote computer diagnostics presented earlier in this article. The widget interfaces have the obvious structure:

public abstract class CPU {
    ...
} // class CPU

The concrete widget classes are simply classes that implement the widget interfaces:

class EmberCPU extends CPU {
    ...
} // class EmberCPU
Below is code for a concrete factory class that creates instances of classes to test ember architecture computers:
class EmberToolkit extends ArchitectureToolkit {
    public CPU createCPU() {
        return new EmberCPU();
    } // createCPU()

    public MMU createMMU() {
        return new EmberMMU();
    } // createMMU()
    ...
} // class EmberFactory

Below is the code for the abstract factory class:

public abstract class ArchitectureToolkit {
    private static final EmberToolkit emberToolkit = new
EmberToolkit();
    private static final EnginolaToolkit
enginolaToolkit 
      = new EnginolaToolkit();
...

    /**
     * Returns a concrete factory object that is an
instance of the
     * concrete factory class appropriate for the given
architecture.
     */
    static final ArchitectureToolkit getFactory(int
architecture) {
        switch (architecture) {
          case ENGINOLA:
            &nbs
p; return enginolaToolkit;

          case EMBER:
            &nbs
p; return emberToolkit;
            ...
        } // switch
        String errMsg =
Integer.toString(architecture);
        throw new
IllegalArgumentException(errMsg);
    } // getFactory()

    public abstract CPU createCPU() ;
    public abstract MMU createMMU() ;
    ...
} // AbstractFactory

Client classes typically create concrete widget objects using code that looks something like this: 
public class Client {
    public void doIt () {
        AbstractFactory af;
        af =
AbstractFactory.getFactory(AbstractFactory.EMBER);
        CPU cpu = af.createCPU();
        ...
    } // doIt
} // class Client

About the Author

Mark Grand is the author of a series of books titled "Patterns in Java." He is a consultant who specializes in object-oriented design and Java. He is currently working on a framework to create integrated enterprise applications.






Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel