Factory Method Design Pattern

In software engineering, the Factory Method 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.

This 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. So it is one of the widely used design patterns to promote loose coupling.

Let's understand factory method pattern with an example

Suppose we are managing the appearance of a game character. For the sake of familiarity, let's consider the Super Mario game, where our goal 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, the GameManager class will serve as the core code, bringing together all the smaller segments.

For example, we have included the changeTheme() method for changing themes and the operations() method for deciding what to do with various game characters. Note: We added 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, the 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 we should prefer loose coupling.

So 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. So, object instantiation is done by the factory, and the startGame() method is just a means of accessing the instantiated object. This factory class is popularly known as the Simple Factory.

Case 2: When multiple types are present

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 can 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

  • 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 need a method that combines the operations and creation of Mario's character while still maintaining flexibility!

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. Here 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

  • 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.

Components and Structure

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. So 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, we can set the return type of the factory method as the Product type.

Implementation

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. This implies that MarioMaker does not need to be an interface always i.e. it can be a base class or an abstract class too.
  • Here base creator class (MarioMaker) will work with the product interface (MARIO). By doing so, methods in MarioMaker do not depend on concrete products.
  • Finally, we create a GameManager class that will use MarioMaker and MARIO classes 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?

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.

In our example, we have a framework that consists of two abstractions, MARIO and MarioMaker. So we can easily subclass these abstractions to implement 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.

In other words, the factory method pattern encapsulates the responsibility of creating concrete products, where subclasses of MarioMaker override 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!

Sometimes, we can reuse existing objects instead of rebuilding them. So the factory method does not need to create a new object every time. It can return existing objects by caching them.

Consequences

  • Decouples concrete products from the Client code as it only deals with the product interface.
  • Supports the Single Responsibility Principle by delegating the responsibility of object creation to its subclasses.
  • Supports the Open-Closed Principle because we can add new types of products without breaking existing client code.
  • 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.
  • Due to a lot of subclasses to implement, our code can be a little complex.

Applications

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. Java 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. Here createDataSource() method is used to create the appropriate DataSource instance based on the 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. DocumentBuilder allows parsing and manipulating XML documents. Here 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. Here 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 the Factory Method pattern to create 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.
  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!

More from EnjoyAlgorithms

Self-paced Courses and Blogs