Mutant World

Sunday, April 24, 2005

Enums as factories

I started to enjoy very much J2SE 5, especially generics, enums and the java.util.concurrent package.

I still don't get static imports, but overall J2SE 5 is been a good experience.

One thing I found nice about enums is the possibility to use them as factories in a simple way.

For example, Webwork 2 can be configured to use 3 different multipart parsers.
The configuration option is called webwork.multipart.parser and can have one of 3 possible values: pell, jakarta or cos.
The 3 implementation classes extends the class com.opensymphony.webwork.dispatcher.multipart.MultiPartRequest so it's possible to write the following enum:

public enum MultipartParsers
{
PELL(PellMultiPartRequest.class),
JAKARTA(JakartaMultiPartRequest.class),
COS(CosMultiPartRequest.class);

private final Class<? extends MultiPartRequest> parserClass;

private MultipartParser(Class<? extends MultiPartRequest> parserClass)
{
this.parserClass = parserClass;
}

public MultiPartRequest newInstance()
{
return parserClass.newInstance();
}
}

Using this class is very simple, given the following configuration:

webwork.multipart.parser=pell

A Configuration class can read the configuration, and creating the multipart parser is then very easy:

String parser = Configuration.getString("webwork.multipart.parser");
MultiPartRequest multipart = MultiPartParsers.valueOf(parser.toUpperCase()).newInstance();

Note the MultiPartParsers.valueOf() method, which is generated by the compiler for each enum class: it takes a string and returns the enum instance whose name matches the string.
Using MultiPartParsers.valueOf() avoid switch statements or chained if/else statements.
The possibility to use more verbose values (like 'pell' or 'jakarta') is definitely more appealing than using integer values (like '1' or '2') to distinguish parsers.

I have used this pattern also for commands, and the newInstance() method can take parameters to initialize the newly created instance, or even a vararg parameters.

public enum Commands
{
UPDATE(UpdateCommand.class),
INSERT(InsertCommand.class),
...
DELETE(DeleteCommand.class);

private final Class<? extends Command> commandClass;

private MultipartParser(Class<? extends Command> commandClass)
{
this.commandClass = commandClass;
}

public Command newInstance(Configuration config)
{
Command command = commandClass.newInstance();
command.configure(config);
return command;
}
}