Factory Pattern in Spring Boot: A Smarter, Cleaner Implementation

3 min readMar 8, 2025

Factory pattern is one of the most commonly used pattern in software world. And also it is known to be violating the Open-Closed Principle in SOLID principles. In this article, we will discuss how you can implement the factory pattern with a smarter and cleaner implementation.

Let’s code…!!!

Example Use Case

Consider a use case where you’re building a simple calculator application that takes two numerical inputs and an operation type (ADD, SUB, MUL, DIV) as a string. Based on the user’s input, the application executes the operation at runtime and returns the result.

At this point, you should recognize that the Factory Pattern is a great fit for this scenario. Let’s dive into its implementation.

Folder structure

.
└── com.nsadisha.calculator
├── CalculatorApplication.java

├── service/
│ ├── OperatorService.java
│ │
│ └── impl/
│ └── OperatorServiceImpl.java

└── operators/
├── Operator.java

├── impl/
│ ├── AddOperator.java
│ ├── SubOperator.java
│ ├── MulOperator.java
│ └── DivOperator.java

└── factory/
└── OperatorFactory.java

Implementation

public interface OperatorService {
double calculate(double num1, double num2, String operator) throws CalculatorFailureException;
}

CalculatorFailureException is a user defined exception class, which throws when an operation is failed.

@Slf4j
@Service
@RequiredArgsConstructor
public class OperatorServiceImpl implements OperatorService {

private final OperatorFactory operatorFactory;

public double calculate(double num1, double num2, String operator) throws CalculatorFailureException {
try {
Operator operator = operatorFactory.getOperator(operator);
return operator.execute(num1, num2);
} catch(Exception e) {
log.error(e.getMessage());
throw new OperatorFailureException(e.getMessage);
}
}
}
public interface Operator {

String ADD_OPERATOR = "ADD";
String SUB_OPERATOR = "SUB";
String MUL_OPERATOR = "MUL";
String DIV_OPERATOR = "DIV";

double execute(double num1, double num2);
}
@Component(Operator.ADD_OPERATOR)
public class AddOperator implements Operator {
public double execute(double num1, double num2) {
// Add 2 numbers
return num1 + num2;
}
}
@Component(Operator.SUB_OPERATOR)
public class SubOperator implements Operator {
public double execute(double num1, double num2) {
// Substract 2 numbers
return num1 - num2;
}
}
@Component(Operator.MUL_OPERATOR)
public class MulOperator implements Operator {
public double execute(double num1, double num2) {
// Multiply 2 numbers
return num1 * num2;
}
}
@Component(Operator.DIV_OPERATOR)
public class DivOperator implements Operator {
public double execute(double num1, double num2) {
// Divide 2 numbers
return num1 / num2;
}
}
@Component
@RequiredArgsConstructor
public class OperatorFactory {

private final ApplicationContext context;

public Operator getOperator(String operatorName) {
if (context.containsBean(operatorName)) {
context.getBean(operatorName, Operator.class);
}

// If bean not found
log.error("No operator found for [{}]", operatorName);
throw new IllegalArgumentException("Invalid operation type: " + operatorName);
}
}

You’ve successfully implemented the Factory Pattern! With this approach, you can add new operator types without modifying the OperatorFactory class.

Usage

CalculatorServiceREST handles requests with CalculatorRequestDTO, containing two numbers and an operator. It delegates the calculation to OperatorService, which uses the factory pattern to select the correct operator dynamically. This approach ensures flexibility and easy extensibility.

@Getter
@Setter
public class CalculatorRequestDTO {
private double num1;
private double num2;
private String operator;
}
@RestController
@RequiredArgsConstructor
public class CalculatorServiceREST {

private final OperatorService operatorService;

@PostMapping("/calculate")
public dounle calculate(@RequestBody CalculatorRequestDTO request) {
return operatorService.calculate(
request.getNum1(),
request.getNum2(),
request.getOperator()
);
}
}

Alternative Factory Implementation

Here’s another way to implement your factory class using Spring’s dependency injection, making it more maintainable and scalable. This approach dynamically retrieves operator instances from a mapped collection. If an invalid operator name is provided, it logs an error and throws an exception, ensuring robustness.

@Component
@RequiredArgsConstructor
public class OperatorFactory {

private final Map<String, Operator> operatorMap;

public Operator getOperator(String operatorName) {
if (operatorMap.containsKey(operatorName)) {
operatorMap.get(operatorName);
}

// If bean not found
log.error("No operator found for [{}]", operatorName);
throw new IllegalArgumentException("Invalid operation type: " + operatorName);
}
}

If you have a better approach to implementing the Factory Pattern, I’d love to hear about it! Write an article, share your insights, and drop the link in the comments. Your contribution could help me and many others improve our understanding. Let’s learn together!

Thank you for reading! I hope this article helps you refine your skills and write better code. If you found it useful, don’t forget to clap and share it to help others too!

See you in the next one — until then, Happy Coding! 🚀

Follow me on:

— Sadisha Nimsara (AKA nsadisha)

--

--

No responses yet