Applying the Strategy Pattern with Simple Factory in a Java Backend Service
This article explains how to use the Strategy pattern combined with a simple factory in a Spring‑based Java backend, showing interface definition, multiple arithmetic strategy implementations, a factory that registers them in a map, a service that selects a strategy at runtime, and a REST controller for testing.
Many developers are familiar with the Strategy pattern but may not know how to apply it in real projects; this article demonstrates its use when integrating a third‑party OA system that calls back different business interfaces based on a routing field.
First, a routing interface CalculationStrategy is defined, and concrete strategy classes (addition, division, multiplication, subtraction) implement this interface and are annotated with @Component so Spring can discover them.
public interface CalculationStrategy {
/**
* Strategy interface
*/
int operate(int num1, int num2);
}
@Component("add")
class AddCalculationStrategyImpl implements CalculationStrategy {
@Override
public int operate(int num1, int num2) {
return num1 + num2;
}
}
@Component("Division")
class DivisionStrategyImpl implements CalculationStrategy {
@Override
public int operate(int num1, int num2) {
return num1 / num2;
}
}
@Component("multiple")
class MultiplicationStrategyImpl implements CalculationStrategy {
@Override
public int operate(int num1, int num2) {
return num1 * num2;
}
}
@Component("subtract")
class SubtractionStrategyImpl implements CalculationStrategy {
@Override
public int operate(int num1, int num2) {
return num1 - num2;
}
}
@Component
class TestStrategyImpl implements CalculationStrategy {
@Override
public int operate(int num1, int num2) {
return num1 - num2;
}
}The second step is a factory (treated as a strategy context) that collects all @Component beans implementing CalculationStrategy and stores them in a Map<String, CalculationStrategy> for quick lookup.
@Component
public class CalculationFactory {
/**
* Put strategy key (bean name) and implementation into the map.
*/
public final Map
calculationStrategyMap = Maps.newHashMapWithExpectedSize(4);
/**
* Register all strategies at startup via constructor injection.
*/
public CalculationFactory(Map
strategyMap) {
this.calculationStrategyMap.clear();
this.calculationStrategyMap.putAll(strategyMap);
}
// Getter for service layer
public Map
getCalculationStrategyMap() {
return calculationStrategyMap;
}
}Third, a service uses the factory to retrieve the appropriate strategy based on the incoming strategy string and executes the operate method.
@Service
public class CalculationService {
@Autowired
private CalculationFactory calculationFactory;
public int operateByStrategy(String strategy, int num1, int num2) {
// Potential null handling for missing strategy
return calculationFactory.getCalculationStrategyMap().get(strategy).operate(num1, num2);
}
}Finally, a simple REST controller exposes an endpoint for testing the strategies, allowing callers to specify the operation name and two integers.
@RestController
@RequestMapping("/strategy")
public class TestStrategyController {
@Autowired
private CalculationService calculationService;
@GetMapping("/test/{operation}/{num1}/{num2}")
public int testCalculation(@PathVariable String operation, @PathVariable int num1, @PathVariable int num2) {
// Parameter validation omitted for brevity
return calculationService.operateByStrategy(operation, num1, num2);
}
}The main advantage of this design is extensibility: adding a new business branch only requires creating a new strategy implementation and annotating it with @Component ; no other code changes are needed, making the system easy to maintain and evolve.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.