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(); } }