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.
The type of object needed depends on the contents of the external data or event. This article summarizes patterns that are related to creating objects. The issues that they address involve dynamically deciding which class to instantiate or to which objects an object will delegate a responsibility. These patterns tell us how to structure and encapsulate these decisions. This article discusses a pattern called Factory Method.
Factory Method
Sometimes, you need to create an object to represent external data or process an external event. The type of object needed depends on the contents of the external data or event. You don’t want the source of the data or event nor object’s clients to need to be aware of the actual type of object created. You accomplish that by encapsulating the decision of the class of object to create in its own class.
As an example, consider the problem of writing a framework for desktop applications. Such applications are typically organized around documents or files. Their operation usually begins with a command to create or edit a word processing document, spreadsheet, time line or whatever type of document or file the application is intended to work with.
A framework to support this type of application will include high-level support for common operations such as creating, opening, or saving documents. Such support will generally include a consistent set of methods to call when the user issues a command. For the purpose of this discussion, we will call the class providing the methods the Application class.
The Application class works through the interface and not with the programmer-provided class. |
Because the logic to implement most of these commands varies with the type of document, the Application class usually delegates most of the commands to some sort of document object. The logic in document objects for implementing these commands varies with the type of document. However, there are operations, such as displaying the title string for a document, that will be common to all document objects. This suggests an organization that includes an application-independent abstract Document class and application-specific subclasses for specific types of documents. Below is a class diagram that shows that organization.
Figure 1: Application Framework.
What neither the above diagram nor the preceding discussion show is how an Application object creates instances of application-specific document classes without itself being application specific. One way to accomplish this is for the programmer using the framework to provide a class that encapsulates logic for selecting and instantiating application-specific subclasses of the Document class. For the Application class to be able to call the programmer-provided class without having any dependencies on it, the framework should provide an interface that the programmer-provided class must implement. The interface would declare a method that the programmer provided class would implement to select and instantiate a class. The Application class works through the interface and not with the programmer-provided class. Below is a class diagram that includes that organization.
Figure 2: Application framework with Factory.
Using the organization shown in the above diagram, an Application object calls the createDocument method of an object that implements the DocumentFactoryIF interface. It passes a string to the createDocument method that allows it to infer which subclass of the Document class to instantiate. The Application class does not need to know the actual class of the object whose method it calls or which subclass of the Document class it instantiated.
Generalizing the classes in the preceding diagram into roles gives us the following class diagram that shows the general purpose of the Factory Method pattern.
Figure 3: Factory Method Pattern.
Here are descriptions of the roles classes and interfaces play in the Factory Method pattern:
- Product
- ConcreteProduct
- CreationRequester
- FactoryInterface
- Factory
The product class is the abstract superclass of objects produced by the Factory Method pattern. An actual class in this role is not normally called Product, but has a name like Document or Image.
This is any concrete class instantiated by the objects participating in the Factory Method pattern. If these classes share no common logic, then the product can be an interface instead of an abstract class. An actual class in this role would not normally be called ConcreteProduct, but have a name like RTFDocument or JPEGImage.
The creation requester is an application-independent class that needs to create application-specific classes. It does so indirectly through an instance of a factory class.
This is an application-independent interface. Objects that create Product objects on behalf of CreationRequester objects must implement this interface. Interfaces of this sort declare a method that can be called by a CreationRequester object to create concrete product objects. The method typically has a name like createDocument or createImage. The method takes whatever arguments are needed to deduce the class to instantiate. Interfaces filling this role will typically have a name like DocumentFactoryIF or ImageFactoryIF.
This is an application-specific class that implements the appropriate factory interface and has a method to create ConcreteProduct objects. Classes filling this role will typically have a name like DocumentFactory or ImageFactory.
An EncryptedSocket object works by first getting an Encryption object from the EncryptionFactoryIF object that is passed to its constructor. |
I will conclude this discussion of the Factory Method pattern with an example. Suppose you are developing an extension to the Socket class to encrypt the stream of bytes written to a socket and decrypt the bytes read from the socket. You call this class EncryptedSocket.
You want the EncryptedSocket class to support multiple encryption algorithms. Because of U.S. legal restrictions on the import and export of encryption software, you want to keep the EncryptedSocket class independent of the encryption classes being used.
The requirement that EncryptedSocket objects be able to work with multiple encryption algorithms without knowing in advance what classes will encapsulate those algorithms suggests the use of the Factory Method pattern. Here is a class diagram to show this:
Figure 4: Factory Method example.
Here is a description of the classes and interface used in this design:
- EncryptedSocket
- EncryptionFactoryIF
- EncryptionFactory
- Encryption
- RSAEncryption
- DESEncryption
This subclass of java.net.Socket fills the role of creation requester.
This interface fills the role of factory interface.
This class fills the role of factory class.
This class fills the role of product.
This class is in the role of concrete product.
This class is in the role of concrete product.
Let’s look at the code that implements these classes. Below is the code for the EncryptedSocket class. The EncryptedSocket class extends socket to encrypt/decrypt the stream of bytes that goes over the Net.
public class EncryptedSocket extends Socket {
private static Encryption crypt;
private Key key;
/**
* Constructor
* @param key The key to use for encryption and decryption. This
* object will determine the encryption technique to use
* by calling the key object’s getAlgorithm() method.
* @param factory The Factory object for creating Encryption objects.
* @exception NoSuchAlgorithmException if the key specifies an
* encryption technique that is not available.
*/
public EncryptedSocket(Key key, EncryptionFactoryIF factory)
throws NoSuchAlgorithmException {
this.key = key;
crypt = factory.createEncryption(key);
} // Constructor(Key, EncryptionFactoryIF)
/**
* Return an input stream that decrypts the inbound stream of bytes.
*/
public InputStream getInputStream() throws IOException {
return crypt.decryptInputStream(super.getInputStream());
} // getInputStream()
/**
* Return an output stream that encrypts the outbound stream of bytes.
*/
public OutputStream getOutputStream() throws IOException {
return crypt.encryptOutputStream(super.getOutputStream());
} // getOutputStream()
} // class EncryptedSocket
An EncryptedSocket object works by first getting an Encryption object from the EncryptionFactoryIF object that is passed to its constructor. It then accomplishes the encryption and decryption using the Filter pattern. It extends the getInputStream and getOutputStream methods so that the InputStream objects and OutputStream objects that they would otherwise return are filtered through objects created by the Encryption object.
Below is code for the EncryptionFactoryIF interface. All factory classes used to create instances of subclasses of Encryption must implement the EncryptionFactoryIF interface.
public interface EncryptionFactoryIF {
Below is the code for the EncryptionFactory class. Its createEncryption method creates an instance of an appropriate subclass of Encryption. Its createEncryption method selects a subclass of Encryption based on information it gets from the key object that is passed to the method.
/**
* Return an instance of the appropriate subclass of Encryption as
* determined from information provided by the given Key object.
*/
public Encryption createEncryption(Key key)
throws NoSuchAlgorithmException;
} // interface EncryptionFactoryIFpublic class EncryptionFactory {
public Encryption createEncryption(Key key)
throws NoSuchAlgorithmException{
String algorithm = key.getAlgorithm();
if (“DES”.equals(algorithm))
return new DESEncryption(key);
if (“RSA”.equals(algorithm))
return new RSAEncryption(key);
throw new NoSuchAlgorithmException(algorithm);
} // createEncryption(Key)
} // class EncryptionFactory
Finally, below is the code for the Encryption class. The Encryption class is the abstract superclass of classes that perform specific types of encryption or decryption.
abstract public class Encryption {
private Key key;
/**
* Constructor
* @param key The key to use to perform the encryption.
*/
public Encryption(Key key) {
this.key = key;
} // Constructor(Key)
…
/**
* This method returns an OutputStream writes encrypted bytes
* to the given OutputStream.
*/
abstract OutputStream encryptOutputStream(OutputStream out);
/**
* This method returns an InputStream that decrypts bytes
* from the given InputStream.
*/
abstract InputStream decryptInputStream(InputStream in);
} // class Encrypt
About the author
Mark Grand is the author of a series of books titled Patterns in Java. He is currently the chief architect of an application framework for e-commerce called EBox. Mark Grand can be contacted at mgrand@mindspring.com.