Monday, July 04, 2005

The many ways to skin a cat

I just ran across yet another way to write the Factory pattern in Java:

public class Factory {
    private static interface Lineman {
        Thing create(final Widget widget);
    }

    private static Lineman[] LINEMEN = {
        new Lineman() {
            public Thing create(final Widget widget) {
                return new CoolThing(widget);
            }
        },
    };

    public static enum Things {
        COOL;

        private final Lineman lineman;

        Things() {
            lineman = LINEMEN[ordinal()];
        }

        public Thing create(final Widget widget) {
            return lineman.create(widget);
        }
    }
}

User code looks likes this:

final Thing thing = Factory.Things.COOL.create(widget);

Although overcomplicated-looking, it does has several useful points.

User code is constrained with enums as to the types of objects created by the factory. No business with class names or special strings. And modern editors have full code completion for the enums.

Secondly, code maintenance for the factory is simple. When you add a new type, add a new enum and a corresponding entry in the static array. This sort of work is straight-forward to automate with XDoclet or some custom build step.

UPDATE: I should point out that suitable imports simplify the user code further:

final Thing thing = COOL.create(widget);

And reflection can automate futher eliminating the need for a static array of factory methods if the classes follow some pattern in their naming, but with the caveats that go with reflection (ignoring exceptions):

public Thing create(final Widget widget) {
    return (Action) Class.forName(getThingClassName(name()))
            .getConstructor(Things.class, Widget.class)
            .newInstance(this, widget);
}

1 comment:

David Kemp said...

I am not entirely convinced that this is a sensible use of enums but, if you have to, I think it is simpler to take advantage of being able to over-ride methods in each enum.
e.g.

public enum Factory {
COOL {
public Thing create(Widget widget) {
return new CoolThing(widget);
}
},

HORRIBLE {
public Thing create(Widget widget) {
return new HorribleThing(widget);
}
};

public abstract Thing create(Widget widget);

}