Factory Method Design Pattern In Java, Purpose, Implement, Pros & Cons

Definition

The Factory Method Design Pattern is a widely used technique in object-oriented programming. It falls under creational design patterns, which focus on efficient and flexible ways to create objects. Let’s understand the factory method design pattern in Java.

The Factory Method pattern defines an interface for creating objects but delegates the instantiation to subclasses. This means that the superclass provides a method for creating objects, but the subclass determines the specific type of object to be created.

Purpose

  • Centralized Object Creation: The factory method pattern introduces a factory class or interface responsible for creating objects. This factory hides the logic of concrete object instantiation, allowing you to create different objects based on the provided criteria.
  • Decoupling Client Code: The client code interacts with the factory, not directly with the concrete object classes. It calls the factory’s method (often named createDriver()) specifying the type of object it needs. The factory then takes care of creating and returning the appropriate object.

Components Of Factory Method Pattern

  • Factory Interface (Optional): This interface defines a method (often named createDriver()) that specifies the type of object the factory should create.
  • Concrete Factory Classes: These classes implement the factory interface (or inherit from a base factory class) and provide the actual logic for creating specific objects. They typically override the createDriver() method to return the desired concrete product.
  • Client Code: The client code interacts with the factory, not directly with the concrete object classes. It calls the factory’s createDriver() method, specifying the type of object it needs.

Let’s begin by discussing the advantages before delving into an example.

Implement in WebDriver Initialization

Let’s First Create and go through the UML diagram to implement the design pattern

abstract method factory in java

1. Let’s First Create the IDriver Interface and one abstract Method initializeDriver() to initialize the driver.

import org.openqa.selenium.WebDriver;

public interface IDriver {
    /**
     * Abstract method to initialize the driver
     * @return webdriver object
     */
    WebDriver initializeDriver();
}


2. Create CustomChromeDriver Class which implements the IDriver interface

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

public class CustomChromeIDriver implements IDriver {

    @Override
    public WebDriver initializeDriver() {
        return new ChromeDriver();
    }
}


3. Create CustomFirefoxDriver Class which implements the IDriver interface

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;

public class CustomFirefoxIDriver implements IDriver {

    @Override
    public WebDriver initializeDriver() {
        return new FirefoxDriver();
    }
}


4. Create CustomSafariDriver Class which implements the IDriver interface

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.safari.SafariDriver;

public class CustomSafariDriver implements IDriver {
    @Override
    public WebDriver initializeDriver() {
        return new SafariDriver();
    }
}


5. Create CustomEdgeDriver Class which implements the IDriver interface

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.edge.EdgeDriver;

public class CustomEdgeDriver implements IDriver {
    @Override
    public WebDriver initializeDriver() {
        return new EdgeDriver();
    }
}


6. Create Browser Enum

public enum Browser {
    CHROME,
    FIREFOX,
    EDGE,
    SAFARI
}


7. Create DriverFactory Class

import org.openqa.selenium.WebDriver;

public class DriverFactory {

    private static WebDriver driver;

    public static WebDriver getDriver(Browser browser) {
        driver = switch (browser) {
            case CHROME -> new CustomChromeIDriver().initializeDriver();
            case FIREFOX -> new CustomFirefoxIDriver().initializeDriver();
            case SAFARI -> new CustomSafariDriver().initializeDriver();
            case EDGE -> new CustomEdgeDriver().initializeDriver();
        };
        return driver;
    }

    public void closeDriver() {
        driver.close();
    }

    public void quitDriver() {
        driver.quit();
    }
}


8. Test the Dirver factory

import org.openqa.selenium.WebDriver;

public class TestDriverFactory {

    public static void main(String[] args) {

        /*
        Create ChromeDriver Instance
         */
        WebDriver chromeDriver = DriverFactory.getDriver(Browser.CHROME);

        /*
        Create FireFoxDriver Instance
         */
        WebDriver firefoxDriver = DriverFactory.getDriver(Browser.FIREFOX);

        /*
        Create SafariDriver Instance
         */
        WebDriver safariDriver = DriverFactory.getDriver(Browser.SAFARI);

         /*
        Create EdgeDriver Instance
         */
        WebDriver edgeDriver = DriverFactory.getDriver(Browser.EDGE);
    }
}

Pros and Cons

Pros

  • Loose Coupling: Client code is decoupled from concrete object classes, interacting solely with the factory interface. This enhances adaptability to changes in the types of objects that can be created.
  • Increased Reusability: New concrete factories for different object types can be created without modifying the client code. This fosters code reusability and maintainability.
  • Flexibility: The factory method pattern permits dynamic object creation based on runtime conditions. Logic within the factory can determine which object to create based on various factors.

Cons

  • Increased Complexity: While the factory offers benefits like loose coupling and reusability, it adds another layer of code (factory classes and interfaces) that needs to be maintained. This can make the codebase slightly more complex, particularly for smaller projects where the advantages might be less significant.
  • Potential Overhead: Creating and managing factories might introduce a small performance overhead compared to directly creating objects using the new keyword. However, in most practical scenarios, this overhead is negligible, and the benefits of the factory pattern outweigh this drawback.
  • Tight Coupling (In some cases): While the factory method aims for loose coupling, there can still be tight coupling between the factory itself and the concrete classes it creates. This can occur if the factory logic becomes intricate or if it depends heavily on specific implementations of the concrete classes. To minimize tight coupling, keep the factory logic focused on creating objects and avoid complex decision-making within the factory.

When to Use factory method design pattern in java

Use the Factory Method pattern when:

  • You want to delegate object creation to subclasses
  • You need to decouple the client code from the concrete implementations
  • You anticipate that the class may have multiple implementations for creating objects

Where to Avoid

Avoid using the factory method design pattern in Java when:

  • There is only one implementation for creating objects
  • The class hierarchy is simple and unlikely to change

Real-life Examples

One real-life example of the Factory Method pattern is in GUI frameworks, where different subclasses of a factory class are used to create different types of UI components such as buttons, text fields, and checkboxes.

Use Cases in Selenium Web Automation

In Selenium WebDriver, the Page Object pattern often utilizes the Factory Method pattern to create instances of page objects representing different web pages.

Use Cases in API Automation Using Rest Assured

In API automation using Rest Assured, the Factory Method pattern can be used to create instances of request specifications or response objects for different API endpoints.

Relationship with Other Patterns

The Factory Method pattern is often used in combination with other creational patterns such as Abstract Factory and Singleton. It is also closely related to the Template Method pattern, which defines the skeleton of an algorithm in the superclass but allows subclasses to override specific steps.

Conclusion

The Factory Method Design Pattern provides an elegant solution to the problem of object creation by allowing subclasses to define the type of objects that will be instantiated. Promoting loose coupling and encapsulating object creation logic helps to improve the maintainability and extensibility of code.

Execute Selenium/Automation Tests On Chrome For Testing


FAQs

  1. What is the difference between the Factory Method and Abstract Factory patterns?
    • The Factory Method pattern deals with creating a single object, while the Abstract Factory pattern deals with creating families of related objects.
  2. Can the Factory Method pattern be used with dependency injection frameworks?
    • Yes, the Factory Method pattern can be used with dependency injection frameworks to provide more flexibility in object creation.
  3. Is the Factory Method pattern suitable for creating complex objects?
    • Yes, the Factory Method pattern can be used to create complex objects by delegating the instantiation logic to subclasses.
  4. How does the Factory Method pattern promote loose coupling?
    • By allowing subclasses to provide their implementation for creating objects, the Factory Method pattern decouples the client code from the concrete implementations.
  5. Are there any alternatives to the Factory Method pattern?
    • Yes, alternatives to the Factory Method pattern include the Simple Factory pattern and the Service Locator pattern, although they may not provide the same level of flexibility and extensibility.

Leave a Comment