The two concrete Reporter types are shown next. Each class implements the Reporter interface, allowing them to be returned by the ReporterFactory. In the writeln method, the ConsoleReporter simply calls the System.out.println method to echo the message to the console. The FileReporter is slightly more complex, as a File is created in the constructor. The file is written to in the writeln method.
package com.foley.reporter;
/**
* A Reporter that writes to the console.
*
* @author Mike Foley
**/
class ConsoleReporter implements Reporter {
/**
* writeln, from Reporter
*
* Method to write a message to the reporting system.
* Write the message to the console.
*
* @param message The message to be written.
**/
public synchronized void writeln( String message ) {
System.out.println( message );
}
} // ConsoleReporter
package com.foley.reporter;
import java.io.File;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* A Reporter that writes to a file. The file
* written to is hard coded to 'Reporter.txt'
* in the current directory.
*
* @author Mike Foley
**/
class FileReporter implements Reporter {
/**
* The file and stream to Report to.
**/
File outputFile;
DataOutputStream outputStream;
/**
* Construct a FileReporter.
*
* @exception IOException If the file can not be opened for writing.
**/
FileReporter() throws IOException {
outputFile = new File( "Reporter.txt" );
outputStream = new DataOutputStream(
new FileOutputStream( outputFile ) );
}
/**
* writeln, from Reporter
*
* Method to write a message to the reporting system.
* Write the message to the Reporter file.
*
* @param message The message to be written.
* @exception IOException If the message can not be written.
**/
public synchronized void writeln( String message )
throws IOException {
outputStream.writeBytes( message + "\n" );
}
} // FileReporter
The classes presented to this point are all contained in the Reporter package. Combined, they implement a parameterized factory. The last example in this section contains an application to obtain a handle to a Reporter and write messages to it. The application requires a single parameterthe type of Reporter to create. After testing the command line argument, the Reporter of the requested type is obtained from the ReporterFactory. Notice the use of the type-safe constant when requesting the Reporter. A few messages are written with the desired Reporter.
The test application is in a different package than the Reporter itself. This is common for parameterized factories. The package interface and factory are public. However, the Concrete classes that implement the package interface are not public. Thus, the only methods of these classes exposed to clients are those that are defined in the package interface, Reporter in this example.
package com.foley.test;
import java.io.IOException;
import com.foley.reporter.Reporter;
import com.foley.reporter.ReporterFactory;
/**
* A simple class to test the Reporter package.
*
* @author Mike Foley
**/
public class TestReporter extends Object {
/**
* Create a Reporter of the given type, and echo
* a few lines of text to it. The type is determined
* by the program argument as follows:
*
* file FileReporter
* console ConsoleReporter
*
**/
public static void main( String[] args ) {
//
// Ensure we have an argument.
//
if( args.length != 1 ) {
usage();
System.exit( -1 );
}
//
// Create the proper Reporter.
//
Reporter reporter = null;
try {
if( args[0].toLowerCase().equals( "file" ) ) {
reporter = ReporterFactory.getReporter( Reporter.FILE );
} else if( args[0].toLowerCase().equals( "console" ) ) {
reporter = ReporterFactory.getReporter(Reporter.CONSOLE);
} else {
usage();
System.exit( -2 );
}
reporter.writeln( "This is a simple test of the Reporter" );
reporter.writeln(
"It will be written to a file, or the console" );
} catch( IOException io ) {
System.out.println( "Reporter system error." );
io.printStackTrace();
System.exit( -3 );
}
} // main
/**
* Write a simple usage message to the console.
**/
private static void usage() {
System.out.println( "usage:\tTestReporter reporterType" );
System.out.println( "\twhere reporterType is console or file" );
} // usage
} // TestReporter
An example of the parameterized factory pattern in the JFC is the Border package. The Border package defines a Border interface that specifies the methods all borders must implement. The BorderFactory class contains public static methods for obtaining a handle to concrete borders that can be used for the calling classs components.
The Border package in the JFC is not a pure parameterized factory pattern. It allows the classes in the Border package to be instantiated directly. However, this is discouraged. The reason for this is more practical than just the desire for design purity. In some situations, borders may be shared among clients. If the BorderFactory is used to obtain a reference to the desired border, the factory can share borders when appropriate. The current implementation of the BorderFactory class shares Bevel and Etched borders that use the default colors. In the JFC, the BorderFactory lives in the Swing package instead of the Border package.
A complete reference to borders and the BorderFactory is given in Chapter 5, Basic Components. Borders are also used in examples throughout this book.
|