Factory Method Design Pattern

What is Factory Method Pattern?

In object-oriented programming, Factory Method Pattern is a creational design pattern that defines an interface or abstract class (base class) for creating an object but lets the subclasses decide which class to instantiate. In other words, this enables a class to delegate the responsibility of object creation to its subclasses. Note: The Factory Method Pattern is also known as Virtual Constructor.

Factory Method Pattern can be useful when a class doesn't know in advance the exact objects it needs to create or when it wants to delegate the creation of objects to its subclasses for greater flexibility and extensibility. This is widely used in software development to promote loose coupling.

Problem statement to understand Factory Method Pattern

Let's say we are managing the appearance of a game character. For the sake of familiarity, let's consider the game to be Super Mario. In this game, our objective is to manage the instantiation of various versions of Mario characters like Fire Mario, Cape Mario, Invincible Mario, and so on.

Case 1: When a single type of Mario is present

First, let's consider a scenario where we have only one type of character in our game: Mario. In this case, we will create a class called Mario and instantiate it directly whenever we need it.

In the following code, we demonstrate how to instantiate Mario in the startGame() method of the GameManager class. Here GameManager class will serve as the core code, which will bring together all the smaller segments.

For example, we have included changeTheme() method for changing themes and operations() method for deciding what to do with various game characters. Note: We introduced these methods to realize the roles of the GameManager class beyond just instantiating Mario.

class Mario {
    public String name() {
        // Return Mario's name
    }
    
    public void run() {
        // Code for Mario to run
    }
    
    public void jump() {
        // Code for Mario to jump
    }
}

//GameManager brings all different parts of the code together
//It is like the heart of the game!
class GameManager {
    public void startGame() {
        Mario mario = new Mario();
        // Operations on Mario
    }
    
    public void operations() {
        // Code for handling game operations
    }
    
    public void changeTheme() {
        // Code for changing game theme
    }
}

Observations

  • GameManager class is responsible for creating Mario, deciding on operations through operations(), changing themes via changeTheme(), and other tasks. So, GameManager class violates the Single Responsibility Principle (SRP) by having multiple responsibilities.
  • We may require an instance of the Mario class in various parts of the application outside of the GameManager class. If all these parts directly instantiate the Mario class using the 'new' keyword, it would lead to tight coupling with the Mario class. This tight coupling becomes problematic if the Mario class undergoes modifications in the future, as all the classes that directly instantiate it would be affected. So this is not desirable, and loose coupling should be preferred.

The key takeaway from this approach is to delegate the responsibility of creating the object to a separate class. To achieve this, we can create a Factory class whose sole responsibility is to create instances of the Mario class.

class Mario {
    // Mario class members and methods
}

// Factory is responsible for creating Mario instances
class Factory {
    public Mario createMario() {
        return new Mario();
    }
}

class GameManager {
    // GameManager class members
    Factory factory;
    
    public void startGame() {
        Mario mario = factory.createMario();
        // Operations on Mario
    }
    
    public void operations() {
        // ...
    }
}

Here, we have replaced direct instantiation with instantiation via a factory. This factory class is popularly known as the Simple Factory. Now, product instantiation is done by the factory, and the startGame() method is just a means of accessing the instantiated product.

Case 2: When multiple types are present

Now, we consider the original problem statement. Suppose we require two types of Mario characters: Fire Mario and Cape Mario. For this, we will declare an abstract class (MARIO) for Mario's character, and concrete subclasses to implement different versions of Mario, such as Fire Mario (FireMario) and Cape Mario (CapeMario). These subclasses are concrete products, and the base class MARIO is an abstract product.

// Abstract class for Mario character
abstract class MARIO {
    public abstract String name();
    public void run() {
        // Implementation for the run() method
    }
    public void jump() {
        // Implementation for the jump() method
    }
}

// Concrete subclass
class FireMario extends MARIO {
    @Override
    public String name() {
        return "FIRE";
    }
}

// Concrete subclass
class CapeMario extends MARIO {
    @Override
    public String name() {
        return "CAPE";
    }
}

Again, we can make a startGame() method in the GameManager class to instantiate any one type of Mario based upon some parameter. The GameManager class will look like this:

public class GameManager {
    private MARIO mario;

    public void startGame(String marioType) {
        if (marioType.equals("FIRE")) {
            mario = new FireMario();
        } 
        else if (marioType.equals("CAPE")) {
            mario = new CapeMario();
        }
        
        // Operations on Mario
    }

    // Rest of the class...
}

Observations

  • As in the previous case, GameManager class violates Single Responsibility Principle as it is responsible for creating all types of Mario and has other responsibilities.
  • The addition of new types of Mario's character will result in a more complex, hard-to-maintain, and much more coupled GameManager class. For example, suppose we are introducing CatMario (a new type of Mario), then we have to change the GameManager class accordingly. This suggests that GameManager is not closed for modification. So it violates the Open-Closed principle.

One might think of making different factories for each type of Mario and then creating the corresponding type of Mario. But if we need to perform specific operations on the Mario character (which has been created in the factory) then we have to write the same code in each factory. Here is the example code:

class FireFactory {
    public MARIO createMario() {
        return new FireMario();
    }

    // Operations on Mario
    public void operations() {
        MARIO mario = createMario();
        System.out.println(mario.name());
        // Additional operations on Mario
    }
}

class CapeFactory {
    public MARIO createMario() {
        return new CapeMario();
    }

    // Operations on Mario
    public void operations() {
        MARIO mario = createMario();
        System.out.println(mario.name());
        // Additional operations on Mario
    }
}
  • The startGame() method in the GameManager class has to use one of these factories, which will once again make it riddled with if-else statements and less flexible (as it will be tightly coupled with the factories this time).
  • Each factory is supposed to perform the same operations on the Mario it creates. So our code is not reusable, as we define the same operations in multiple classes (factories).

Takeaway:  We would like to have a framework that combines the operations and creation of Mario's character together while still maintaining flexibility!

Final solution approach using Factory Method Pattern

One key idea is to associate all the factories under a common interface. This interface will have two responsibilities: creating Mario's character and performing essential operations on that character. Let's name this interface (or abstract class) MarioMaker, which will include the methods createMario() and operations().

The critical question is: How does createMario() determine which type of Mario to instantiate? No worries, it won't make that decision, at least not within the interface itself. Here's what we'll do: We'll create subclasses of MarioMaker called FireMaker and CapeMaker, and override the createMario() method to instantiate FireMario and CapeMario, respectively.

Here's the catch: We are delegating the responsibility of creating Mario to the subclasses. Afterwards, we'll use the operations() method on the created Mario.

However, there are still a few aspects to consider. The operations() method will use an object of type MARIO (which is abstract), due to which this method does not know about the type of Mario it uses. As a result, it is decoupled from the concrete subclasses of the MARIO class.

Takeaways from this approach

  • We will use interfaces or abstract classes in the GameManager class to instantiate the character. This approach is beneficial because writing code that relies on interfaces or abstract classes is better than directly using concrete classes.
  • In the future, adding new types of MARIO characters will be easy, as we will not need to modify the GameManager class. Instead, we can simply add new subclasses of the MARIO and MarioMaker classes. This adherence to the Open-Closed principle indicates that our GameManager class is now more flexible.
  • The code is now highly reusable, particularly in terms of the operations() method.
  • GameManager class is no longer directly coupled to the concrete classes.

This approach is called as the factory method pattern, where we use createMario() to create an object.

Components and Structure of Factory Method Pattern

Factory design pattern UML diagram and structure

  1. Product (MARIO): An interface common to all objects that can be produced by the creator and its subclasses.
  2. Concrete Products (FireMario, CapeMario): Subclasses of the product interface that implement specific types of products.
  3. Creator (MarioMaker): An interface which declares the factory method and includes operations to be performed on the product returned by the factory method.
  4. Concrete Creators (FireMaker, CapeMaker): Subclasses of the creator interface which override the factory method and return an instance of one of the concrete products.

Note

  • Creator doesn't always need to be a purely abstract class or interface! We can provide default implementation to the factory method in the Creator class itself i.e. returning a default Concrete Product that subclasses can further override.
  • The creator class is not solely responsible for creating products; it also has operations to perform on the created product. The factory method decouples these operations from the concrete products i.e. the operations don't need to know the specific type of Mario they are working on.
  • Although the concrete creators instantiate concrete products, the return type of the factory method is set as the Product type.

Implementation using the factory method pattern

Now let's understand the implementation of the above idea using the factory method.

Factory design pattern example

We create the product interface MARIO. Then, we implement its subclasses: concrete products FireMario and CapeMario. Now, we create the creator class MarioMaker which declares a factory method(createMario()) for creating concrete products and operational methods (operations()) to work on the created products. 

  • We can give the default implementation of createMario() in MarioMaker itself, or we can leave it as a completely abstract method which implies that MarioMaker does not need to be an interface always i.e. it can be a base class or an abstract class too.
  • This pattern will work when the base creator class (MarioMaker) works with the product interface (MARIO). By doing so, methods in MarioMaker do not depend on concrete products.

Finally, we create GameManager class that will use MarioMaker and MARIO classes only, i.e., base class and/or interfaces only. Based on the system requirements, we pass the appropriate concrete creator to the startGame() method of GameManager through which we create the concrete product.

Complete solution code in Java

import java.util.Scanner;

// Product interface
interface MARIO {
    String name();
    void jump();
    void run();
}

// Concrete products
class FireMario implements MARIO {
    @Override
    public String name() {
        return "FIRE";
    }

    @Override
    public void jump() {
        System.out.println("Fire Mario jumps high!");
    }

    @Override
    public void run() {
        System.out.println("Fire Mario runs fast!");
    }
}

class CapeMario implements MARIO {
    @Override
    public String name() {
        return "CAPE";
    }

    @Override
    public void jump() {
        System.out.println("Cape Mario jumps with a cape glide!");
    }

    @Override
    public void run() {
        System.out.println("Cape Mario runs gracefully!");
    }
}

// Base Creator class providing factory method and necessary operations
abstract class MarioMaker {
    abstract MARIO createMario();

    void operations() {
        MARIO mario = createMario();
        System.out.println(mario.name());
        mario.jump();
        mario.run();
    }
}

// Concrete creators
class FireMaker extends MarioMaker {
    @Override
    MARIO createMario() {
        return new FireMario();
    }
}

class CapeMaker extends MarioMaker {
    @Override
    MARIO createMario() {
        return new CapeMario();
    }
}

class GameManager {
    //works with an instance of concrete creator via base creator
    public void startGame(MarioMaker creator) {
        creator.operations();
    }
    //......
}

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("Enter the type of Mario: ");
        String input_type = sc.nextLine();

        MarioMaker in = null;
        GameManager gamer = new GameManager();

        if (input_type.equals("Fire")) {
            in = new FireMaker();
        } 
        else if (input_type.equals("Cape")) {
            in = new CapeMaker();
        } 
        else {
            System.out.println("ERROR!");
            System.exit(0);
        }

        gamer.startGame(in);
    }
}

When to use Factory Method Pattern?

After discussing many approaches and their drawbacks, we should use the factory method when:

  • We don't know the exact types of objects to be created beforehand.
  • We want to extend the internal components of a framework or library.

A framework uses abstract classes to define and maintain dependencies between objects; it is also responsible for creating them. In our example, let's say we have a framework that consists of two abstractions, MARIO and MarioMaker. We have to subclass these abstractions to implement their character-specific implementations. However, since a particular Mario subclass to instantiate is system-specific, the MarioMaker class doesn't know which concrete product needs to be created.

The factory method pattern encapsulates the responsibility of creating concrete products and moving them out of the framework. Then, subclasses of MarioMaker redefine an abstract method (createMario()) to return the appropriate Mario. Once a MarioMaker subclass is instantiated, it can then instantiate system-specific Mario characters without knowing their class!

  • We want to reuse existing objects instead of rebuilding them. It is worth noting that the factory method does not need to create a new object every time. It can return existing objects from a cache or any other source.

Consequences

  • The factory method decouples concrete products from the Client code as it only deals with the product interface.
  • The factory method supports the Single Responsibility Principle because we can move the product creation code into one place.
  • The factory method supports the Open-Closed Principle because we can add new types of products without breaking existing client code.
  • The factory method connects parallel class hierarchies, which occur when a class delegates some of its responsibilities to a separate class. For example, the above code establishes the connection between the two parallel hierarchies MarioMaker and MARIO.
  • Clients might need to subclass the Creator class to create a single concrete product. Although this is not a substantial disadvantage, it still adds another evolution to the code for a single concrete product.
  • Due to a lot of subclasses to implement this pattern, our code can be a little complex. 

Applications of the Factory Method Pattern

Frameworks and libraries often use the Factory Method pattern to provide extensibility and enable clients to create objects without tightly coupling them to specific implementations. 

  1. The SLF4J library uses the Factory Method pattern with the LoggerFactory class. It provides a factory method called getLogger() that allows clients to create logger instances without being tightly coupled to a specific logging implementation. 
  2. JDBC (Java API for database connectivity) includes the DataSourceFactory interface. Implementations of this interface serve as factories for creating DataSource objects, which represent connections to specific databases. The factory method createDataSource() is used to create the appropriate DataSource instance based on the configuration or database type.
  3. ExecutorService interface in the java.util.concurrent package is a factory for creating and managing threads to execute concurrent tasks. It provides methods such as newFixedThreadPool() to create different types of thread pools with varying configurations.
  4. Java XML Processing (JAXP) provides the DocumentBuilderFactory class, which is a factory for creating instances of the DocumentBuilder interface. The DocumentBuilder allows parsing and manipulating XML documents. The factory method newDocumentBuilder() enables clients to obtain a DocumentBuilder instance without depending on a specific XML parser implementation.
  5. ParserFactory in JSON libraries uses the Factory Method pattern to create JSON parser instances. The factory method allows clients to obtain a parser implementation based on their specific requirements or preferences.
  6. In Java, the java.util.Calendar class uses a factory method to create Calendar instances based on different locales and time zones.
  7. Graphical User Interface (GUI) frameworks use Factory Method pattern when it comes to creating UI components. For example, it uses this pattern to instantiate buttons with distinct styles.
  8. Game development frameworks leverage the Factory Method pattern to create game objects like characters, items, and enemies. Each game level or scenario can define its own factory to create specific types of objects.
  9. Dependency Injection frameworks leverage the Factory Method pattern to dynamically create and provide object instances based on their dependencies and configurations.
  10. Whenever you encounter an abstract factory design pattern, you'll likely catch a glimpse of the factory method. Abstract Factory pattern encompasses multiple factory methods within a single class.

Conclusion

Factory method pattern is a creational design pattern that should be used when a class can't anticipate the class of objects it must create. 

  • First, we saw the simple factory, where we realized that we should make a separate class for instantiation of products (even if only one variant of product exists). 
  • Then we explored the scenario of multiple variants of a product. We saw that using different simple factories for each product variant is neither a flexible nor efficient approach.
  • Finally, we formulated the factory method pattern where we created a base creator class and delegated the responsibility of product creation to its sub-classes.

Our discussion shows that using the factory method under appropriate circumstances results in a flexible, easy-to-maintain, and reliable code that follows various Object-Oriented design principles.

Further Exploration:

  • Making parameterized factory methods!
  • When should we use an abstract creator class with a default implementation of the factory method?

Thanks to Ankit Nishad for his contribution in creating the first version of this content. If you have any queries or feedback, please write us at contact@enjoyalgorithms.com. Enjoy learning, Enjoy oops!

Share Your Insights

☆ 16-week live DSA course
☆ 16-week live ML course
☆ 10-week live DSA course

More from EnjoyAlgorithms

Self-paced Courses and Blogs

Coding Interview

Machine Learning

System Design

Our Newsletter

Subscribe to get well designed content on data structure and algorithms, machine learning, system design, object orientd programming and math.