Simple Factory Pattern Implementation in Java

Prerequisites: Understanding UML Class Diagrams

Before diving into design patterns, it's essential to understand UML class diagrams. They provide a visual representation of class structures and relationships, which is fundamental to grasping how design patterns work. If you're unfamiliar with UML notation, consider reviewing a basic UML class diagram guide first.

Overview of the Simple Factory Pattern

The Simple Factory Pattern is a creational design pattern that encapsulates object creation logic within a single factory class. This approach centralizes instantiation logic and promotes loose coupling between clients and concrete product classes.

UML Class Diagram

The following diagram illustrates the structure of our calculator implementation using the Simple Factory Pattern. The diagram shows the abstract base class, four concrete implementations, and the factory that creates instances based on input parameters.

Implementation Steps

Step 1: Create the Abstract Base Class

First, we define an abstract operation class that contains common properties and defines the contract for all concrete implementations.

/**
 * Abstract base class for all arithmetic operations
 */
public abstract class Operation {

    private double firstOperand = 0;
    private double secondOperand = 0;

    public double getFirstOperand() {
        return firstOperand;
    }

    public void setFirstOperand(double firstOperand) {
        this.firstOperand = firstOperand;
    }

    public double getSecondOperand() {
        return secondOperand;
    }

    public void setSecondOperand(double secondOperand) {
        this.secondOperand = secondOperand;
    }

    /**
     * Template method for calculating results
     * @return computed result
     */
    public abstract double calculate();
}

Note that we use the abstract keyword to mark this class as incomplete, requiring subclasses to provide specific implementations.

Step 2: Implement Concrete Operation Classes

Next, we create four concrete classes, each handling one arithmetic operation:

/**
 * Addition operation implementation
 */
public class AdditionOperation extends Operation {

    @Override
    public double calculate() {
        return getFirstOperand() + getSecondOperand();
    }
}

/**
 * Subtraction operation implementation
 */
public class SubtractionOperation extends Operation {

    @Override
    public double calculate() {
        return getFirstOperand() - getSecondOperand();
    }
}

/**
 * Multiplication operation implementation
 */
public class MultiplicationOperation extends Operation {

    @Override
    public double calculate() {
        return getFirstOperand() * getSecondOperand();
    }
}

/**
 * Division operation implementation
 */
public class DivisionOperation extends Operation {

    @Override
    public double calculate() {
        if (getSecondOperand() == 0) {
            throw new ArithmeticException("Cannot divide by zero");
        }
        return getFirstOperand() / getSecondOperand();
    }
}

Step 3: Create the Factory Class

The factory class contains the logic for determining which concrete class to instantiate based on the operator symbol provided:

/**
 * Factory for creating operation instances
 */
public class OperationFactory {

    /**
     * Creates an operation instance based on the given operator
     * @param operator mathematical operator symbol
     * @return appropriate operation instance
     */
    public static Operation createOperation(String operator) {
        Operation result = null;

        switch (operator) {
            case "+":
                result = new AdditionOperation();
                break;
            case "-":
                result = new SubtractionOperation();
                break;
            case "*":
                result = new MultiplicationOperation();
                break;
            case "/":
                result = new DivisionOperation();
                break;
            default:
                throw new IllegalArgumentException("Unsupported operator: " + operator);
        }

        return result;
    }
}

Step 4: Client Code Usage

Here's how clients use the factory to perform calculations:

/**
 * Demonstration of Simple Factory Pattern usage
 */
public class Client {

    public static void main(String[] args) {
        Operation operation = OperationFactory.createOperation("/");
        operation.setFirstOperand(10);
        operation.setSecondOperand(3);
        System.out.println("Result: " + operation.calculate());

        Operation addition = OperationFactory.createOperation("+");
        addition.setFirstOperand(5);
        addition.setSecondOperand(7);
        System.out.println("Result: " + addition.calculate());
    }
}

Key Benefits of This Pattern

Encapsulation: Object creation logic is isolated within the factory Maintainability: Adding new operations requires changes only to the factory Testability: Concrete classes can be easily mocked for testing Loose Coupling: Client code depends only on abstractions, not concrete implementations

Considerations

While the Simple Factory Pattern offers several advantages, it does have a limitation: the factory class itself violates the Open/Closed Principle because it must be modified whenever new operation types are added. For scenarios requiring more flexibility, consider exploring the Factory Method or Abstract Factory patterns instead.

Tags: java design-patterns simple-factory creational-patterns UML

Posted on Fri, 16 Oct 2026 16:21:28 +0000 by jibster