Design Patterns: Factory Method

Factory Method Design Pattern

Factory Method is one of the most used Design Patterns in Java. Part of the Creational Patterns, it provides a way to encapsulate the creation of objects.

Intent

Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

Problem

One of the Object Oriented Principles is to “Code against interfaces, not implementations”. Using the “new” operator in our code we create an object and we are actually coding to implementations.

Let’s see a simple example.

    Vehicle vehicle;
	
	if(sunny && close) {
		vehicle = new Bicycle();
	}
	else if(sunny && far) {
		vehicle = new Motorcycle();
	}
	else {
		vehicle = new Car();
	}

While the “new” operator is a fundamental part of Java and the only way to instantiate an object, we can see in this example that it can lead to error-prone and unmaintainable code if not used correctly. If you want to change a vehicle or add a new one you would have to modify the code and add or remove if cases.

We can use the Factory Method Pattern in the following cases:

  • A class can’t predict the class of objects it must create.
  • A class wants its subclasses to define the objects it creates.
  • A class delegates the responsibility of creating and localizing the object to one of the several helper classes.

Solution

We need to decouple and encapsulate the object creation from the creator class. The Factory Method is an abstract method declared in the creator class which every subclass has to implement to produce a concrete product which is also a subclass of the product. Using polymorphism the creator class doesn’t know what concrete product is returned.

Let’s see the structure to get a better look.

Structure

Factory Method

Implementation

For the implementation, we will create a simulation of a Blacksmith Store in an RPG Game which sells various items to the character.

Let’s start with the product.

public abstract class Item {

    String name;
    Double weight;
    Double price;
    Integer level;
    String enhancement;

    void prepare(){
        System.out.println("Starting preparations...");
        System.out.println("Gathering all the components...");
        System.out.println("Melting iron...");
        System.out.println("Cutting wood...");
        System.out.println("Adding enhancement...");
        System.out.println(name + " is ready! " + enhancement);
        System.out.println("Weight: " + weight + " Price: " + price);
    }
}
We have an abstract class Item which contains some common attributes for an RPG item and a prepare function which prints some generic steps for the preparation of the item. It would make more sense to declare the variables protected and provide only getters but for the sake of the example, it’s better to keep it simple.
Next, two concrete products.
public class Sword extends Item {

    Double attack;

    public Sword(String enhancement, Integer level) {

        switch(level){
            case 2:
                swordLevel2();
            default:
                swordLevel1();
        }

        name = "Sword lvl " + level;
        this.enhancement = "Attack: " + attack + ". " + enhancement;

        prepare();
    }
    
    private void swordLevel1(){
        weight = 5.5;
        price = 40.0;
        attack = 13.0;
    }

    private void swordLevel2(){
        weight = 6.5;
        price = 56.0;
        attack = 21.0;
    }
}
public class Shield extends Item{

    Double defense;

    Shield(String enhancement, Integer level){

        switch (level){
            case 2:
                sheildLevel2();
            default:
                sheildLevel1();
        }

        name = "Shield lvl " + level;
        this.enhancement = "Defence: " + defense + ". " +  enhancement;

        prepare();
    }

    private void sheildLevel1(){
        weight = 7.5;
        price = 35.0;
        defense = 10.0;
    }
    private void sheildLevel2(){
        weight = 8.7;
        price = 48.0;
        defense = 17.0;
    }
}

Sword and Shield class extend the Item class and add an attack and a defense variable respectively. There are also some initializing functions regarding the level of the item. You could use the constructor to pass all the variables and move the logic of initializing the variables outside of the classes. I chose this approach to keep the factories cleaner.

Now, let’s see the factories. First, the Abstract Factory.

public abstract class ItemFactory {

    // Factory Method
    public abstract Item createItem(String type);

    public void sellItem(String type){

        Item item = createItem(type);
        System.out.println("Item sold for " + item.price + " coins!");
    }
}

Here we see the actual factory method createItem(String type). This function must be implemented by all the concrete factories. We also have a sellItem method that just prints out that the item is sold. The most important point here is that the factory method doesn’t know anything about the object that is returned apart from the fact that it is an Item.

Next, two concrete factories.

BlacksmithStoreLvl1

public class BlacksmithStoreLvl1 extends ItemFactory {

    @Override
    public Item createItem(String type) {

        switch (type){
            case "sword":
                return new Sword(getRandomEnhancement(), 1);
            case "shield":
                return new Shield(getRandomEnhancement(), 1);
            default:
                System.out.println("Sorry not available.");
                return null;
        }
    }

    private String getRandomEnhancement(){

        // Level 1 Enhancements
        String[] enhancements = {
                "Do 3% more damage against dragons.",
                "Adds 3% resistance against magic.",
                "5% increased chance for critical strike."
        };

        int randomNum = ThreadLocalRandom.current().nextInt(0, 3);

        return enhancements[randomNum];
    }
}

BlacksmithStoreLvl2

public class BlacksmithStoreLvl2 extends ItemFactory {

    @Override
    public Item createItem(String type) {
        switch (type){
            case "sword":
                return new Sword(getRandomEnhancement(), 2);
            case "shield":
                return new Shield(getRandomEnhancement(), 2);
            default:
                System.out.println("Sorry not available.");
                return null;
        }
    }

    private String getRandomEnhancement(){

        // Level 2 Enhancements
        String[] enhancements = {
                "Do 6% more damage against dragons.",
                "Adds 6% resistance against magic.",
                "10% increased chance for critical strike."
        };

        int randomNum = ThreadLocalRandom.current().nextInt(0, 3);

        return enhancements[randomNum];
    }
}

As you see the factories are pretty much the same again to keep it simple but the point here is that every concrete factory can have its own logic and ways to instantiate the object. I made 2 different versions of a blacksmith store. The level 1 produces starter items and the level 2 a little more advanced. In order to give a purpose to the concrete factories, I added the enhancements which they randomly choose. Remember that the level stats for the variables are inside the concrete products(Sword, Shield).

The concrete factories are actually why the Factory Method Pattern is so powerful. Each concrete factory can implement different goals. E.g. a randomItemFactory can return a random concrete product etc. The actual creation of the object is decoupled and encapsulated.

public class FactoryTest {

    public static void main(String[] args){

        ItemFactory blacksmithlvl1 = new BlacksmithStoreLvl1();
        ItemFactory blacksmithlvl2 = new BlacksmithStoreLvl2();

        blacksmithlvl1.sellItem("sword");
        blacksmithlvl2.sellItem("shield");
    }
}

The outcome of the above code:

Starting preparations...
Gathering all the components...
Melting iron...
Cutting wood...
Adding enhancement...
Sword lvl 1 is ready! Attack: 13.0. 5% increased chance for critical strike.
Weight: 5.5 Price: 40.0
Item sold for 40.0 coins!
Starting preparations...
Gathering all the components...
Melting iron...
Cutting wood...
Adding enhancement...
Shield lvl 2 is ready! Defence: 10.0. 10% increased chance for critical strike.
Weight: 7.5 Price: 35.0
Item sold for 35.0 coins!

It’s obvious that in your code you wouldn’t have such a simple use. Maybe you could decide which Blacksmith Store to use according to the character level or coins. In either case, you would treat the object as an Item and move it to the character after it was sold.

Outcomes

The Factory Method Pattern is a widely used Design Pattern along with the closely related Abstract Factory Pattern. You are going to find many different variations but the main goal is to provide an interface for the creation of an object and defer the implementation details to subclasses.

From this perspective, the Factory Method Pattern is a very good way to achieve loose coupling and encapsulation when instantiating objects. It’s a good practice on many occasions and favors the object-oriented principles.

There is one drawback however and that is its complexity. It involves many classes and many layers to implement it and the number can escalate quickly reducing the maintainability sometimes.

As with every Design Pattern, Factory Method gives us guidelines to design good object-oriented programs and readable code, but we should not overdo it. Use it when it makes sense in your code.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.