4
\$\begingroup\$

I am working on a very simple game and thought it would be a good opportunity to learn and use the Factory Method pattern for creating game objects. I studied the pattern but I'm a bit confused about the Java implementation so thought I would share what I have come up with and get some opinions.

Please keep in mind that I am basing this design on the example from "Design Patterns... by the GOF" so we will use the following pieces: Product, ConcreteProduct, Creator, and ConcreteCreator.

Product (Interface for Game Objects)

public interface GameObject {
   public abstract void doSomeStuff();  
}

ConcreteProducts (Implements Product Interface, 1 for each Game Object)

public class PlayerImpl implements GameObject {    
    @Override
    public void doSomeStuff() {
        System.out.println("I am a player object!");
    }    
}

public class EnemyImpl implements GameObject {    
    @Override
    public void doSomeStuff() {
        System.out.println("I am an Enemy, you better run!");
    }    
}

Creator (Interface that defines how to create objects)

This is the one I'm most confused about, seems un-necessary unless instead of an Interface it was some Abstract class, but even then seems kind of pointless to use a Creator in this design pattern?

 public interface GameObjectFactory {    
    public abstract GameObject createGameObject(GameObjectFactoryImpl.Type type);    
}

ConcreteCreator (Implements Creator, creates objects)

The ConcreteCreator will have a nested enumeration to help the client decide which player types are available. We will throw an exception if for some reason there is a game object type in the enumeration that is not handled in the factory method.

public class GameObjectFactoryImpl implements GameObjectFactory{

    // A helper enum that clients will use to create game objects
    public enum Type {
        PLAYER, ENEMY;
    }

    public GameObject createGameObject(GameObjectFactoryImpl.Type type) {

        switch (type) {    
        case PLAYER:
            return new PlayerImpl();    
        case ENEMY:
            return new EnemyImpl();    
        default:
            throw new RuntimeException("Unsupported object type!");
        }

    }
}

Client

public class Game {

    private static GameObjectFactory gameObjectFactory = new GameObjectFactoryImpl();

    public static void main(String args[]){

        GameObject player = gameObjectFactory.createGameObject(GameObjectFactoryImpl.Type.PLAYER);
        GameObject enemy = gameObjectFactory.createGameObject(GameObjectFactoryImpl.Type.ENEMY);

        player.doSomeStuff();
        enemy.doSomeStuff();
    }

}
\$\endgroup\$
  • \$\begingroup\$ What problem does the Factory pattern solve? You need to "feel" the pain of the problem a bit before you seek to adding all this complexity of the pattern. Your example shows new PlayerImpl() is what actually happens when there's a new player. That's way less code than gameObjectFactory.createGameObject(GameObjectFactoryImpl.Type.PLAYER); - ask yourself what are you gaining by adding this. Usually, you want to apply this pattern when the code to create the object is complex (and so it's encapsulated in the factory method). Your example doesn't suffer from the problem this pattern solves. \$\endgroup\$ – Fuhrmanator Apr 19 '14 at 12:59
4
\$\begingroup\$

The code you present is neat, well structured, etc. (unfortunately it is dangerously close to being off-topic since it is hypothetical code, and it does not work - where does the Type come from in the interface's public abstract GameObject createGameObject(Type type); ? ).

The neat code is really useful because it makes it easy to home in on the critical deviation you have made from the 'idiomatic' way of doing the Factory pattern in Java.

This is (often) wrong:

public interface GameObjectFactory {    
    public abstract GameObject createGameObject(Type type);    
}

Your narrative is closer-to-home: "... seems un-necessary unless instead of an Interface it was some Abstract class"

The factory is often a combination of static methods, and an interface. Sometimes the interface and the static methods are combined on to a single abstract class, and sometimes they are separate. What you are missing is the static methods for creating the Factory instance.

Note: many factory-pattern implementations in Java do not use the word Factory in the name....

So, there are factories that separate the static methods in a different class from the interface, and factories that have the static methods and the interface methods in the same abstract class.

Abstract Class way

The Abstract class is the most recognizable way of doing this in Java. It is often most easily identified by having static methods like newInstance(). The Abstract class has abstract methods which are the factory methods, but it also has factory-factory methods for creating the factory!!!! (yes, really). The factory-factory methods are normally implemented as static concrete methods. In your example code, the GameObjectFactory and parts of the GameObjectFactoryImpl would be merged as:

public abstract class GameObjectFactory {

    // A helper enum that clients will use to create game objects
    // Note that this enum is declared as part of the 'interface' for the factory,
    // not as part of one of the actual fctory implementations.
    public enum Type {
        PLAYER, ENEMY;
    }

    // factory factory method here ('default' factory implementation):
    public static final GameObjectFactory newInstance() {
        // look for an implementation of the factory in some known/default place
        String implName = ....; // default implementation...
        return newInstance(implName);
    }

    // factory factory method here ('specific' factory implementation):
    public static final GameObjectFactory newInstance(String specificImpl) {
        // fancy code to create a new instance .... perhaps reflection?
        GameObjectFactory ret = .......
        return ret;
    }

    // factory method here....
    public abstract GameObject createGameObject(Type type);

}

The concrete implementation of the Abstract GameObjectFactory is somewhere else (often un-documented).

Examples of this type of factory pattern are:

Separate Static class, and interface

In this case, there is normally a class with only static methods, and an interface that is returned from these methods. For your code, it would require three classs (the enum would be separate)....

// A helper enum that clients will use to create game objects
// Note that this enum is declared as part of the 'interface' for the factory,
// not as part of one of the actual fctory implementations.
public enum Type {
    PLAYER, ENEMY;
}

public interface GameObjectFactory {
    // factory method here....
    public GameObject createGameObject(Type type);
}


public final class GameObjectFactoryFactory {


    // factory factory method here ('default' factory implementation):
    public static final GameObjectFactory newInstance() {
        // look for an implementation of the factory in some known/default place
        String implName = ....; // default implementation...
        return newInstance(implName);
    }

    // factory factory method here ('specific' factory implementation):
    public static final GameObjectFactory newInstance(String specificImpl) {
        // fancy code to create a new instance .... perhaps reflection?
        GameObjectFactory ret = .......
        return ret;
    }

}

Examples of this type of factory are:

\$\endgroup\$
  • \$\begingroup\$ Thanks very much for your detailed review. Type worked in my codebase because I had it imported in the .java file. I edited the above code sample to include GameObjectFactoryImpl.Type instead. I'm a bit confused about the factory-factory, why not just hardcode that particular method to return a default type directly, rather than delegating it to an overloaded version? I guess a default return type might not always be ideal, can you provide an example of something that you have seen that makes sense as a default return type of a Factory-Factory method? \$\endgroup\$ – Shijima Apr 18 '14 at 0:38
  • \$\begingroup\$ SAX XML Parser newInstance() is possibly the more complicated factory-factory method... \$\endgroup\$ – rolfl Apr 18 '14 at 0:50
4
\$\begingroup\$

This pattern can be difficult to understand outside of an application with many modes of operation. There are two advantages to using the factory patterns: you can swap in different implementations, and you can vary the construction parameters without changing client code.

Where do these advantages shine? Testing! In the case of a game, randomness is a good example. When playing the game for real, you want your monsters to vary their behavior randomly. But when testing, you need to be able to choose from their possible behaviors. The real factory will initialize each monster with a real random number generator, but tests must be able to specify the behavior choices.

Take this example monster action:

public void chooseCombatAction() {
    if (isAfraid()) {
        return Action.RUN;
    }
    else {
        return Action.ATTACK;
    }
}

private boolean isAfraid() {
    return random.nextInt(10) > 7;
}

To test chooseCombatAction, you need to provide a mock random number generator. But if the constructor creates it, you can't. Instead, have the factory provide it, and create an implementation that takes one with pre-configured values. Now you can give that factory to the root game object that uses the factory to create monsters.

Style Note: Drop implied modifiers--such as public and abstract on interface members--wherever possible. Every modifier adds context that must be groked.

\$\endgroup\$
  • \$\begingroup\$ Thanks very much for the review! I know that public abstract will be groked (nice terminology) but there's no performance penalty for keeping them is there? \$\endgroup\$ – Shijima Apr 18 '14 at 0:28
  • \$\begingroup\$ @Shijima As you know, every in race member is public and either abstract (methods) or final (fields). Reading those modifiers costs a small amount of developer time which happens more often than writing. Omitting them leaves your interfaces crisp and clean IMHO. \$\endgroup\$ – David Harkness Apr 18 '14 at 1:26
  • \$\begingroup\$ Testing is a great context for factory. You might want to edit to say that mocking random allows to generate the numbers that are in boundary tests. \$\endgroup\$ – Fuhrmanator Apr 19 '14 at 13:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.