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 with Java by Younes Rabdi

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