Factory Method with Java
Factory method is a creational design pattern which solves the problem of creating product objects without specifying their concrete classes.
Factory Method defines a method, which should be used for creating objects instead of direct constructor call (new operator). Subclasses can override this method to change the class of objects that will be created.
Structure
Factory Method Java Example Code
In this example, we will simulate a game application that have different worlds, every world is a vast map with it’s own creatures and logic. We will have hundreds of different worlds in the future. The Difficulty : In our main program, we want to load a world. We do not know how many worlds we will have in the future and we do not want to change code of the main logic just because we added a different world to the game. Solution with Factory Method : The Factory Method pattern suggests that you replace direct object construction calls (using the new operator) with calls to a special factory method. Don’t worry: the objects are still created via the new operator, but it’s being called from within the factory method. Objects returned by a factory method are often referred to as products.
The Java Code
Interface : IWorld (Product)
package Worlds;
// The interface implemented by all the concrete world classes.
// The main app should work only with this interface and not with concrete implementing classes.
public interface IWorld {
void SpawnWorld();
void SpawnCreatures();
// here goes all the functions that we way need in the main app.
String GetWorldName();
}
Class : Africa (Concrete Product 1)
package Worlds;
public class Africa implements IWorld {
@Override
public void SpawnWorld() {
System.out.println("Run complex Africa logic");
}
@Override
public void SpawnCreatures() {
System.out.println("Spawn lions, girafs, elephants.");
}
@Override
public String GetWorldName() {
return "Africa";
}
}
Class : Andromeda (Concrete Product 2)
package Worlds;
public class Andromeda implements IWorld {
@Override
public void SpawnWorld() {
System.out.println("Run complex Andromeda logic");
}
@Override
public void SpawnCreatures() {
System.out.println("Spawn UFOs, SpaceShips, BlackHoles");
}
@Override
public String GetWorldName() {
return "Andromeda";
}
}
Class : America (Concrete Product 3)
package Worlds;
public class America implements IWorld {
@Override
public void SpawnWorld() {
System.out.println("Run complex America logic");
}
@Override
public void SpawnCreatures() {
System.out.println("Spawn Bears, Ducks, Wolves.");
}
@Override
public String GetWorldName() {
return "America";
}
}
Class : Creator (Base Factory)
package Factory;
import Worlds.IWorld;
// The base creator that should be extended by all concrete factory classes.
// The main app should only work with this class without having to know any concrete extending class
public abstract class Creator {
// Available worlds
public enum Worlds {Andromeda, Africa, America};
// Factory Method will be overridden by subclasses
public abstract IWorld WorldFactoryMethod();
// Static function to create a factory depending on the string parameter
// This allows us to return any factory without the main program ever knowing the concrete factory classes
public static Creator GetFactory(String p_WorldName) {
Creator c;
switch (p_WorldName) {
case "africa" : c = new AfricaFactory(); break;
case "andromeda" : c = new AndromedaFactory(); break;
default : c = new AmericaFactory();
}
return c;
}
}
Class : AfricaFactory (Concrete factory 1)
package Factory;
import Worlds.Africa;
import Worlds.IWorld;
public class AfricaFactory extends Creator {
@Override
public IWorld WorldFactoryMethod() {
// Here goes any extra logic that may be related to African world creation
return new Africa();
}
}
Class : AmericaFactory (Concrete factory 2)
package Factory;
import Worlds.America;
import Worlds.IWorld;
public class AmericaFactory extends Creator {
@Override
public IWorld WorldFactoryMethod() {
// Here goes any extra logic that may be related to America world creation
return new America();
}
}
Class : AndromedaFactory (Concrete factory 3)
package Factory;
import Worlds.Andromeda;
import Worlds.IWorld;
public class AndromedaFactory extends Creator {
@Override
public IWorld WorldFactoryMethod() {
// Here goes any extra logic that may be related to Andromeda world creation
return new Andromeda();
}
}
Class : ConsoleApp (The Client)
import Factory.Creator;
import Worlds.IWorld;
import java.util.Scanner;
public class ConsoleApp {
public static void main(String[] args) {
// Ask the user to pick a world to play in
System.out.print("console app Started. \nWhich world to load (");
// showing available worlds to pick from
for (Creator.Worlds w: Creator.Worlds.values())
System.out.print(w.name() + ", ");
System.out.print(") ?");
// User's input to define which map to spawn
Scanner s = new Scanner(System.in);
String worldName = s.next();
// Passing the mapName to the Creator's static function to get the correct factory
Creator worldCreator = Creator.GetFactory(worldName.trim().toLowerCase());
// Getting the world to work with without knowing it's concrete class
IWorld world = worldCreator.WorldFactoryMethod();
// I don't know what world concrete class is, but I can work with it
System.out.println("Welcome to " + world.GetWorldName() + " World");
world.SpawnWorld();
world.SpawnCreatures();
}
}