Factory Method Pattern in Java


What is a Factory Method Pattern

  • Factory method pattern is a creational design pattern that uses a factory method to deal with the creation of objects rather than specifying their exact classes.
  • In a simple factory pattern, a method will directly create the objects using the new keyword.
  • Meanwhile in the factory method pattern, the creation will be delegated to the subclasses, this is done by creating a factory method, typically in an abstract class which is then extended by it’s subclasses to create the objects.

Advantages

  1. Modular code - Since each subclass handles its own creation logic, we follow Single Responsibility principle and makes the code cleaner.
  2. Flexible - The encapsulation and loose coupling also ensures that any further extension will not violate Open Closed principle.

Disadvantages

  1. Forces to create more classes
  2. It’s slightly more harder to understand the flow as there is one more hierarchy for creator classes.

Example

We have a factory method (createTaxCalculator) in TaxCalculatorProcessor, which is extended by its subclasses GoldTaxCalculatorProcessor and PetrolTaxCalculatorProcessor. This follows the Creator and Concrete Creator classes hierarchy. We have a private method displayTaxDetails and public method processTax which we want to expose. This is just added to illustrate the advantage of having the subclass control over their creation of objects, we can add any logic pre/post creation of the objects. TaxCalculationStrategy, PetrolTaxCalculator, GoldTaxCalculator are like the products in our examples. This is same as in the Simple Factory example. Finally, we have a TaxCalculator which has a simple method that returns the specific subclass factory and then calls the processTax method.

package org.nirmalks.factory;

public abstract class TaxCalculatorProcessor {

    public void processTax(double price) {
        TaxCalculationStrategy calculator = createTaxCalculator();

        double tax = calculator.calculateTax(price);
        displayTaxDetails(price, tax);
    }

    private void displayTaxDetails(double price, double tax) {
        System.out.println("Price before tax: " + price);
        System.out.println("Tax amount: " + tax);
        System.out.println("Total price after tax: " + (price + tax));
    }
    protected abstract TaxCalculationStrategy createTaxCalculator();
}
package org.nirmalks.factory;

public class GoldTaxCalculatorProcessor extends TaxCalculatorProcessor {

    @Override
    public TaxCalculationStrategy createTaxCalculator() {
        return new GoldTaxCalculator();
    }
}
package org.nirmalks.factory;

public class PetrolTaxCalculatorProcessor extends TaxCalculatorProcessor {

    @Override
    public TaxCalculationStrategy createTaxCalculator() {
        return new PetrolTaxCalculator();
    }
}
package org.nirmalks.factory;

public interface TaxCalculationStrategy {
    double calculateTax(double price);
}
package org.nirmalks.factory;

public class PetrolTaxCalculator implements TaxCalculationStrategy {
    @Override
    public double calculateTax(double price) {
        return price * 0.05;
    }
}
package org.nirmalks.factory;

public class GoldTaxCalculator implements TaxCalculationStrategy {
    @Override
    public double calculateTax(double price) {
        return price * 0.10;
    }
}
package org.nirmalks.factory;

public enum ProductType {
    GOLD("gold"),
    PETROL("petrol");
    private String value;

    ProductType(String value) {
        this.value = value;
    }
}
package org.nirmalks.factory;

public class TaxCalculator {
    public void processTax(ProductType productType, double price) throws Exception {
        getFactory(productType).processTax(price);
    }

    private TaxCalculatorProcessor getFactory(ProductType productType) {
        return switch (productType) {
            case GOLD -> new GoldTaxCalculatorProcessor();
            case PETROL -> new PetrolTaxCalculatorProcessor();
        };
    }
}
package org.nirmalks.factory;

public class TaxCalculatorRunner {
    public static void main(String[] args) throws Exception {
        TaxCalculator taxCalculator = new TaxCalculator();
        taxCalculator.processTax(ProductType.GOLD, 25000);
        taxCalculator.processTax(ProductType.PETROL, 100);
    }
}

Output

Price before tax: 25000.0
Tax amount: 2500.0
Total price after tax: 27500.0
Price before tax: 100.0
Tax amount: 5.0
Total price after tax: 105.0