Mastering Factory Design Pattern in Java: Complete Guide

This is one of the commonly used Creational Design Patterns.The Factory Design pattern in Java returns one of the instances of several sub-classes based on some logic.

Implementation of Factory Design pattern in Java

Here are the steps to implement the Factory Design Pattern in Java:

  1. Define an interface or abstract class: Create an interface or abstract class that declares common methods for the product.
  2. Implement concrete classes: Create concrete classes that implement the interface or extend the abstract class, representing different types of products.
  3. Create the Factory class: Create a factory class with a method for creating objects based on certain conditions or inputs.
  4. Implement the factory method: Implement the factory method in the factory class, which determines which concrete class to instantiate and return as the product.
  5. Client code: Use the factory class to create objects without directly instantiating concrete classes. Call the factory method to get the desired product object.

Let us take a very simple example for implementing Factory design pattern in Java.

In this case, Animal is the interface and we have Lion & Tiger as two concrete implementations of Animal. The AnimalFactory class’s getAnimal API will return Lion or Tiger instance based on the name passed to it. Look at the return type of getAnimal API, it is Animal not Lion or Tiger. This design follows the Design Principle “Program to Interface not to Implementation”.

Please find below the Class Diagram and Code.

Class Diagram

Factory Design pattern in Java

Code

// Step 1: Define the Animal interface
package com.design.patterns;

public interface Animal {
    public void describe();
}

// Step 2: Implement concrete classes
package com.design.patterns;

public class Lion implements Animal {
    @Override
    public void describe() {
        System.out.println("This is a Lion");
    }
}

package com.design.patterns;

public class Tiger implements Animal {
    @Override
    public void describe() {
        System.out.println("This is a Tiger");
    }
}

// Step 3: Create the Factory class
package com.design.patterns;

public class AnimalFactory {
// Step 4: Implement the factory method
    public static Animal getAnimal(String name) {
        if ("Tiger".equalsIgnoreCase(name)) {
            return new Tiger();
        }
        if ("Lion".equalsIgnoreCase(name)) {
            return new Lion();
        }
        return null;
    }
}

// Step 5: Client code
package com.design.patterns;

public class AnimalClient {
    public static void main(String[] args) {
        Animal animal = AnimalFactory.getAnimal("lion");
        animal.describe();
    }
}

Output

This is a Lion

Advantages of Using Factory design pattern in Java

The advantages of using the Factory Design Pattern in Java are:

  1. Encapsulation: Object creation is encapsulated within a separate factory class, promoting loose coupling.
  2. Abstraction and Polymorphism: Clients work with abstract product types, allowing for flexibility and extensibility.
  3. Centralized Object Creation: Better control and management of object creation logic.
  4. Code Organization: Separation of object creation improves code readability, maintainability, and modularity.
  5. Dependency Injection: Facilitates dependency injection, promoting inversion of control and dependency inversion.
  6. Testing and Mocking: Simplifies unit testing and mocking by allowing substitution of objects during testing.

Limitations of Factory design pattern in Java

Here are some limitations of the Factory design pattern in Java:

  1. Static Binding: Factory methods are often bound at compile-time, leading to limited flexibility in changing the object creation behavior dynamically.
  2. Complexity: As the application grows, the factory class can become complex due to the inclusion of multiple creation methods, reducing code readability.
  3. Maintenance Overhead: Adding new product types requires modifying the factory class, which can introduce maintenance challenges and affect the Open/Closed Principle.
  4. Scalability: The Factory pattern might not scale well for applications with numerous product types, leading to a proliferation of factory methods.
  5. Subclass Proliferation: In some cases, a separate subclass is required for each product, resulting in a high number of classes and potential maintenance issues.
  6. Tight Coupling: Factories can introduce tight coupling between client code and the product classes, making changes more complex and affecting code maintainability.
  7. Difficulty in Unit Testing: Testing factory-based code might require mocking or other techniques to isolate and test specific product instances.

Real life use cases for Factory Design Pattern in Java

The Factory Design Pattern in Java finds application in various real-life scenarios. Some common use cases include:

  1. Object Creation with Complex Initialization: Simplifies the creation of objects requiring complex initialization or configuration.
  2. Dependency Injection: Works alongside dependency injection frameworks to create fully initialized objects with injected dependencies.
  3. Database Connection Management: Manages the creation and pooling of database connections, optimizing resource usage.
  4. Plugin or Module System: Dynamically creates instances of different modules or plugins based on configuration or user input.
  5. Logging Frameworks: Creates logger objects, allowing seamless switching between different logging implementations or configurations.
  6. GUI Component Creation: Generates GUI components like buttons, panels, or windows based on desired attributes or configurations.
  7. Caching Mechanisms: Manages the creation of cached objects, implementing strategies such as object pooling, lazy initialization, or condition-based caching.

Conclusion

The factory design pattern in Java simplifies the codebase, enhances code reuse, and allows for easy addition of new object types in the future. The Factory pattern is widely used in Java applications where there is a need for flexible object creation with a common interface.

This pattern promotes loose coupling between the client code and the specific classes being instantiated, making it easier to introduce new types of objects without modifying the client code.

One comment

Leave a Reply

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